Shader: execute RDNA2 FP64 shadow arithmetic and log denorm limits

Shader: disable FP64 denorm preservation controls for now
This commit is contained in:
nmzik
2026-09-26 14:46:41 +04:00
parent cf268af839
commit 16b83a034c
19 changed files with 307 additions and 9 deletions
+159
View File
@@ -15837,6 +15837,7 @@ private:
device_features.shaderImageGatherExtended = true;
device_features.sampleRateShading = true;
device_features.shaderInt64 = true;
device_features.shaderFloat64 = available_features.shaderFloat64;
device_features.fillModeNonSolid = true;
device_features.tessellationShader = true;
device_info.pEnabledFeatures = &device_features;
@@ -16400,6 +16401,11 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode,
case Opcode::V_MAX3_F32:
case Opcode::V_MED3_F32:
case Opcode::V_DOT2C_F32_F16:
case Opcode::V_CVT_F64_I32:
case Opcode::V_CVT_F32_F64:
case Opcode::V_RCP_F64:
case Opcode::V_MUL_F64:
case Opcode::V_FMA_F64:
case Opcode::V_CVT_F32_I32:
case Opcode::V_CVT_F32_U32:
case Opcode::V_CVT_U32_F32:
@@ -21689,6 +21695,157 @@ TestCase VectorSpecialF32FlushesDenormalInputs() {
O::V_SQRT_F32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
}
TestCase VectorF64CapturedScreenSpaceShadows() {
using O = ShaderOpcode;
TestCase test;
test.name = "VectorF64CapturedScreenSpaceShadows";
const auto store_pair = [&](u32 reg, double value) {
const auto bits = std::bit_cast<uint64_t>(value);
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(), {static_cast<u32>(bits),
static_cast<u32>(bits >> 32)});
};
for (const auto values :
{std::array<int32_t, 3>{63, 127, 101},
std::array<int32_t, 3>{16777217, -16777217, 123456789},
std::array<int32_t, 3>{INT32_MIN, INT32_MAX, -31}}) {
AppendVMovLiteral(&test.code, 7, static_cast<u32>(values[0]));
AppendVMovLiteral(&test.code, 1, static_cast<u32>(values[1]));
AppendVMovLiteral(&test.code, 9, static_cast<u32>(values[2]));
// Exact FP64 instruction words from b90e2024732c6111, PCs 0xc8 through
// 0x130.
test.code.insert(test.code.end(), {0x7e060907u, 0x7e0a0901u, 0x7e020909u});
store_pair(3, double(values[0]));
store_pair(5, double(values[1]));
store_pair(1, double(values[2]));
test.code.push_back(0x7e065f03u);
const double reciprocal = 1.0 / double(values[0]);
store_pair(3, reciprocal);
test.code.insert(test.code.end(), {0xd5650005u, 0x00020305u});
const double product = double(values[1]) * double(values[2]);
store_pair(5, product);
test.code.insert(test.code.end(), {0xd54c0001u, 0x84060705u});
const double result = std::fma(product, reciprocal, -double(values[2]));
store_pair(1, result);
test.code.push_back(0x7e041f01u);
AppendStoreVgpr(&test.code, 2, static_cast<u32>(test.expected.size()));
test.expected.push_back(std::bit_cast<u32>(static_cast<float>(result)));
}
AppendEnd(&test.code);
test.opcodes = {O::V_MOV_B32, O::V_CVT_F64_I32, O::V_RCP_F64,
O::V_MUL_F64, O::V_FMA_F64, O::V_CVT_F32_F64,
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
test.required_spirv = {"OpCapability Float64",
"OpTypeFloat 64",
"OpFDiv",
"Fma",
"SignedZeroInfNanPreserve 64",
"RoundingModeRTE 32"};
test.ir_counts = {{"ConvertF64S32", 9},
{"FPRecip64", 3},
{"FPMul64", 3},
{"FPFma64", 3},
{"ConvertF32F64", 3}};
ShaderComputeInputInfo info{};
std::vector<u32> normal_key, other_key;
BuildStageStaticKey(info, normal_key);
info.float_mode = 0xc4;
BuildStageStaticKey(info, other_key);
Require(test.name, "pipeline identity",
normal_key != other_key && normal_key.size() == other_key.size(),
"FP64 rounding modes must not share a compiled shader key");
return test;
}
TestCase VectorF64ModesModifiersAndExec() {
using O = ShaderOpcode;
TestCase test;
test.name = "VectorF64ModesModifiersAndExec";
const auto load_pair = [&](u32 reg, uint64_t bits) {
AppendVMovLiteral(&test.code, reg, static_cast<u32>(bits));
AppendVMovLiteral(&test.code, reg + 1, static_cast<u32>(bits >> 32));
};
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(), {static_cast<u32>(bits),
static_cast<u32>(bits >> 32)});
};
const auto store_word = [&](u32 reg, u32 bits) {
AppendStoreVgpr(&test.code, reg, static_cast<u32>(test.expected.size()));
test.expected.push_back(bits);
};
for (const auto [source, expected] :
{std::pair<uint64_t, uint64_t>{0, 0x7ff0000000000000ull},
{0x8000000000000000ull, 0xfff0000000000000ull},
{0x7ff0000000000000ull, 0},
{0xfff0000000000000ull, 0x8000000000000000ull},
{0x0008000000000000ull, 0x7fe0000000000000ull},
{0x7fe0000000000000ull, 0x0008000000000000ull}}) {
load_pair(1, source);
test.code.push_back(EncodeVop1(0x2f, 1, Vgpr(1)));
store_pair(1, expected);
}
// A fused result retains the residual that separate multiply/add would round
// to zero.
load_pair(1, std::bit_cast<uint64_t>(1.0 + 0x1p-27));
load_pair(3, std::bit_cast<uint64_t>(1.0 - 0x1p-27));
AppendVop3(&test.code, 0x14c, 1, Vgpr(1), Vgpr(3), 243u);
store_pair(1, std::bit_cast<uint64_t>(-0x1p-54));
// Unaligned pairs, SGPR pairs, inline floats, literal highword expansion, ABS
// then NEG.
test.code.push_back(EncodeSop1(0x03, 8, 255));
test.code.push_back(0u);
test.code.push_back(EncodeSop1(0x03, 9, 255));
test.code.push_back(0xbff00000u);
AppendVop3(&test.code, 0x165, 1, 8u, 244u, 0, 1, 0, false, 0, 1);
store_pair(1, std::bit_cast<uint64_t>(-2.0));
AppendVop3(&test.code, 0x165, 1, 255u, 242u);
test.code.push_back(0x40080000u);
store_pair(1, std::bit_cast<uint64_t>(3.0));
AppendVop3(&test.code, 0x165, 1, 248u, 242u);
store_pair(1, 0x3fc45f306dc9c882ull);
AppendVop3(&test.code, 0x184, 1, InlineU32(3), 0);
store_pair(1, std::bit_cast<uint64_t>(3.0));
AppendVop3(&test.code, 0x1af, 1, 244u, 0);
store_pair(1, std::bit_cast<uint64_t>(0.5));
// Preserve both destination words when EXEC is clear, restoring the original
// live lanes.
test.code.push_back(EncodeSop1(0x04, 12, 126));
test.code.push_back(EncodeSop1(0x04, 126, InlineU32(0)));
test.code.push_back(EncodeVop1(0x04, 1, InlineU32(9)));
test.code.push_back(EncodeSop1(0x04, 126, 12));
store_pair(1, std::bit_cast<uint64_t>(0.5));
for (const auto [source, expected] :
{std::pair<uint64_t, u32>{0x47f0000000000000ull, 0x7f7fffffu},
{0xc7f0000000000000ull, 0xff7fffffu},
{0x7ff0000000000000ull, 0x7f800000u},
{0xfff0000000000000ull, 0xff800000u},
{0x3800000000000000ull, 0},
{0xb800000000000000ull, 0x80000000u},
{0x3810000000000000ull, 0x00800000u},
{0x3ff0000010000000ull, 0x3f800000u}}) {
load_pair(1, source);
AppendVop3(&test.code, 0x18f, 1, Vgpr(1), 0);
store_word(1, expected);
}
load_pair(1, 0x7ff8123456789abcull);
test.code.push_back(EncodeVop1(0x2f, 1, Vgpr(1)));
AppendVMovLiteral(&test.code, 3, 0x7ff80000u);
test.code.push_back(EncodeVop2(0x1b, 2, Vgpr(3), 2));
store_word(2, 0x7ff80000u);
AppendEnd(&test.code);
test.opcodes = {O::V_MOV_B32, O::S_MOV_B32, O::S_MOV_B64,
O::V_CVT_F64_I32, O::V_RCP_F64, O::V_MUL_F64,
O::V_FMA_F64, O::V_CVT_F32_F64, O::V_AND_B32,
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
return test;
}
TestCase VectorRcpIflagF32IntegerReciprocal() {
using O = ShaderOpcode;
@@ -28874,6 +29031,8 @@ std::vector<TestCase> MakeCases() {
AddCase(CvtF32ToIntSaturatesNaNAndOutOfRange);
AddCase(VectorSpecialF32FlushesDenormalInputs);
AddCase(VectorRcpIflagF32IntegerReciprocal);
AddCase(VectorF64CapturedScreenSpaceShadows);
AddCase(VectorF64ModesModifiersAndExec);
AddCase(VectorSinCosMaxFiniteSpecialCases);
AddCase(VectorCompareOps);
AddCase(VectorVop3CompareEqI64OnGpu);