shader: clamp before float-to-int conversion and drop the redundant truncate

This commit is contained in:
nmzik
2026-10-22 00:26:40 +02:00
parent 1e4ac1371d
commit 3bb09ed2a4
2 changed files with 16 additions and 30 deletions
@@ -94,35 +94,23 @@ uint32_t EmitF32ToF16RtzBits(EmitterState& state, uint32_t value) {
} }
uint32_t EmitF32ToU32(EmitterState& state, uint32_t src, bool signed_value) { uint32_t EmitF32ToU32(EmitterState& state, uint32_t src, bool signed_value) {
const auto trunc = EmitTruncF32Value(state, src); // Clamping first keeps the truncating conversion defined; NaN and values past the largest
const auto converted_raw = state.builder.AllocateId(); // float below the integer limit are selected afterwards.
if (signed_value) { const auto clamped =
const auto converted_i = state.builder.AllocateId(); EmitExt(state, TypeF32(state), GLSLstd450NClamp,
state.builder.AddFunction(spv::OpConvertFToS, TypeI32(state), converted_i, trunc); {src, ConstantF32(state, signed_value ? 0xcf000000u : 0u),
state.builder.AddFunction(spv::OpBitcast, TypeU32(state), converted_raw, converted_i); ConstantF32(state, signed_value ? 0x4effffffu : 0x4f7fffffu)});
} else { const auto converted = signed_value
state.builder.AddFunction(spv::OpConvertFToU, TypeU32(state), converted_raw, trunc); ? Unary(state, spv::OpBitcast, TypeU32(state),
} Unary(state, spv::OpConvertFToS, TypeI32(state), clamped))
const auto nan = Unary(state, spv::OpIsNan, TypeBool(state), src); : Unary(state, spv::OpConvertFToU, TypeU32(state), clamped);
if (signed_value) { const auto above = Binary(state, spv::OpFOrdGreaterThanEqual, TypeBool(state), src,
const auto below = Binary(state, spv::OpFOrdLessThanEqual, TypeBool(state), src, ConstantF32(state, signed_value ? 0x4f000000u : 0x4f800000u));
ConstantF32(state, 0xcf000000u));
const auto above = Binary(state, spv::OpFOrdGreaterThanEqual, TypeBool(state), src,
ConstantF32(state, 0x4f000000u));
const auto high =
Select(state, TypeU32(state), above, ConstantU32(state, 0x7fffffffu), converted_raw);
const auto low =
Select(state, TypeU32(state), below, ConstantU32(state, 0x80000000u), high);
return Select(state, TypeU32(state), nan, ConstantU32(state, 0), low);
}
const auto below =
Binary(state, spv::OpFOrdLessThanEqual, TypeBool(state), src, ConstantF32(state, 0));
const auto above = Binary(state, spv::OpFOrdGreaterThanEqual, TypeBool(state), src,
ConstantF32(state, 0x4f800000u));
const auto zero = Binary(state, spv::OpLogicalOr, TypeBool(state), nan, below);
const auto high = const auto high =
Select(state, TypeU32(state), above, ConstantU32(state, 0xffffffffu), converted_raw); Select(state, TypeU32(state), above,
return Select(state, TypeU32(state), zero, ConstantU32(state, 0), high); ConstantU32(state, signed_value ? 0x7fffffffu : 0xffffffffu), converted);
return Select(state, TypeU32(state), Unary(state, spv::OpIsNan, TypeBool(state), src),
ConstantU32(state, 0), high);
} }
} // namespace } // namespace
@@ -518,8 +518,6 @@ uint32_t EmitClassMaskF16(EmitterState& state, uint32_t bits, uint32_t mask);
uint32_t EmitMinMaxF32Value(EmitterState& state, uint32_t lhs, uint32_t rhs, bool max_value); uint32_t EmitMinMaxF32Value(EmitterState& state, uint32_t lhs, uint32_t rhs, bool max_value);
inline constexpr auto EmitTruncF32Value = EmitGlsl<GLSLstd450Trunc, IR::Type::F32, uint32_t>;
uint32_t EmitFlushF32DenormToSignedZero(EmitterState& state, uint32_t value); uint32_t EmitFlushF32DenormToSignedZero(EmitterState& state, uint32_t value);
uint32_t EmitTrigCycleF32(EmitterState& state, uint32_t src, bool preserve_signed_zero); uint32_t EmitTrigCycleF32(EmitterState& state, uint32_t src, bool preserve_signed_zero);