mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-27 05:16:56 +01:00
shader: evaluate saturating float-to-signed conversions in runtime descriptor values
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user