mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-05 13:20:11 +00:00
test(agc): skip only the device-dependent f64 denormal mode results
On a device whose target reports no f64 DenormPreserve, the f64 rounding and add/mul denormal mode tests skipped every mismatch in the modes that keep f64 denormal inputs (1, 3 and no known float mode) and checked modes 0 and 2 strictly. A recompiler regression in modes 1 and 3 never failed there, such as dropping the v_fract_f64 result flush that only mode 1 shows, while mode 2 failed where a driver may flush a denormal result of normal inputs (v_add_f64 and v_mul_f64 rows 4, 9, 11 and 15). A result is now skipped only when its expected value changes if the device flushes f64 denormal inputs, results or both, and only until the device shows that it keeps them: when the run without a known float mode, which uses the native instructions, matches every row, the other modes are checked strictly. Co-Authored-By: Claude <[email protected]>
This commit is contained in:
1 parent
7d876ad3ba
commit
1868043281
2 files changed
+50
-41
No files matched your search
@@ -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<std::uint32_t, 3> 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<std::uint64_t>(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';
|
||||
|
||||
@@ -94,28 +94,41 @@ std::uint64_t Bits(double value) {
|
||||
return std::bit_cast<std::uint64_t>(value);
|
||||
}
|
||||
|
||||
void Check(const Mode& mode) {
|
||||
std::array<std::uint64_t, 5> Expected(std::uint64_t source, std::uint32_t field) {
|
||||
const double value = std::bit_cast<double>(Flush(source, (field & 1u) == 0u));
|
||||
const double below = std::bit_cast<double>(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<std::uint32_t>(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<std::uint32_t, 3> 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<double>(Flush(source, (field & 1u) == 0u));
|
||||
const double below = std::bit_cast<double>(0x3fefffffffffffffull);
|
||||
int exponent = 0;
|
||||
const double mantissa = std::frexp(value, &exponent);
|
||||
const std::array<std::uint64_t, 4> 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<std::uint64_t>(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<std::uint64_t>(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<std::int32_t>(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<std::uint32_t>(exponent), std::string("v_frexp_exp_i32_f64 ") + mode.name + " of " + Hex(source) + " is " + std::to_string(static_cast<std::int32_t>(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';
|
||||
|
||||
Reference in new issue
Block a user