From 11465a5e3caf17f4f82ab1e9faec69dd3b608272 Mon Sep 17 00:00:00 2001 From: nmzik Date: Thu, 22 Oct 2026 00:44:43 +0200 Subject: [PATCH] shader: drop the large-input select before sin and cos since fract already returns zero --- .../backend/spirv/spirvEmitterAluHelpers.cpp | 33 +++++------------ tests/ShaderRecompilerComputeTests.cpp | 35 +++++++++++-------- 2 files changed, 29 insertions(+), 39 deletions(-) diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAluHelpers.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAluHelpers.cpp index c8a5e9b0f..d2120d8ed 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAluHelpers.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAluHelpers.cpp @@ -162,34 +162,19 @@ uint32_t EmitFlushF32DenormToSignedZero(EmitterState& state, uint32_t value) { } uint32_t EmitTrigCycleF32(EmitterState& state, uint32_t src, bool preserve_signed_zero) { - const auto fract = state.builder.AllocateId(); - const auto bits = state.builder.AllocateId(); - const auto abs_bits = state.builder.AllocateId(); - const auto large = state.builder.AllocateId(); - const auto finite = state.builder.AllocateId(); - const auto large_finite = state.builder.AllocateId(); - const auto reduced = state.builder.AllocateId(); + // Finite values of magnitude 2^23 and above are integers, so fract already gives them +0. + const auto fract = state.builder.AllocateId(); state.builder.AddFunction(spv::OpExtInst, TypeF32(state), fract, GlslStd450(state), GLSLstd450Fract, src); - state.builder.AddFunction(spv::OpBitcast, TypeU32(state), bits, src); - state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), abs_bits, bits, - ConstantU32(state, 0x7fffffffu)); - state.builder.AddFunction(spv::OpUGreaterThanEqual, TypeBool(state), large, abs_bits, - ConstantU32(state, 0x4b000000u)); - state.builder.AddFunction(spv::OpULessThan, TypeBool(state), finite, abs_bits, - ConstantU32(state, 0x7f800000u)); - state.builder.AddFunction(spv::OpLogicalAnd, TypeBool(state), large_finite, large, finite); - state.builder.AddFunction(spv::OpSelect, TypeF32(state), reduced, large_finite, - ConstantF32(state, 0), fract); if (!preserve_signed_zero) { - return reduced; + return fract; } - const auto zero = state.builder.AllocateId(); - const auto ret = state.builder.AllocateId(); - state.builder.AddFunction(spv::OpIEqual, TypeBool(state), zero, abs_bits, - ConstantU32(state, 0)); - state.builder.AddFunction(spv::OpSelect, TypeF32(state), ret, zero, src, reduced); - return ret; + const auto abs_bits = Binary(state, spv::OpBitwiseAnd, TypeU32(state), + Unary(state, spv::OpBitcast, TypeU32(state), src), + ConstantU32(state, 0x7fffffffu)); + return Select(state, TypeF32(state), + Binary(state, spv::OpIEqual, TypeBool(state), abs_bits, ConstantU32(state, 0)), + src, fract); } uint32_t EmitF16BitsToF32(EmitterState& state, uint32_t bits) { diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index fb7ffefd9..1febaa64c 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -29230,23 +29230,28 @@ TestCase VectorRcpIflagF32IntegerReciprocal() { return test; } +// Runtime inputs keep the driver from folding; finite values from 2^23 up are whole periods. TestCase VectorSinCosMaxFiniteSpecialCases() { using O = ShaderOpcode; - - std::vector code; - AppendVMovLiteral(&code, 0, 0xff7fffffu); - code.push_back(EncodeVop1(0x35, 1, Vgpr(0))); - code.push_back(EncodeVop1(0x36, 2, Vgpr(0))); - AppendStoreVgpr(&code, 1, 0); - AppendStoreVgpr(&code, 2, 1); - AppendEnd(&code); - - return {"VectorSinCosMaxFiniteSpecialCases", - code, - {}, - {0x00000000u, 0x3f800000u}, - {O::V_MOV_B32, O::V_SIN_F32, O::V_COS_F32, O::BUFFER_STORE_DWORD, - O::S_ENDPGM}}; + constexpr u32 inputs[] = {0xff7fffffu, 0x7f7fffffu, 0x4b000001u, 0xcb800000u}; + TestCase test; + test.name = "VectorSinCosMaxFiniteSpecialCases"; + test.initial.assign(std::begin(inputs), std::end(inputs)); + test.expected = test.initial; + for (u32 i = 0; i < std::size(inputs); i++) { + AppendVMovU32(&test.code, 30, i * 4u); + AppendBufferLoadDword(&test.code, 0, 30); + test.code.push_back(EncodeVop1(0x35, 1, Vgpr(0))); + test.code.push_back(EncodeVop1(0x36, 2, Vgpr(0))); + AppendStoreVgpr(&test.code, 1, u32(test.expected.size())); + AppendStoreVgpr(&test.code, 2, u32(test.expected.size() + 1)); + test.expected.insert(test.expected.end(), {0x00000000u, 0x3f800000u}); + } + AppendEnd(&test.code); + test.initial.resize(test.expected.size()); + test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_DWORD, O::V_SIN_F32, O::V_COS_F32, + O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + return test; } TestCase VectorCompareF32ExposureGuard() {