diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h index db33223..473d33c 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h @@ -33,6 +33,7 @@ enum : uint32_t { ExecutionModeEarlyFragmentTests = 9, ExecutionModeDepthReplacing = 12, ExecutionModeLocalSize = 17, + ExecutionModeSignedZeroInfNanPreserve = 4461, ExecutionModeDerivativeGroupQuadsKHR = 5289, AddressingModelLogical = 0, MemoryModelGLSL450 = 1, @@ -46,6 +47,7 @@ enum : uint32_t { CapabilityGroupNonUniform = 61, CapabilityGroupNonUniformBallot = 64, CapabilityGroupNonUniformShuffle = 65, + CapabilitySignedZeroInfNanPreserve = 4466, CapabilityComputeDerivativeGroupQuadsKHR = 5288, StorageClassUniformConstant = 0, StorageClassInput = 1, diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp index eb2170a..5314793 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp @@ -486,6 +486,7 @@ void EmitHeaderAndTypes(EmitterState& state) { state.builder.AddCapability({CapabilitySampled1D}); state.builder.AddCapability({CapabilityImage1D}); state.builder.AddCapability({CapabilityImageQuery}); + state.builder.AddCapability({CapabilitySignedZeroInfNanPreserve}); if (state.needs_image_gather_extended) { state.builder.AddCapability({CapabilityImageGatherExtended}); } @@ -509,10 +510,14 @@ void EmitHeaderAndTypes(EmitterState& state) { state.builder.AddCapability({CapabilityComputeDerivativeGroupQuadsKHR}); state.builder.AddExtension("SPV_KHR_compute_shader_derivatives"); } + state.builder.AddExtension("SPV_KHR_float_controls"); state.builder.AddExtInstImport(state.glsl_std450, "GLSL.std.450"); state.builder.AddMemoryModel({AddressingModelLogical, MemoryModelGLSL450}); state.builder.AddEntryPoint(ExecutionModelForStage(state.stage), state.main_func, "main", state.interface_variables); + // GCN/RDNA arithmetic preserves 32-bit signed zero, infinity, and NaN. Declaring that + // contract prevents host compilers from treating synthesized IEEE values as finite. + state.builder.AddExecutionMode({state.main_func, ExecutionModeSignedZeroInfNanPreserve, 32u}); if (state.stage == ShaderType::Compute) { uint32_t local_x = state.needs_compute_derivatives ? 2u : 1u; uint32_t local_y = state.needs_compute_derivatives ? 2u : 1u; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 86632f5..be82400 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -12679,6 +12679,31 @@ TestCase VectorVop3FmaF16UsesRdna2Opcode34b() { {O::VMovB32, O::VFmaF16, O::BufferStoreDword, O::SEndpgm}}; } +TestCase VectorFloatControlContractPreservesInfNan() { + using O = ShaderOpcode; + + std::vector code; + code.push_back(EncodeSop2(0x24, 0, InlineU32(8), InlineU32(23))); + code.push_back(EncodeVop1(0x01, 0, InlineU32(0))); + AppendVMovU32(&code, 1, 1); + AppendVMovU32(&code, 2, 3); + AppendVop3(&code, 0x108, 10, 0, Vgpr(0)); + code.push_back(EncodeVopc(0x88, Vgpr(10), 2)); + code.push_back(EncodeVop2(0x01, 3, InlineU32(0), 1)); + AppendStoreVgpr(&code, 3, 0); + AppendEnd(&code); + + TestCase test; + test.name = "VectorFloatControlContractPreservesInfNan"; + test.code = std::move(code); + test.expected = {1u}; + test.opcodes = {O::SBfmB32, O::VMovB32, O::VMulF32, O::VCmpClassF32, + O::VCndmaskB32, O::BufferStoreDword, O::SEndpgm}; + test.required_spirv = {"SPV_KHR_float_controls", + "SignedZeroInfNanPreserve 32"}; + return test; +} + TestCase VectorFloatArithmeticOps() { using O = ShaderOpcode; @@ -17005,6 +17030,7 @@ std::vector MakeCases() { AddCase(MadMixF16LiteralHalfSourceUsesOpsel); AddCase(MadMixF16NegHiIsAbsAndNegIsIndependent); AddCase(VectorVop3FmaF16UsesRdna2Opcode34b); + AddCase(VectorFloatControlContractPreservesInfNan); AddCase(VectorFloatArithmeticOps); AddCase(VectorMinMaxF32NanAndSignedZeroEdges); AddCase(VectorMed3F32NanUsesMin3Path); diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index bd726d4..3811164 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -978,6 +978,10 @@ void TestNewShaderRecompilerSMovB32() { Check(std::find(result.spirv.begin(), result.spirv.end(), 0x12345678u) != result.spirv.end(), "new SPIR-V emitter did not encode the literal as a binary word"); + Check(SpirvContainsCapability(result.spirv, 4466), + "SPIR-V binary does not request signed-zero/Inf/NaN preservation"); + Check(SpirvContainsExecutionMode(result.spirv, 4461), + "SPIR-V binary does not enable signed-zero/Inf/NaN preservation"); CheckSpirvBinaryValidates(result.spirv); }