shader: implement RDNA2 double-precision fractional part

This commit is contained in:
nmzik
2026-10-04 02:39:28 +04:00
parent 068d7621db
commit 284d58d1d5
8 changed files with 106 additions and 4 deletions
@@ -179,6 +179,7 @@ inline constexpr auto EmitFPFloor32 = EmitGlsl<GLSLstd450Floor, IR::Type::F3
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 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);
uint32_t EmitFPCos(EmitterState& state, uint32_t arg0);
#undef EMIT_NATIVE
@@ -186,6 +186,7 @@ enum class Opcode {
V_RCP_F32,
V_RCP_IFLAG_F32,
V_FRACT_F32,
V_FRACT_F64,
V_TRUNC_F32,
V_CEIL_F32,
V_RNDNE_F32,
@@ -123,6 +123,7 @@ constexpr OpcodeMap VOP1_OPCODE_LIST[] = {
{0x39u, Opcode::V_FFBH_U32},
{0x3au, Opcode::V_FFBL_B32},
{0x3bu, Opcode::V_FFBH_I32},
{0x3eu, Opcode::V_FRACT_F64},
{0x3fu, Opcode::V_FREXP_EXP_I32_F32},
{0x40u, Opcode::V_FREXP_MANT_F32},
{0x42u, Opcode::V_MOVRELD_B32},
@@ -182,6 +183,7 @@ constexpr OpcodeMap VOP3_ENCODED_VOP1_OPCODE_LIST[] = {
{0x39u, Opcode::V_FFBH_U32},
{0x3au, Opcode::V_FFBL_B32},
{0x3bu, Opcode::V_FFBH_I32},
{0x3eu, Opcode::V_FRACT_F64},
{0x3fu, Opcode::V_FREXP_EXP_I32_F32},
{0x40u, Opcode::V_FREXP_MANT_F32},
{0x42u, Opcode::V_MOVRELD_B32},
@@ -472,11 +474,18 @@ bool IsNativeVop3B16BinaryOpcode(Opcode opcode) {
bool IsVop1FloatResultOpcode(Opcode opcode);
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;
}
bool IsVop1FloatSourceOpcode(Opcode opcode) {
switch (opcode) {
case Opcode::V_CVT_F32_F64:
case Opcode::V_CVT_F64_F32:
case Opcode::V_RCP_F64:
case Opcode::V_FRACT_F64:
case Opcode::V_MOV_B32:
case Opcode::V_CVT_F32_F16:
case Opcode::V_CVT_U32_F32:
@@ -648,6 +657,10 @@ bool SupportsVop1Clamp(Opcode opcode) {
}
bool ValidateVop1Sdwa(Instruction& inst, uint32_t opcode, uint32_t modifier) {
if (IsVop1Float64Opcode(inst.opcode)) {
SetUnsupported(inst, Family::VOP1, opcode, "FP64 instructions do not support SDWA");
return false;
}
const auto dst_sel = (modifier >> 8u) & 0x7u;
const auto dst_u = (modifier >> 11u) & 0x3u;
const auto clamp = (modifier >> 13u) & 0x1u;
@@ -736,9 +749,7 @@ void ApplyDppModifier(Operand& operand, uint32_t modifier, uint32_t encoding) {
void DecodeVop1Dpp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
uint32_t opcode, uint32_t vdst, Instruction& inst) {
if (inst.opcode == Opcode::V_CVT_F64_I32 || inst.opcode == Opcode::V_CVT_F32_F64 ||
inst.opcode == Opcode::V_CVT_F64_F32 || inst.opcode == Opcode::V_CVT_F64_U32 ||
inst.opcode == Opcode::V_RCP_F64) {
if (IsVop1Float64Opcode(inst.opcode)) {
SetUnsupported(inst, Family::VOP1, opcode, "FP64 instructions do not support DPP");
return;
}
@@ -896,7 +896,8 @@ void IncludeInstructionVectorRegisters(const Decoder::Instruction& inst, uint32_
case Decoder::Opcode::V_FMA_F64: include_vector(inst.src2, 2u); [[fallthrough]];
case Decoder::Opcode::V_ADD_F64:
case Decoder::Opcode::V_MUL_F64: include_vector(inst.src1, 2u); [[fallthrough]];
case Decoder::Opcode::V_RCP_F64: include_vector(inst.dst, 2u); [[fallthrough]];
case Decoder::Opcode::V_RCP_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:
case Decoder::Opcode::V_CMP_LE_F64:
@@ -453,6 +453,7 @@ void Translator::EmitVector(const Decoder::Instruction& inst) {
case O::V_RCP_F64: return FloatUnary(inst, IR::ValueOpcode::FPRecip64);
case O::V_RCP_IFLAG_F32: return FloatUnary(inst, IR::ValueOpcode::FPRecipIFlag32);
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_CEIL_F32: return FloatUnary(inst, IR::ValueOpcode::FPCeil32);
case O::V_RNDNE_F32: return FloatUnary(inst, IR::ValueOpcode::FPRoundEven32);
@@ -203,6 +203,7 @@ VALUE_OPCODE(FPFloor32, F32, F32)
VALUE_OPCODE(FPCeil32, F32, F32)
VALUE_OPCODE(FPTrunc32, F32, F32)
VALUE_OPCODE(FPFract32, F32, F32)
VALUE_OPCODE(FPFract64, F64, F64)
VALUE_OPCODE(LaneId, U32)
VALUE_OPCODE(Ballot, U32x4, U1)
+64
View File
@@ -17774,6 +17774,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode,
case Opcode::V_CVT_F64_F32:
case Opcode::V_CVT_F64_U32:
case Opcode::V_RCP_F64:
case Opcode::V_FRACT_F64:
case Opcode::V_MUL_F64:
case Opcode::V_FMA_F64:
case Opcode::V_CVT_F32_I32:
@@ -23693,6 +23694,67 @@ TestCase VectorF64CapturedScreenSpaceShadows() {
return test;
}
TestCase VectorFractF64CapturedAndEdges() {
using O = ShaderOpcode;
constexpr std::array<std::array<uint64_t, 2>, 17> cases{{
{0x400a000000000000ull, 0x3fd0000000000000ull}, // 3.25 -> .25
{0xbff4000000000000ull, 0x3fe8000000000000ull}, // -1.25 -> .75
{0x3ff0000000000001ull, 0x3cb0000000000000ull}, // 1 + 2^-52
{0xbff0000000000001ull, 0x3feffffffffffffeull},
{0x4320000000000001ull, 0x3fe0000000000000ull}, // 2^51 + .5
{0, 0}, {0x8000000000000000ull, 0},
{0x401c000000000000ull, 0}, {0xc01c000000000000ull, 0},
{1, 1}, {0x8000000000000001ull, 0x3ff0000000000000ull},
{0x000fffffffffffffull, 0x000fffffffffffffull},
{0x7fefffffffffffffull, 0},
{0x7ff0000000000000ull, 0x7ff8000000000000ull},
{0xfff0000000000000ull, 0x7ff8000000000000ull},
{0x7ff8123456789abcull, 0x7ff8000000000000ull},
{0x7ff0000000000001ull, 0x7ff8000000000000ull}}};
TestCase test;
test.name = "VectorFractF64CapturedAndEdges";
const auto store_pair = [&](u32 reg, uint64_t expected) {
const auto index = static_cast<u32>(test.expected.size());
AppendStoreVgpr(&test.code, reg, index);
AppendStoreVgpr(&test.code, reg + 1, index + 1);
test.expected.insert(test.expected.end(), {u32(expected), u32(expected >> 32)});
};
for (u32 i = 0; i < cases.size(); i++) {
const auto [source, expected] = cases[i];
test.initial.insert(test.initial.end(), {u32(source), u32(source >> 32)});
for (u32 word = 0; word < 2; word++) {
AppendVMovU32(&test.code, 30, (i * 2 + word) * 4);
AppendBufferLoadDword(&test.code, 4 + word, 30);
}
test.code.push_back(0x7e087d04u); // Captured v_fract_f64 v[4:5], v[4:5].
if ((source & 0x7ff0000000000000ull) == 0x7ff0000000000000ull) {
// Classify quiet NaNs without requiring a particular payload or sign.
AppendVMovU32(&test.code, 4, 0);
AppendVMovLiteral(&test.code, 6, 0x7ff80000u);
test.code.push_back(EncodeVop2(0x1b, 5, Vgpr(6), 5));
}
store_pair(4, expected);
}
AppendVMovU32(&test.code, 1, 0);
AppendVMovLiteral(&test.code, 2, 0xbff40000u);
AppendVop3(&test.code, 0x1be, 1, Vgpr(1), 0, 0, 1, 0, false, 0, 1);
store_pair(1, 0x3fe8000000000000ull); // Odd pair; ABS precedes NEG.
AppendVMovU32(&test.code, 4, 1);
AppendVMovLiteral(&test.code, 5, 0x3ff00000u);
test.code.push_back(EncodeSop1(0x04, 12, 126));
test.code.push_back(EncodeSop1(0x04, 126, InlineU32(0)));
test.code.push_back(0x7e087d04u);
test.code.push_back(EncodeSop1(0x04, 126, 12));
store_pair(4, 0x3ff0000000000001ull); // Both words survive inactive EXEC.
AppendEnd(&test.code);
test.initial.resize(test.expected.size());
test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_DWORD, O::V_FRACT_F64,
O::V_AND_B32, O::S_MOV_B64, O::BUFFER_STORE_DWORD, O::S_ENDPGM};
test.decoded_counts = {{"V_FRACT_F64 v4, v4", cases.size() + 1}};
test.required_spirv = {"OpCapability Float64", "OpTypeFloat 64", "Fract"};
return test;
}
TestCase VectorF64WideningConversions() {
using O = ShaderOpcode;
TestCase test;
@@ -32493,6 +32555,7 @@ std::vector<TestCase> MakeCases() {
cases.push_back(VectorCompareF64WaveMasks(32));
cases.push_back(VectorCompareF64WaveMasks(64));
AddCase(VectorF64CapturedScreenSpaceShadows);
AddCase(VectorFractF64CapturedAndEdges);
AddCase(VectorF64ModesModifiersAndExec);
AddCase(VectorF64WideningConversions);
AddCase(VectorSinCosMaxFiniteSpecialCases);
@@ -37596,6 +37659,7 @@ int main(int argc, char **argv) {
RunCase(&vulkan, VectorCompareF64WaveMasks(32));
RunCase(&vulkan, VectorCompareF64WaveMasks(64));
RunCase(&vulkan, VectorF64CapturedScreenSpaceShadows());
RunCase(&vulkan, VectorFractF64CapturedAndEdges());
RunCase(&vulkan, VectorF64ModesModifiersAndExec());
return 0;
}
+22
View File
@@ -3579,6 +3579,27 @@ void TestNewShaderRecompilerCapturedVop1SdwaByteConvert() {
"V_CVT_F16_U16 accepted unimplemented SDWA byte sign extension");
}
void TestNewShaderRecompilerCapturedFractF64() {
using namespace ShaderRecompiler::Decoder;
const uint32_t captured[] = {0x7e087d04u};
Instruction decoded;
DecodeInstruction(captured, 0, decoded);
Check(decoded.family == Family::VOP1 && decoded.opcode == Opcode::V_FRACT_F64 &&
decoded.opcode_id == 0x3e && decoded.word_count == 1 &&
decoded.dst.kind == OperandKind::Vgpr && decoded.dst.reg == 4 &&
decoded.src0.kind == OperandKind::Vgpr && decoded.src0.reg == 4,
"captured V_FRACT_F64 did not decode its in-place register pair");
for (const uint32_t opcode : {0x3eu, 0x2fu}) {
for (const uint32_t source : {249u, 250u}) {
const uint32_t invalid[] = {EncodeVop1(opcode, 4, source),
source == 249u ? 0x00060604u : EncodeVop1Dpp(4, 0xe4)};
DecodeInstruction(invalid, 0, decoded);
Check(decoded.opcode == Opcode::UNSUPPORTED,
"FP64 VOP1 accepted an unsupported SDWA or DPP encoding");
}
}
}
void TestNewShaderRecompilerVop1SdwaBfrev() {
using namespace ShaderRecompiler::Decoder;
const uint32_t captured[] = {0x7e0070f9u, 0x00040600u};
@@ -14567,6 +14588,7 @@ int main() {
TestNewShaderRecompilerDsReadWrite2Translation();
TestNewShaderRecompilerDsWideAndAtomicTranslation();
TestNewShaderRecompilerCapturedVop1SdwaByteConvert();
TestNewShaderRecompilerCapturedFractF64();
TestNewShaderRecompilerVop1SdwaBfrev();
TestNewShaderRecompilerVop1SdwaNotDestination();
TestNewShaderRecompilerScalarMemoryBindingDomains();