diff --git a/src/graphics/presentation/window/vulkanWindow.cpp b/src/graphics/presentation/window/vulkanWindow.cpp index d05a85d5a..453013441 100644 --- a/src/graphics/presentation/window/vulkanWindow.cpp +++ b/src/graphics/presentation/window/vulkanWindow.cpp @@ -560,8 +560,7 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics, device_features.shaderInt64 = VK_TRUE; device_features.shaderFloat64 = supported_features2.features.shaderFloat64 && - float_controls.shaderSignedZeroInfNanPreserveFloat64 && - float_controls.shaderRoundingModeRTEFloat32; + float_controls.shaderSignedZeroInfNanPreserveFloat64; // if (device_features.shaderFloat64 && !float_controls.shaderDenormPreserveFloat64) { // Log::WriteToConsoleAndLog( // "WARNING: Vulkan device does not guarantee FP64 denormal preservation; " diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp index cc5b1edc4..4986f58fa 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp @@ -738,15 +738,13 @@ void DefineModule(EmitterState& state) { if (state.requirements.float64) { EXIT_NOT_IMPLEMENTED(state.program.stage == ShaderType::Compute && state.input_info.compute->float_mode != 0xc0); - // MODE=0xc0 uses round-to-nearest-even and preserves FP64 input/output denormals. + // Use native rounding for MODE=0xc0, consistent with ordinary FP32 arithmetic. state.builder.RequireCapability(spv::CapabilityFloat64); - state.builder.RequireCapability(spv::CapabilityRoundingModeRTE); state.builder.AddExecutionMode(state.main_func, spv::ExecutionModeSignedZeroInfNanPreserve, 64u); // FP64 denormal preservation is temporarily disabled. // state.builder.RequireCapability(spv::CapabilityDenormPreserve); // state.builder.AddExecutionMode(state.main_func, spv::ExecutionModeDenormPreserve, 64u); - state.builder.AddExecutionMode(state.main_func, spv::ExecutionModeRoundingModeRTE, 32u); } if (const auto* cs = ShaderWorkgroupInput(state.program.stage, state.input_info)) { uint32_t local_x = state.requirements.compute_derivatives ? 2u : 1u; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index c9dcdc0e0..bba993b16 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -23680,8 +23680,7 @@ TestCase VectorF64CapturedScreenSpaceShadows() { "OpTypeFloat 64", "OpFDiv", "Fma", - "SignedZeroInfNanPreserve 64", - "RoundingModeRTE 32"}; + "SignedZeroInfNanPreserve 64"}; test.ir_counts = {{"ConvertF64S32", 9}, {"FPRecip64", 3}, {"FPMul64", 3}, @@ -23760,6 +23759,73 @@ TestCase VectorFractF64CapturedAndEdges() { return test; } +TestCase VectorFractF64CapturedChainRuntimeExec() { + using O = ShaderOpcode; + TestCase test; + test.name = "VectorFractF64CapturedChainRuntimeExec"; + auto& code = test.code; + AppendSMovLiteral(&code, 60, 3); + AppendSMovLiteral(&code, 61, 3); + code.push_back(EncodeSop1(0x04, 126, 60)); // Lanes 0,1 and 32,33. + code.push_back(EncodeVop1(0x01, 20, Vgpr(0))); + code.push_back(EncodeVop2(0x1b, 30, InlineU32(1), 0)); + code.push_back(EncodeVop2(0x1a, 30, InlineU32(4), 30)); + AppendVMovU32(&code, 7, 192 * 4); + code.push_back(EncodeVop2(0x25, 30, Vgpr(7), 30)); + constexpr u32 input_regs[] = {6, 4, 10, 11}; + for (u32 word = 0; word < 4; ++word) { + code.push_back(EncodeMubuf0(0x0c, word * 4)); + code.push_back(EncodeMubuf1(input_regs[word], 0, 30)); + } + code.push_back(EncodeSmem0(0x08, 16, 0)); + code.push_back(EncodeSmem1(200 * 4, 125)); + constexpr u32 output_regs[] = {12, 22, 24}; + for (const auto reg : output_regs) AppendVMovLiteral(&code, reg, 0xdeadbeefu); + code.push_back(EncodeVopc(0xc2, InlineU32(1), 6)); + code.push_back(EncodeSop1(0x24, 8, 106)); + code.push_back(EncodeSMovB32(106, 16)); + AppendSMovLiteral(&code, 107, 0x3fea36a9u); + code.push_back(EncodeVop1(0x2a, 4, Vgpr(4))); // Runtime FP32 reciprocal with FP64 narrowing. + // Captured 4ee720fc4678fa19 PCs 0x1e8..0x20c, 0x23c..0x258, 0x260..0x270. + code.insert(code.end(), {0x7e04086au, 0xbeea03f4u, 0x7e082104u, 0xd54c0004u, + 0x0410d502u, 0xbeea0b86u, 0xbeeb03ffu, 0x3fe3f9e1u, + 0x7e087d04u, 0x7e181f04u}); + code.push_back(EncodeVop1(0x01, 4, Vgpr(10))); // Second runtime image value. + code.insert(code.end(), {0x7e082104u, 0xd54c0004u, 0x0410d502u, 0xbeea0b87u, + 0xbeeb03ffu, 0x3fc46bacu, 0x7e087d04u, 0x7e2c1f04u, + 0x7e08210bu, 0xd54c0002u, 0x0410d502u, 0x7e047d02u, + 0x7e301f02u}); + code.push_back(EncodeSop1(0x04, 126, 8)); + for (u32 component = 0; component < 3; ++component) + AppendStoreVgprAtLaneDwordOffset(&code, output_regs[component], 20, component * 64); + AppendEnd(&code); + + test.initial.resize(192); + test.initial.insert(test.initial.end(), {1, 0x3e800000u, 0xbfa00000u, 0x40600000u, + 0, 0x3f800000u, 0x40000000u, 0x40400000u, 3}); + test.expected = test.initial; + constexpr u32 results[] = {0x3eea3fbcu, 0x3f1f6d21u, 0x3f7a860du}; + for (u32 component = 0; component < 3; ++component) { + for (const u32 lane : {0u, 32u}) test.expected[component * 64 + lane] = results[component]; + for (const u32 lane : {1u, 33u}) test.expected[component * 64 + lane] = 0xdeadbeefu; + } + test.compute_info.wave_size = 64; + test.compute_info.threads_num[0] = 64; + test.compute_info.threads_num[1] = test.compute_info.threads_num[2] = 1; + test.compute_info.thread_ids_num = 1; + test.has_compute_info = true; + test.required_spirv = {"OpCapability Float64", "OpFDiv", "Fma", "Fract"}; + test.forbidden_spirv = {"RoundingModeRTE"}; + test.ir_counts = {{"FPRecip32", 1}, {"ConvertF64S32", 1}, {"ConvertF64F32", 3}, + {"FPFma64", 3}, {"FPFract64", 3}, {"ConvertF32F64", 3}}; + test.opcodes = {O::S_MOV_B32, O::S_MOV_B64, O::V_MOV_B32, O::V_AND_B32, + O::V_LSHLREV_B32, O::V_ADD_NC_U32, O::BUFFER_LOAD_DWORD, + O::S_BUFFER_LOAD_DWORD, O::V_CMP_EQ_U32, O::S_AND_SAVEEXEC_B64, + O::S_BREV_B32, O::V_CVT_F64_I32, O::V_CVT_F64_F32, O::V_FMA_F64, + O::V_FRACT_F64, O::V_RCP_F32, O::V_CVT_F32_F64, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + return test; +} + TestCase VectorF64WideningConversions() { using O = ShaderOpcode; TestCase test; @@ -23852,6 +23918,16 @@ TestCase VectorF64ModesModifiersAndExec() { for (const auto& pair : add_pairs) for (const auto bits : pair) test.initial.insert(test.initial.end(), {u32(bits), u32(bits >> 32)}); + // Runtime F64 values immediately around even/odd F32 halfway points, both signs. + constexpr std::array, 12> narrowing{{ + {0x3ff000000fffffffull, 0x3f800000u}, {0x3ff0000010000000ull, 0x3f800000u}, + {0x3ff0000010000001ull, 0x3f800001u}, {0x3ff000002fffffffull, 0x3f800001u}, + {0x3ff0000030000000ull, 0x3f800002u}, {0x3ff0000030000001ull, 0x3f800002u}, + {0xbff000000fffffffull, 0xbf800000u}, {0xbff0000010000000ull, 0xbf800000u}, + {0xbff0000010000001ull, 0xbf800001u}, {0xbff000002fffffffull, 0xbf800001u}, + {0xbff0000030000000ull, 0xbf800002u}, {0xbff0000030000001ull, 0xbf800002u}}}; + for (const auto& item : narrowing) + test.initial.insert(test.initial.end(), {u32(item.first), u32(item.first >> 32)}); test.expected = test.initial; const auto load_pair = [&](u32 reg, uint64_t bits) { AppendVMovLiteral(&test.code, reg, static_cast(bits)); @@ -23953,6 +24029,14 @@ TestCase VectorF64ModesModifiersAndExec() { AppendVop3(&test.code, 0x18f, 1, Vgpr(1), 0); store_word(1, expected); } + for (u32 i = 0; i < narrowing.size(); ++i) { + for (u32 word = 0; word < 2; ++word) { + AppendVMovU32(&test.code, 31, (add_pairs.size() * 4 + i * 2 + word) * 4); + AppendBufferLoadDword(&test.code, 1 + word, 31); + } + AppendVop3(&test.code, 0x18f, 1, Vgpr(1), 0); + store_word(1, narrowing[i].second); + } load_pair(1, 0x7ff8123456789abcull); test.code.push_back(EncodeVop1(0x2f, 1, Vgpr(1))); AppendVMovLiteral(&test.code, 3, 0x7ff80000u); @@ -32635,6 +32719,7 @@ std::vector MakeCases() { cases.push_back(VectorCompareF64WaveMasks(64)); AddCase(VectorF64CapturedScreenSpaceShadows); AddCase(VectorFractF64CapturedAndEdges); + AddCase(VectorFractF64CapturedChainRuntimeExec); AddCase(VectorF64ModesModifiersAndExec); AddCase(VectorF64WideningConversions); AddCase(VectorSinCosMaxFiniteSpecialCases); @@ -37741,6 +37826,7 @@ int main(int argc, char **argv) { RunCase(&vulkan, VectorCompareF64WaveMasks(64)); RunCase(&vulkan, VectorF64CapturedScreenSpaceShadows()); RunCase(&vulkan, VectorFractF64CapturedAndEdges()); + RunCase(&vulkan, VectorFractF64CapturedChainRuntimeExec()); RunCase(&vulkan, VectorF64ModesModifiersAndExec()); return 0; }