mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
shader: add FP64 min, max and rounding
Co-authored-by: Çağatay Güçlü <33038942+defektu@users.noreply.github.com>
This commit is contained in:
@@ -30,6 +30,28 @@ uint32_t CompareEqual64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_va
|
||||
return Unary(state, not_equal ? spv::OpAny : spv::OpAll, TypeBool(state), compare);
|
||||
}
|
||||
|
||||
uint32_t EmitMinMaxF64(EmitterState& state, uint32_t lhs, uint32_t rhs, bool max_value) {
|
||||
const auto bits_type = TypeU64(state);
|
||||
const auto lhs_bits = Unary(state, spv::OpBitcast, bits_type, lhs);
|
||||
const auto rhs_bits = Unary(state, spv::OpBitcast, bits_type, rhs);
|
||||
const auto ordered = Binary(state, max_value ? spv::OpFOrdGreaterThan : spv::OpFOrdLessThan,
|
||||
TypeBool(state), lhs, rhs);
|
||||
auto result = Select(state, TypeF64(state), ordered, lhs, rhs);
|
||||
|
||||
// Equal values have identical bits except signed zero: min chooses -0, max chooses +0.
|
||||
const auto equal = Binary(state, spv::OpFOrdEqual, TypeBool(state), lhs, rhs);
|
||||
const auto equal_bits = Binary(state, max_value ? spv::OpBitwiseAnd : spv::OpBitwiseOr,
|
||||
bits_type, lhs_bits, rhs_bits);
|
||||
result = Select(state, TypeF64(state), equal,
|
||||
Unary(state, spv::OpBitcast, TypeF64(state), equal_bits), result);
|
||||
|
||||
// Non-IEEE mode selects the other operand for NaN, including rhs when both are NaN.
|
||||
result = Select(state, TypeF64(state), Unary(state, spv::OpIsNan, TypeBool(state), rhs),
|
||||
lhs, result);
|
||||
return Select(state, TypeF64(state), Unary(state, spv::OpIsNan, TypeBool(state), lhs),
|
||||
rhs, result);
|
||||
}
|
||||
|
||||
uint32_t CompareOrdered64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value,
|
||||
spv::Op high_compare, spv::Op low_compare) {
|
||||
const auto lhs = ExtractPair(state, lhs_value);
|
||||
@@ -468,6 +490,14 @@ uint32_t EmitFPMax32(EmitterState& state, uint32_t arg0, uint32_t arg1) {
|
||||
return EmitMinMaxF32Value(state, arg0, arg1, true);
|
||||
}
|
||||
|
||||
uint32_t EmitFPMin64(EmitterState& state, uint32_t arg0, uint32_t arg1) {
|
||||
return EmitMinMaxF64(state, arg0, arg1, false);
|
||||
}
|
||||
|
||||
uint32_t EmitFPMax64(EmitterState& state, uint32_t arg0, uint32_t arg1) {
|
||||
return EmitMinMaxF64(state, arg0, arg1, true);
|
||||
}
|
||||
|
||||
uint32_t EmitFPMinTri32(EmitterState& state, uint32_t arg0, uint32_t arg1, uint32_t arg2) {
|
||||
return EmitFMinMax3(state, arg0, arg1, arg2, false);
|
||||
}
|
||||
|
||||
@@ -164,6 +164,8 @@ inline constexpr auto EmitFPFma32 =
|
||||
EmitGlsl<GLSLstd450Fma, IR::Type::F32, uint32_t, uint32_t, uint32_t>;
|
||||
uint32_t EmitFPMin32(EmitterState& state, uint32_t arg0, uint32_t arg1);
|
||||
uint32_t EmitFPMax32(EmitterState& state, uint32_t arg0, uint32_t arg1);
|
||||
uint32_t EmitFPMin64(EmitterState& state, uint32_t arg0, uint32_t arg1);
|
||||
uint32_t EmitFPMax64(EmitterState& state, uint32_t arg0, uint32_t arg1);
|
||||
uint32_t EmitFPMinTri32(EmitterState& state, uint32_t arg0, uint32_t arg1, uint32_t arg2);
|
||||
uint32_t EmitFPMaxTri32(EmitterState& state, uint32_t arg0, uint32_t arg1, uint32_t arg2);
|
||||
uint32_t EmitFPMedTri32(EmitterState& state, uint32_t arg0, uint32_t arg1, uint32_t arg2);
|
||||
@@ -178,6 +180,9 @@ inline constexpr auto EmitFPRoundEven32 = EmitGlsl<GLSLstd450RoundEven, IR::Type
|
||||
inline constexpr auto EmitFPFloor32 = EmitGlsl<GLSLstd450Floor, IR::Type::F32, uint32_t>;
|
||||
inline constexpr auto EmitFPCeil32 = EmitGlsl<GLSLstd450Ceil, IR::Type::F32, uint32_t>;
|
||||
inline constexpr auto EmitFPTrunc32 = EmitGlsl<GLSLstd450Trunc, IR::Type::F32, uint32_t>;
|
||||
inline constexpr auto EmitFPFloor64 = EmitGlsl<GLSLstd450Floor, IR::Type::F64, uint32_t>;
|
||||
inline constexpr auto EmitFPCeil64 = EmitGlsl<GLSLstd450Ceil, IR::Type::F64, uint32_t>;
|
||||
inline constexpr auto EmitFPTrunc64 = EmitGlsl<GLSLstd450Trunc, IR::Type::F64, uint32_t>;
|
||||
inline constexpr auto EmitFPFract32 = EmitGlsl<GLSLstd450Fract, IR::Type::F32, uint32_t>;
|
||||
inline constexpr auto EmitFPFract64 = EmitGlsl<GLSLstd450Fract, IR::Type::F64, uint32_t>;
|
||||
uint32_t EmitFPSin(EmitterState& state, uint32_t arg0);
|
||||
|
||||
@@ -188,9 +188,12 @@ enum class Opcode {
|
||||
V_FRACT_F32,
|
||||
V_FRACT_F64,
|
||||
V_TRUNC_F32,
|
||||
V_TRUNC_F64,
|
||||
V_CEIL_F32,
|
||||
V_CEIL_F64,
|
||||
V_RNDNE_F32,
|
||||
V_FLOOR_F32,
|
||||
V_FLOOR_F64,
|
||||
V_EXP_F32,
|
||||
V_LOG_F32,
|
||||
V_RSQ_F32,
|
||||
@@ -248,6 +251,8 @@ enum class Opcode {
|
||||
V_FMA_F64,
|
||||
V_ADD_F64,
|
||||
V_MUL_F64,
|
||||
V_MIN_F64,
|
||||
V_MAX_F64,
|
||||
V_FMA_F32,
|
||||
V_FMA_F16,
|
||||
V_PACK_B32_F16,
|
||||
|
||||
@@ -91,6 +91,9 @@ constexpr OpcodeMap VOP1_OPCODE_LIST[] = {
|
||||
{0x0fu, Opcode::V_CVT_F32_F64},
|
||||
{0x10u, Opcode::V_CVT_F64_F32},
|
||||
{0x16u, Opcode::V_CVT_F64_U32},
|
||||
{0x17u, Opcode::V_TRUNC_F64},
|
||||
{0x18u, Opcode::V_CEIL_F64},
|
||||
{0x1au, Opcode::V_FLOOR_F64},
|
||||
{0x2fu, Opcode::V_RCP_F64},
|
||||
{0x05u, Opcode::V_CVT_F32_I32},
|
||||
{0x06u, Opcode::V_CVT_F32_U32},
|
||||
@@ -156,6 +159,9 @@ constexpr OpcodeMap VOP3_ENCODED_VOP1_OPCODE_LIST[] = {
|
||||
{0x0fu, Opcode::V_CVT_F32_F64},
|
||||
{0x10u, Opcode::V_CVT_F64_F32},
|
||||
{0x16u, Opcode::V_CVT_F64_U32},
|
||||
{0x17u, Opcode::V_TRUNC_F64},
|
||||
{0x18u, Opcode::V_CEIL_F64},
|
||||
{0x1au, Opcode::V_FLOOR_F64},
|
||||
{0x2fu, Opcode::V_RCP_F64},
|
||||
{0x05u, Opcode::V_CVT_F32_I32},
|
||||
{0x06u, Opcode::V_CVT_F32_U32},
|
||||
@@ -292,6 +298,8 @@ constexpr OpcodeMap VOP3_OPCODE_LIST[] = {
|
||||
{0x14cu, Opcode::V_FMA_F64},
|
||||
{0x164u, Opcode::V_ADD_F64},
|
||||
{0x165u, Opcode::V_MUL_F64},
|
||||
{0x166u, Opcode::V_MIN_F64},
|
||||
{0x167u, Opcode::V_MAX_F64},
|
||||
{0x148u, Opcode::V_BFE_U32},
|
||||
{0x149u, Opcode::V_BFE_I32},
|
||||
{0x14au, Opcode::V_BFI_B32},
|
||||
@@ -478,7 +486,9 @@ bool IsVopcCompareExec(Opcode opcode);
|
||||
bool IsVop1Float64Opcode(Opcode opcode) {
|
||||
return opcode == Opcode::V_CVT_F64_I32 || opcode == Opcode::V_CVT_F32_F64 ||
|
||||
opcode == Opcode::V_CVT_F64_F32 || opcode == Opcode::V_CVT_F64_U32 ||
|
||||
opcode == Opcode::V_RCP_F64 || opcode == Opcode::V_FRACT_F64;
|
||||
opcode == Opcode::V_RCP_F64 || opcode == Opcode::V_FRACT_F64 ||
|
||||
opcode == Opcode::V_TRUNC_F64 || opcode == Opcode::V_CEIL_F64 ||
|
||||
opcode == Opcode::V_FLOOR_F64;
|
||||
}
|
||||
|
||||
bool IsVop1FloatSourceOpcode(Opcode opcode) {
|
||||
@@ -487,6 +497,9 @@ bool IsVop1FloatSourceOpcode(Opcode opcode) {
|
||||
case Opcode::V_CVT_F64_F32:
|
||||
case Opcode::V_RCP_F64:
|
||||
case Opcode::V_FRACT_F64:
|
||||
case Opcode::V_TRUNC_F64:
|
||||
case Opcode::V_CEIL_F64:
|
||||
case Opcode::V_FLOOR_F64:
|
||||
case Opcode::V_MOV_B32:
|
||||
case Opcode::V_CVT_F32_F16:
|
||||
case Opcode::V_CVT_U32_F32:
|
||||
@@ -1235,6 +1248,8 @@ uint32_t NativeVop3SourceCount(Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case Opcode::V_ADD_F64:
|
||||
case Opcode::V_MUL_F64:
|
||||
case Opcode::V_MIN_F64:
|
||||
case Opcode::V_MAX_F64:
|
||||
case Opcode::V_MUL_LO_U32:
|
||||
case Opcode::V_MUL_HI_U32:
|
||||
case Opcode::V_MUL_LO_I32:
|
||||
@@ -1387,6 +1402,8 @@ bool SupportsNativeVop3SourceModifiers(Opcode opcode) {
|
||||
case Opcode::V_MAD_F32:
|
||||
case Opcode::V_ADD_F64:
|
||||
case Opcode::V_MUL_F64:
|
||||
case Opcode::V_MIN_F64:
|
||||
case Opcode::V_MAX_F64:
|
||||
case Opcode::V_FMA_F64:
|
||||
case Opcode::V_FMA_F32:
|
||||
case Opcode::V_PACK_B32_F16:
|
||||
|
||||
@@ -895,8 +895,13 @@ void IncludeInstructionVectorRegisters(const Decoder::Instruction& inst, uint32_
|
||||
case Decoder::Opcode::V_CVT_F64_U32: include_vector(inst.dst, 2u); break;
|
||||
case Decoder::Opcode::V_FMA_F64: include_vector(inst.src2, 2u); [[fallthrough]];
|
||||
case Decoder::Opcode::V_ADD_F64:
|
||||
case Decoder::Opcode::V_MIN_F64:
|
||||
case Decoder::Opcode::V_MAX_F64:
|
||||
case Decoder::Opcode::V_MUL_F64: include_vector(inst.src1, 2u); [[fallthrough]];
|
||||
case Decoder::Opcode::V_RCP_F64:
|
||||
case Decoder::Opcode::V_TRUNC_F64:
|
||||
case Decoder::Opcode::V_CEIL_F64:
|
||||
case Decoder::Opcode::V_FLOOR_F64:
|
||||
case Decoder::Opcode::V_FRACT_F64: include_vector(inst.dst, 2u); [[fallthrough]];
|
||||
case Decoder::Opcode::V_CVT_F32_F64: include_vector(inst.src0, 2u); break;
|
||||
case Decoder::Opcode::V_CMP_EQ_F64:
|
||||
|
||||
@@ -456,9 +456,12 @@ void Translator::EmitVector(const Decoder::Instruction& inst) {
|
||||
case O::V_FRACT_F32: return FloatUnary(inst, IR::ValueOpcode::FPFract32);
|
||||
case O::V_FRACT_F64: return FloatUnary(inst, IR::ValueOpcode::FPFract64);
|
||||
case O::V_TRUNC_F32: return FloatUnary(inst, IR::ValueOpcode::FPTrunc32);
|
||||
case O::V_TRUNC_F64: return FloatUnary(inst, IR::ValueOpcode::FPTrunc64);
|
||||
case O::V_CEIL_F32: return FloatUnary(inst, IR::ValueOpcode::FPCeil32);
|
||||
case O::V_CEIL_F64: return FloatUnary(inst, IR::ValueOpcode::FPCeil64);
|
||||
case O::V_RNDNE_F32: return FloatUnary(inst, IR::ValueOpcode::FPRoundEven32);
|
||||
case O::V_FLOOR_F32: return FloatUnary(inst, IR::ValueOpcode::FPFloor32);
|
||||
case O::V_FLOOR_F64: return FloatUnary(inst, IR::ValueOpcode::FPFloor64);
|
||||
case O::V_EXP_F32: return FloatUnary(inst, IR::ValueOpcode::FPExp2);
|
||||
case O::V_LOG_F32: return FloatUnary(inst, IR::ValueOpcode::FPLog2);
|
||||
case O::V_RSQ_F32: return FloatUnary(inst, IR::ValueOpcode::FPRecipSqrt32);
|
||||
@@ -471,6 +474,8 @@ void Translator::EmitVector(const Decoder::Instruction& inst) {
|
||||
case O::V_MUL_F32: return FloatBinary(inst, IR::ValueOpcode::FPMul32, false);
|
||||
case O::V_ADD_F64: return FloatBinary(inst, IR::ValueOpcode::FPAdd64, false);
|
||||
case O::V_MUL_F64: return FloatBinary(inst, IR::ValueOpcode::FPMul64, false);
|
||||
case O::V_MIN_F64: return FloatBinary(inst, IR::ValueOpcode::FPMin64, false);
|
||||
case O::V_MAX_F64: return FloatBinary(inst, IR::ValueOpcode::FPMax64, false);
|
||||
case O::V_MIN_F32: return FloatBinary(inst, IR::ValueOpcode::FPMin32, false);
|
||||
case O::V_MAX_F32: return FloatBinary(inst, IR::ValueOpcode::FPMax32, false);
|
||||
case O::V_LDEXP_F32: return FloatBinary(inst, IR::ValueOpcode::FPLdexp, false);
|
||||
|
||||
@@ -186,6 +186,8 @@ VALUE_OPCODE(FPFma64, F64, F64, F64, F64)
|
||||
VALUE_OPCODE(FPRecip64, F64, F64)
|
||||
VALUE_OPCODE(FPMin32, F32, F32, F32)
|
||||
VALUE_OPCODE(FPMax32, F32, F32, F32)
|
||||
VALUE_OPCODE(FPMin64, F64, F64, F64)
|
||||
VALUE_OPCODE(FPMax64, F64, F64, F64)
|
||||
VALUE_OPCODE(FPMinTri32, F32, F32, F32, F32)
|
||||
VALUE_OPCODE(FPMaxTri32, F32, F32, F32, F32)
|
||||
VALUE_OPCODE(FPMedTri32, F32, F32, F32, F32)
|
||||
@@ -202,6 +204,9 @@ VALUE_OPCODE(FPRoundEven32, F32, F32)
|
||||
VALUE_OPCODE(FPFloor32, F32, F32)
|
||||
VALUE_OPCODE(FPCeil32, F32, F32)
|
||||
VALUE_OPCODE(FPTrunc32, F32, F32)
|
||||
VALUE_OPCODE(FPFloor64, F64, F64)
|
||||
VALUE_OPCODE(FPCeil64, F64, F64)
|
||||
VALUE_OPCODE(FPTrunc64, F64, F64)
|
||||
VALUE_OPCODE(FPFract32, F32, F32)
|
||||
VALUE_OPCODE(FPFract64, F64, F64)
|
||||
|
||||
|
||||
@@ -24486,6 +24486,147 @@ TestCase VectorFractF64CapturedAndEdges() {
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase VectorMinMaxF64CapturedAndEdges() {
|
||||
using O = ShaderOpcode;
|
||||
// MODE.IEEE=0: select the numeric operand, or the second NaN unchanged.
|
||||
constexpr uint64_t positive_zero = 0, negative_zero = 0x8000000000000000ull;
|
||||
constexpr uint64_t one = 0x3ff0000000000000ull, minus_two = 0xc000000000000000ull;
|
||||
constexpr uint64_t qnan = 0x7ff8123456789abcull, snan = 0xfff0000000000042ull;
|
||||
constexpr uint64_t infinity = 0x7ff0000000000000ull;
|
||||
constexpr std::array<std::array<uint64_t, 4>, 20> cases{{
|
||||
{one, minus_two, minus_two, one}, {minus_two, one, minus_two, one},
|
||||
{one, one, one, one},
|
||||
{positive_zero, negative_zero, negative_zero, positive_zero},
|
||||
{negative_zero, positive_zero, negative_zero, positive_zero},
|
||||
{negative_zero, negative_zero, negative_zero, negative_zero},
|
||||
{positive_zero, positive_zero, positive_zero, positive_zero},
|
||||
{qnan, one, one, one}, {one, qnan, one, one},
|
||||
{snan, minus_two, minus_two, minus_two}, {minus_two, snan, minus_two, minus_two},
|
||||
{qnan, snan, snan, snan}, {snan, qnan, qnan, qnan},
|
||||
{infinity, one, one, infinity},
|
||||
{infinity | negative_zero, one, infinity | negative_zero, one},
|
||||
{0x3fe0000000000000ull, one, 0x3fe0000000000000ull, one},
|
||||
{1, negative_zero | 1, negative_zero | 1, 1}, {1, 0, 0, 1},
|
||||
{1, 2, 1, 2}, {2, 1, 1, 2}}};
|
||||
TestCase test;
|
||||
test.name = "VectorMinMaxF64CapturedAndEdges";
|
||||
for (const auto &item : cases) {
|
||||
for (u32 source = 0; source < 2; source++) {
|
||||
test.initial.insert(test.initial.end(), {u32(item[source]), u32(item[source] >> 32)});
|
||||
}
|
||||
}
|
||||
test.expected = test.initial;
|
||||
const auto store_pair = [&](u32 reg, uint64_t bits) {
|
||||
AppendStoreVgpr(&test.code, reg, static_cast<u32>(test.expected.size()));
|
||||
AppendStoreVgpr(&test.code, reg + 1, static_cast<u32>(test.expected.size() + 1));
|
||||
test.expected.insert(test.expected.end(), {u32(bits), u32(bits >> 32)});
|
||||
};
|
||||
for (u32 i = 0; i < cases.size(); i++) {
|
||||
for (u32 word = 0; word < 4; word++) {
|
||||
AppendVMovU32(&test.code, 30, (i * 4 + word) * 4);
|
||||
AppendBufferLoadDword(&test.code, 2 + word, 30);
|
||||
}
|
||||
AppendVop3(&test.code, 0x166, 7, Vgpr(2), Vgpr(4));
|
||||
store_pair(7, cases[i][2]);
|
||||
AppendVop3(&test.code, 0x167, 9, Vgpr(2), Vgpr(4));
|
||||
store_pair(9, cases[i][3]);
|
||||
// CS daced64c72398e80 PCs 0x10c/0x11c clamp the pair to [0, 1].
|
||||
test.code.insert(test.code.end(), {0xd5670006u, 0x00020480u,
|
||||
0xd5660006u, 0x00020cf2u});
|
||||
const auto source = std::bit_cast<double>(cases[i][0]);
|
||||
uint64_t clamped = 0;
|
||||
if (source > 0) clamped = source < 1 ? cases[i][0] : one;
|
||||
store_pair(6, clamped);
|
||||
}
|
||||
AppendSMovLiteral(&test.code, 8, 0);
|
||||
AppendSMovLiteral(&test.code, 9, u32(minus_two >> 32));
|
||||
// Scalar source and odd destination pairs; ABS must precede NEG.
|
||||
AppendVop3(&test.code, 0x166, 1, 8, 243, 0, 1, 0, false, 0, 1);
|
||||
store_pair(1, minus_two);
|
||||
AppendVop3(&test.code, 0x167, 1, 8, 243, 0, 1, 0, false, 0, 1);
|
||||
store_pair(1, 0xbff0000000000000ull);
|
||||
test.code.push_back(EncodeSop1(0x04, 12, 126));
|
||||
test.code.push_back(EncodeSop1(0x04, 126, InlineU32(0)));
|
||||
AppendVop3(&test.code, 0x166, 1, 242, 242);
|
||||
AppendVop3(&test.code, 0x167, 1, 242, 242);
|
||||
test.code.push_back(EncodeSop1(0x04, 126, 12));
|
||||
store_pair(1, 0xbff0000000000000ull);
|
||||
AppendEnd(&test.code);
|
||||
test.initial.resize(test.expected.size());
|
||||
test.opcodes = {O::V_MOV_B32, O::S_MOV_B32, O::S_MOV_B64, O::BUFFER_LOAD_DWORD,
|
||||
O::V_MIN_F64, O::V_MAX_F64, O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
test.required_spirv = {"OpCapability Float64", "OpTypeFloat 64",
|
||||
"SignedZeroInfNanPreserve 64"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase VectorRoundF64EdgesAndModifiers() {
|
||||
using O = ShaderOpcode;
|
||||
constexpr std::array<uint64_t, 14> sources{
|
||||
0x400a000000000000ull, 0xbff4000000000000ull,
|
||||
0x3fd0000000000000ull, 0xbfd0000000000000ull,
|
||||
0, 0x8000000000000000ull, 0x4330000000000001ull, 0xc330000000000001ull,
|
||||
0x7ff0000000000000ull, 0xfff0000000000000ull,
|
||||
0xfff8123456789abcull, 0x7ff0000000000001ull, 1, 0x8000000000000001ull};
|
||||
TestCase test;
|
||||
test.name = "VectorRoundF64EdgesAndModifiers";
|
||||
for (const auto bits : sources) {
|
||||
test.initial.insert(test.initial.end(), {u32(bits), u32(bits >> 32)});
|
||||
}
|
||||
test.expected = test.initial;
|
||||
const auto rounded = [](u32 opcode, double value) {
|
||||
switch (opcode) {
|
||||
case 0x17: return std::trunc(value);
|
||||
case 0x18: return std::ceil(value);
|
||||
case 0x1a: return std::floor(value);
|
||||
default: std::abort();
|
||||
}
|
||||
};
|
||||
const auto store_pair = [&](u32 reg, uint64_t bits) {
|
||||
AppendStoreVgpr(&test.code, reg, static_cast<u32>(test.expected.size()));
|
||||
AppendStoreVgpr(&test.code, reg + 1, static_cast<u32>(test.expected.size() + 1));
|
||||
test.expected.insert(test.expected.end(), {u32(bits), u32(bits >> 32)});
|
||||
};
|
||||
for (const u32 opcode : {0x17u, 0x18u, 0x1au}) {
|
||||
for (u32 i = 0; i < sources.size(); i++) {
|
||||
for (u32 word = 0; word < 2; word++) {
|
||||
AppendVMovU32(&test.code, 30, (i * 2 + word) * 4);
|
||||
AppendBufferLoadDword(&test.code, 2 + word, 30);
|
||||
}
|
||||
test.code.push_back(EncodeVop1(opcode, 5, Vgpr(2)));
|
||||
AppendVop3(&test.code, 0x180 + opcode, 7, Vgpr(2), 0);
|
||||
const auto value = std::bit_cast<double>(sources[i]);
|
||||
for (const u32 reg : {5u, 7u}) {
|
||||
if (std::isnan(value)) {
|
||||
// IEEE=false does not promise a particular NaN payload or quiet bit.
|
||||
test.code.push_back(EncodeVopc(0x22, Vgpr(reg), reg));
|
||||
AppendStoreSgprPair(&test.code, 106, static_cast<u32>(test.expected.size()));
|
||||
test.expected.insert(test.expected.end(), {0, 0});
|
||||
} else {
|
||||
store_pair(reg, std::bit_cast<uint64_t>(rounded(opcode, value)));
|
||||
}
|
||||
}
|
||||
}
|
||||
AppendSMovLiteral(&test.code, 8, 0);
|
||||
AppendSMovLiteral(&test.code, 9, 0xbff40000u);
|
||||
AppendVop3(&test.code, 0x180 + opcode, 1, 8, 0, 0, 1, 0, false, 0, 1);
|
||||
const auto expected = std::bit_cast<uint64_t>(rounded(opcode, -1.25));
|
||||
store_pair(1, expected);
|
||||
test.code.push_back(EncodeSop1(0x04, 12, 126));
|
||||
test.code.push_back(EncodeSop1(0x04, 126, InlineU32(0)));
|
||||
test.code.push_back(EncodeVop1(opcode, 1, 242));
|
||||
test.code.push_back(EncodeSop1(0x04, 126, 12));
|
||||
store_pair(1, expected);
|
||||
}
|
||||
AppendEnd(&test.code);
|
||||
test.initial.resize(test.expected.size());
|
||||
test.opcodes = {O::V_MOV_B32, O::S_MOV_B32, O::S_MOV_B64, O::BUFFER_LOAD_DWORD,
|
||||
O::V_TRUNC_F64, O::V_CEIL_F64, O::V_FLOOR_F64, O::V_CMP_EQ_F64,
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
test.required_spirv = {"OpCapability Float64", "OpTypeFloat 64", "Trunc", "Ceil", "Floor"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase VectorFractF64CapturedChainRuntimeExec() {
|
||||
using O = ShaderOpcode;
|
||||
TestCase test;
|
||||
@@ -34023,6 +34164,8 @@ std::vector<TestCase> MakeCases() {
|
||||
cases.push_back(VectorCompare64WaveMasks(64, true));
|
||||
AddCase(VectorF64CapturedScreenSpaceShadows);
|
||||
AddCase(VectorFractF64CapturedAndEdges);
|
||||
AddCase(VectorMinMaxF64CapturedAndEdges);
|
||||
AddCase(VectorRoundF64EdgesAndModifiers);
|
||||
AddCase(VectorFractF64CapturedChainRuntimeExec);
|
||||
AddCase(VectorF64ModesModifiersAndExec);
|
||||
AddCase(VectorF64WideningConversions);
|
||||
@@ -39371,6 +39514,8 @@ int main(int argc, char **argv) {
|
||||
RunCase(&vulkan, VectorCompare64WaveMasks(64));
|
||||
RunCase(&vulkan, VectorF64CapturedScreenSpaceShadows());
|
||||
RunCase(&vulkan, VectorFractF64CapturedAndEdges());
|
||||
RunCase(&vulkan, VectorMinMaxF64CapturedAndEdges());
|
||||
RunCase(&vulkan, VectorRoundF64EdgesAndModifiers());
|
||||
RunCase(&vulkan, VectorFractF64CapturedChainRuntimeExec());
|
||||
RunCase(&vulkan, VectorF64ModesModifiersAndExec());
|
||||
return 0;
|
||||
|
||||
@@ -3600,6 +3600,53 @@ void TestNewShaderRecompilerCapturedFractF64() {
|
||||
}
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerF64AluEncodings() {
|
||||
using namespace ShaderRecompiler::Decoder;
|
||||
for (const bool maximum : {false, true}) {
|
||||
const std::array<uint32_t, 2> captured = maximum
|
||||
? std::array<uint32_t, 2>{0xd5670006u, 0x00020480u}
|
||||
: std::array<uint32_t, 2>{0xd5660006u, 0x00020cf2u};
|
||||
Instruction decoded;
|
||||
DecodeInstruction(captured, 0, decoded);
|
||||
Check(decoded.family == Family::VOP3 &&
|
||||
decoded.opcode == (maximum ? Opcode::V_MAX_F64 : Opcode::V_MIN_F64) &&
|
||||
decoded.opcode_id == (maximum ? 0x167u : 0x166u) && decoded.word_count == 2 &&
|
||||
decoded.dst.kind == OperandKind::Vgpr && decoded.dst.reg == 6 &&
|
||||
decoded.src0.kind == (maximum ? OperandKind::IntegerInlineConstant
|
||||
: OperandKind::FloatInlineConstant) &&
|
||||
decoded.src0.value == (maximum ? 0u : 0x3f800000u) &&
|
||||
decoded.src1.kind == OperandKind::Vgpr && decoded.src1.reg == (maximum ? 2u : 6u) &&
|
||||
!decoded.src0.absolute && !decoded.src0.negate &&
|
||||
!decoded.src1.absolute && !decoded.src1.negate &&
|
||||
!decoded.dst.clamp && decoded.dst.omod == 0,
|
||||
"captured V_MIN/MAX_F64 lost its inline source, register pair, or modifiers");
|
||||
}
|
||||
for (const auto [opcode, expected] : {
|
||||
std::pair{0x17u, Opcode::V_TRUNC_F64}, std::pair{0x18u, Opcode::V_CEIL_F64},
|
||||
std::pair{0x1au, Opcode::V_FLOOR_F64}}) {
|
||||
for (const bool vop3 : {false, true}) {
|
||||
const std::array<uint32_t, 2> words = vop3
|
||||
? std::array<uint32_t, 2>{EncodeVop3Word0(0x180 + opcode, 1, 0, 1),
|
||||
EncodeVop3Word1(8, 0, 0) | (1u << 29)}
|
||||
: std::array<uint32_t, 2>{EncodeVop1(opcode, 1, 258), 0};
|
||||
Instruction decoded;
|
||||
DecodeInstruction(words, 0, decoded);
|
||||
Check(decoded.opcode == expected && decoded.word_count == (vop3 ? 2u : 1u) &&
|
||||
decoded.dst.reg == 1 && decoded.src0.reg == (vop3 ? 8u : 2u) &&
|
||||
decoded.src0.absolute == vop3 && decoded.src0.negate == vop3,
|
||||
"FP64 rounding lost its VOP1/VOP3 encoding, odd pair, or source modifiers");
|
||||
}
|
||||
for (const uint32_t source : {249u, 250u}) {
|
||||
const uint32_t invalid[] = {EncodeVop1(opcode, 1, source),
|
||||
source == 249u ? 0x00060604u : EncodeVop1Dpp(4, 0xe4)};
|
||||
Instruction decoded;
|
||||
DecodeInstruction(invalid, 0, decoded);
|
||||
Check(decoded.opcode == Opcode::UNSUPPORTED,
|
||||
"FP64 rounding accepted unsupported SDWA or DPP encoding");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerVop1SdwaBfrev() {
|
||||
using namespace ShaderRecompiler::Decoder;
|
||||
const uint32_t captured[] = {0x7e0070f9u, 0x00040600u};
|
||||
@@ -14742,6 +14789,7 @@ int main() {
|
||||
TestNewShaderRecompilerDsWideAndAtomicTranslation();
|
||||
TestNewShaderRecompilerCapturedVop1SdwaByteConvert();
|
||||
TestNewShaderRecompilerCapturedFractF64();
|
||||
TestNewShaderRecompilerF64AluEncodings();
|
||||
TestNewShaderRecompilerVop1SdwaBfrev();
|
||||
TestNewShaderRecompilerVop1SdwaNotDestination();
|
||||
TestNewShaderRecompilerScalarMemoryBindingDomains();
|
||||
|
||||
Reference in New Issue
Block a user