From 8a8c0290d62fb2c7929110ac8961772d5d03addb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Hasi=C5=84ski?= Date: Mon, 5 Oct 2026 12:09:13 +0200 Subject: [PATCH] fix(shader): apply f32 output modifiers on the exponent bits The output modifier was a separate multiply by 2, 4 or 0.5. On NVIDIA that multiply behaves as if folded into the instruction's own v_mul_f32 despite NoContraction, so a product that overflows comes back finite after div:2 (FLT_MAX * 2.0 div:2 gave FLT_MAX instead of +inf). Scale a normal result by adding to or subtracting from its exponent field instead: mul:2/mul:4 saturate to signed infinity when the exponent overflows, div:2 keeps the existing signed-zero flush below 2^-125, and infinities and NaNs pass through unchanged. Over all 2^32 inputs the results are bit-identical to the previous IEEE multiply and selects. --- .../src/TranslationContext/OperandAccess.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/core/shader/recompiler/Translation/src/TranslationContext/OperandAccess.cpp b/core/shader/recompiler/Translation/src/TranslationContext/OperandAccess.cpp index d0b6568c04..4ab2cdb99c 100644 --- a/core/shader/recompiler/Translation/src/TranslationContext/OperandAccess.cpp +++ b/core/shader/recompiler/Translation/src/TranslationContext/OperandAccess.cpp @@ -257,21 +257,18 @@ IrU32 TranslationContext::applyBitSourceModifiers(const RdnaOperand& operand, Ir IrF32 TranslationContext::applyF32ResultModifiers(const RdnaOperand& operand, IrF32 value) { if (operand.omod != 0u) { - float multiplier = 0.5f; - switch (operand.omod) { - case 1u: multiplier = 2.0f; break; - case 2u: multiplier = 4.0f; break; - default: break; - } - const IrF32 scaled(ir.Emit(IrOpcode::FPMul32, IrType::F32, {&value.Value(), &ir.ConstantF32(multiplier)})); IrValue& bits = ir.BitCastU32(value.Value()); IrValue& magnitude = ir.BitwiseAnd(bits, ir.Constant(0x7fffffffu)); - IrValue* result = &ir.BitCastU32(scaled.Value()); + IrValue& sign = ir.BitwiseAnd(bits, ir.Constant(0x80000000u)); + IrValue* result = nullptr; if (operand.omod == 3u) { - result = &ir.Select(ir.ULessThan(magnitude, ir.Constant(0x01000000u)), ir.BitwiseAnd(bits, ir.Constant(0x80000000u)), *result); + result = &ir.Select(ir.ULessThan(magnitude, ir.Constant(0x01000000u)), sign, ir.ISub(bits, ir.Constant(0x00800000u))); + } else { + const std::uint32_t exponentStep = operand.omod == 1u ? 0x00800000u : 0x01000000u; + result = &ir.Select(ir.ULessThan(magnitude, ir.Constant(0x7f800000u - exponentStep)), ir.IAdd(bits, ir.Constant(exponentStep)), ir.BitwiseOr(sign, ir.Constant(0x7f800000u))); } result = &ir.Select(ir.ULessThan(magnitude, ir.Constant(0x00800000u)), ir.Constant(0u), *result); - result = &ir.Select(ir.UGreaterThan(magnitude, ir.Constant(0x7f800000u)), bits, *result); + result = &ir.Select(ir.UGreaterThan(magnitude, ir.Constant(0x7f7fffffu)), bits, *result); value = IrF32(ir.BitCastF32(*result)); } if (operand.clamp) {