diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/F64AddMulDenormalModes.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/F64AddMulDenormalModes.cpp index 71de71a46b..fa57f2f3ec 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/F64AddMulDenormalModes.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/F64AddMulDenormalModes.cpp @@ -167,15 +167,23 @@ void Run(AgcDriver::VulkanDevice& device, const Mode& mode) { device.WaitIdle(); } -void Check(const Mode& mode) { +std::uint32_t Check(const Mode& mode, bool keepsDenormals) { constexpr const char* Names[2] = {"v_add_f64", "v_mul_f64"}; + const std::array deviceFlushes{mode.field & 2u, mode.field & 1u, 0u}; + std::uint32_t skipped = 0; for (std::uint32_t tid = 0; tid < Threads; ++tid) { for (std::uint32_t form = 0; form < 2u; ++form) { const std::uint64_t actual = Output[tid * Results + form * 2u] | (static_cast(Output[tid * Results + form * 2u + 1u]) << 32u); const std::uint64_t expected = Expected[mode.field][tid][form]; - Require(actual == expected, std::string(Names[form]) + " " + mode.name + " of " + Hex(Pairs[tid][0]) + " and " + Hex(Pairs[tid][1]) + " is " + Hex(actual) + ", expected " + Hex(expected)); + if (actual == expected) continue; + const std::string message = std::string(Names[form]) + " " + mode.name + " of " + Hex(Pairs[tid][0]) + " and " + Hex(Pairs[tid][1]) + " is " + Hex(actual) + ", expected " + Hex(expected); + const bool deviceDependent = std::any_of(deviceFlushes.begin(), deviceFlushes.end(), [&](std::uint32_t flushed) { return Expected[flushed][tid][form] != expected; }); + Require(deviceDependent && !keepsDenormals, message); + std::printf("skipped %s, the device flushes f64 denormals without DenormPreserve support\n", message.c_str()); + ++skipped; } } + return skipped; } } @@ -195,21 +203,15 @@ int main() { {"f64 denormal mode 3, IEEE mode on", ShaderRecompiler::ShaderFloatMode{0xf0u, true, true, false}, 3u}, {"f64 denormal mode 3, IEEE mode off", ShaderRecompiler::ShaderFloatMode{0xf0u, true, false, false}, 3u}, }; - const bool canPreserveDenormals = device->Target().float64DenormPreserve; + bool keepsDenormals = device->Target().float64DenormPreserve; + std::uint32_t skipped = 0; for (const Mode& mode : modes) { Run(*device, mode); - const bool keepsDenormals = !mode.value.has_value() || ((mode.value->floatMode >> 6u) & 1u) != 0u; - if (!keepsDenormals || canPreserveDenormals) { - Check(mode); - continue; - } - try { - Check(mode); - } catch (const std::exception& error) { - std::printf("skipped %s, the device flushes f64 denormals without DenormPreserve support: %s\n", mode.name, error.what()); - } + const std::uint32_t modeSkipped = Check(mode, keepsDenormals); + keepsDenormals = keepsDenormals || (!mode.value.has_value() && modeSkipped == 0u); + skipped += modeSkipped; } - std::puts("f64 add and mul denormal mode tests passed"); + std::printf("f64 add and mul denormal mode tests passed, %u device-dependent results skipped\n", skipped); return 0; } catch (const std::exception& error) { std::cerr << error.what() << '\n'; diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/F64RoundingDenormalModes.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/F64RoundingDenormalModes.cpp index 60ee60dd0b..363303b53d 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/F64RoundingDenormalModes.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/F64RoundingDenormalModes.cpp @@ -94,28 +94,41 @@ std::uint64_t Bits(double value) { return std::bit_cast(value); } -void Check(const Mode& mode) { +std::array Expected(std::uint64_t source, std::uint32_t field) { + const double value = std::bit_cast(Flush(source, (field & 1u) == 0u)); + const double below = std::bit_cast(0x3fefffffffffffffull); + int exponent = 0; + const double mantissa = std::frexp(value, &exponent); + return { + Bits(std::floor(value)), + Bits(std::ceil(value)), + Flush(Bits(std::min(value - std::floor(value), below)), (field & 2u) == 0u), + Bits(mantissa), + static_cast(exponent), + }; +} + +std::uint32_t Check(const Mode& mode, bool keepsDenormals) { const std::uint32_t field = mode.value.has_value() ? mode.value->floatMode >> 6u : 3u; + const std::array deviceFlushes{field & 2u, field & 1u, 0u}; + constexpr const char* Names[5] = {"v_floor_f64", "v_ceil_f64", "v_fract_f64", "v_frexp_mant_f64", "v_frexp_exp_i32_f64"}; + std::uint32_t skipped = 0; for (std::uint32_t tid = 0; tid < Threads; ++tid) { const std::uint64_t source = ValueAt(tid); - const double value = std::bit_cast(Flush(source, (field & 1u) == 0u)); - const double below = std::bit_cast(0x3fefffffffffffffull); - int exponent = 0; - const double mantissa = std::frexp(value, &exponent); - const std::array expected{ - Bits(std::floor(value)), - Bits(std::ceil(value)), - Flush(Bits(std::min(value - std::floor(value), below)), (field & 2u) == 0u), - Bits(mantissa), - }; - constexpr const char* Names[4] = {"v_floor_f64", "v_ceil_f64", "v_fract_f64", "v_frexp_mant_f64"}; + const auto expected = Expected(source, field); const std::uint32_t* out = &Output[tid * Results]; for (std::uint32_t index = 0; index < expected.size(); ++index) { - const std::uint64_t actual = out[index * 2u] | (static_cast(out[index * 2u + 1u]) << 32u); - Require(actual == expected[index], std::string(Names[index]) + " " + mode.name + " of " + Hex(source) + " is " + Hex(actual) + ", expected " + Hex(expected[index])); + const std::uint64_t actual = index < 4u ? out[index * 2u] | (static_cast(out[index * 2u + 1u]) << 32u) : out[8]; + if (actual == expected[index]) continue; + const auto text = [index](std::uint64_t bits) { return index < 4u ? Hex(bits) : std::to_string(static_cast(bits)); }; + const std::string message = std::string(Names[index]) + " " + mode.name + " of " + Hex(source) + " is " + text(actual) + ", expected " + text(expected[index]); + const bool deviceDependent = std::any_of(deviceFlushes.begin(), deviceFlushes.end(), [&](std::uint32_t flushed) { return Expected(source, flushed)[index] != expected[index]; }); + Require(deviceDependent && !keepsDenormals, message); + std::printf("skipped %s, the device flushes f64 denormals without DenormPreserve support\n", message.c_str()); + ++skipped; } - Require(out[8] == static_cast(exponent), std::string("v_frexp_exp_i32_f64 ") + mode.name + " of " + Hex(source) + " is " + std::to_string(static_cast(out[8])) + ", expected " + std::to_string(exponent)); } + return skipped; } } @@ -135,21 +148,15 @@ int main() { {"f64 denormal mode 3, IEEE mode on", ShaderRecompiler::ShaderFloatMode{0xf0u, true, true, false}}, {"f64 denormal mode 3, IEEE mode off", ShaderRecompiler::ShaderFloatMode{0xf0u, true, false, false}}, }; - const bool canPreserveDenormals = device->Target().float64DenormPreserve; + bool keepsDenormals = device->Target().float64DenormPreserve; + std::uint32_t skipped = 0; for (const Mode& mode : modes) { Run(*device, mode); - const bool keepsDenormals = !mode.value.has_value() || ((mode.value->floatMode >> 6u) & 1u) != 0u; - if (!keepsDenormals || canPreserveDenormals) { - Check(mode); - continue; - } - try { - Check(mode); - } catch (const std::exception& error) { - std::printf("skipped %s, the device flushes f64 denormals without DenormPreserve support: %s\n", mode.name, error.what()); - } + const std::uint32_t modeSkipped = Check(mode, keepsDenormals); + keepsDenormals = keepsDenormals || (!mode.value.has_value() && modeSkipped == 0u); + skipped += modeSkipped; } - std::puts("f64 rounding denormal mode tests passed"); + std::printf("f64 rounding denormal mode tests passed, %u device-dependent results skipped\n", skipped); return 0; } catch (const std::exception& error) { std::cerr << error.what() << '\n';