shader: evaluate saturating float-to-signed conversions in runtime descriptor values

This commit is contained in:
Claxten authored and nmzik committed 2026-10-27 04:23:01 +01:00
1 parent 99f4eba223
commit 0a72641678
2 files changed
+47

No files matched your search

@@ -82,6 +82,7 @@ bool IsRuntimeUniformOp(ValueOpcode op) {
case ValueOpcode::BitCastU32F32:
case ValueOpcode::BitCastF32U32:
case ValueOpcode::ConvertU32F32:
case ValueOpcode::ConvertS32F32:
case ValueOpcode::ConvertF32U32:
case ValueOpcode::CompositeConstructU64:
case ValueOpcode::CompositeExtractU64:
@@ -1038,6 +1039,23 @@ void WalkerCompiler::EmitStrict(const Node& node) {
c.L(done);
break;
}
case ValueOpcode::ConvertS32F32: {
// The conversion saturates and maps NaN to zero. cvttss2si's out-of-range result,
// INT32_MIN, is already right for negative overflow.
Xbyak::Label done;
c.movd(c.xmm0, c.eax);
c.xor_(c.eax, c.eax);
c.ucomiss(c.xmm0, c.xmm0);
c.jp(done, Near);
c.mov(c.ecx, 0x4f000000u);
c.movd(c.xmm1, c.ecx);
c.mov(c.eax, INT32_MAX);
c.ucomiss(c.xmm0, c.xmm1);
c.jae(done, Near);
c.cvttss2si(c.eax, c.xmm0);
c.L(done);
break;
}
case ValueOpcode::FPMul32:
c.movd(c.xmm0, c.eax);
c.movd(c.xmm1, c.ecx);
+29
View File
@@ -515,6 +515,34 @@ void TestFloatComparisonDescriptorInputs() {
}
}
void TestSignedConversionDescriptorInputs() {
// 3.75 and -3.75 truncate, the largest float below 2^31 converts exactly, 2^31 and +inf
// saturate up, -2^31 and -1e10 saturate down, and -0.0, a denormal and both NaNs read zero.
constexpr std::array<std::pair<uint32_t, uint32_t>, 11> inputs = {{
{0x40700000u, 3u}, {0xc0700000u, 0xfffffffdu}, {0x4effffffu, 0x7fffff80u},
{0x4f000000u, 0x7fffffffu}, {0x7f800000u, 0x7fffffffu}, {0xcf000000u, 0x80000000u},
{0xd01502f9u, 0x80000000u}, {0x80000000u, 0u}, {0x00000001u, 0u},
{0x7fc00000u, 0u}, {0xffc00000u, 0u}}};
Fixture fixture;
const auto user = fixture.Emit(
ValueOpcode::GetUserData, {Value(static_cast<ScalarReg>(2))});
const auto converted = fixture.Emit(
ValueOpcode::ConvertS32F32, {fixture.Emit(ValueOpcode::BitCastF32U32, {user})});
Check(ValidateRuntimeValue(fixture.program, converted),
"signed float conversion was rejected as a runtime value");
fixture.program.descriptor_sources.push_back(
{.dwords = {converted}, .dword_count = 1});
auto plan = ExtractResourcePlan(fixture.program);
for (const auto [bits, expected] : inputs) {
const std::array user_data{bits};
DescriptorValue result;
Check(EvaluateSource(plan, {.user_data = user_data}, 0, result) &&
result.dwords[0] == expected,
"descriptor signed conversion disagrees with the native conversion");
}
}
void TestSharedIntegerRuntimeDependencies() {
Fixture fixture;
const auto lane = fixture.Emit(ValueOpcode::LaneId);
@@ -888,6 +916,7 @@ int main() {
TestRuntime64BitDescriptorOps();
TestUniformFirstLaneSamplerLod();
TestFloatComparisonDescriptorInputs();
TestSignedConversionDescriptorInputs();
TestSharedIntegerRuntimeDependencies();
TestConstantBufferBounds();
TestReadLaneElimination();