shader: clamp V_FRACT_F16/F32/F64 results below one

This commit is contained in:
TheCruZ
2026-10-25 00:36:03 +02:00
committed by nmzik
parent 40aaab482e
commit e864cb20bd
6 changed files with 73 additions and 8 deletions
@@ -1,6 +1,7 @@
#include "graphics/shader/recompiler/frontend/translate/Translator.h"
#include <array>
#include <bit>
#include <utility>
namespace Libs::Graphics::ShaderRecompiler::Frontend {
@@ -118,6 +119,33 @@ void Translator::FloatUnary(const Decoder::Instruction& inst, IR::ValueOpcode op
WriteOperand(DestinationOperand(inst), ir.Emit(opcode, {ReadOperand(inst.src0, type)}));
}
void Translator::V_FRACT(const Decoder::Instruction& inst, IR::Type type) {
// x - floor(x) rounds to 1.0 for a tiny negative x; v_fract returns the largest value below
// one instead. The ordered compare lets NaN through.
const auto destination = DestinationOperand(inst);
if (type == IR::Type::F64) {
const auto result = ir.Emit(IR::ValueOpcode::FPFract64, {ReadOperand(inst.src0, type)});
const auto below_one = ir.Emit(IR::ValueOpcode::BitCastF64U64,
{IR::Value(uint64_t {0x3fefffffffffffff})});
const auto above = ir.Emit(IR::ValueOpcode::FPOrdGreaterThan64, {result, below_one});
WriteOperand(destination, ir.Emit(IR::ValueOpcode::SelectF64, {above, below_one, result}));
return;
}
const bool half = type == IR::Type::F16;
const auto source = half ? ReadF16AsF32(inst.src0) : IR::F32(ReadOperand(inst.src0, type));
const auto result = IR::F32(ir.Emit(IR::ValueOpcode::FPFract32, {source}));
// Clamp in the destination precision: 0x3f7fe000 is the half 0x3bff.
const auto below_one =
IR::F32(IR::Value::F32(std::bit_cast<float>(half ? 0x3f7fe000u : 0x3f7fffffu)));
const auto above = IR::U1(ir.Emit(IR::ValueOpcode::FPOrdGreaterThan32, {result, below_one}));
const auto clamped = SelectF32(above, below_one, result);
if (half) {
WriteF16(destination, clamped);
} else {
WriteOperand(destination, clamped);
}
}
void Translator::FloatBinary(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
bool reverse) {
std::array<IR::Value, 2> args;
@@ -153,6 +153,7 @@ private:
void Float16Ternary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool accumulator,
bool mix);
void FloatUnary(const Decoder::Instruction& inst, IR::ValueOpcode opcode);
void V_FRACT(const Decoder::Instruction& inst, IR::Type type);
void FloatBinary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool reverse);
void FloatTernary(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool accumulator,
bool mix);
@@ -563,7 +563,7 @@ void Translator::EmitVector(const Decoder::Instruction& inst) {
case O::V_CEIL_F16: return Float16Unary(inst, IR::ValueOpcode::FPCeil32, false);
case O::V_TRUNC_F16: return Float16Unary(inst, IR::ValueOpcode::FPTrunc32, false);
case O::V_RNDNE_F16: return Float16Unary(inst, IR::ValueOpcode::FPRoundEven32, false);
case O::V_FRACT_F16: return Float16Unary(inst, IR::ValueOpcode::FPFract32, false);
case O::V_FRACT_F16: return V_FRACT(inst, IR::Type::F16);
case O::V_SIN_F16: return Float16Trig(inst, IR::ValueOpcode::FPSin);
case O::V_COS_F16: return Float16Trig(inst, IR::ValueOpcode::FPCos);
case O::V_MIN3_F16: return Float16Ternary(inst, IR::ValueOpcode::FPMinTri32, false, false);
@@ -574,8 +574,8 @@ void Translator::EmitVector(const Decoder::Instruction& inst) {
case O::V_RCP_F32: return FloatUnary(inst, IR::ValueOpcode::FPRecip32);
case O::V_RCP_F64: return FloatUnary(inst, IR::ValueOpcode::FPRecip64);
case O::V_RCP_IFLAG_F32: return FloatUnary(inst, IR::ValueOpcode::FPRecipIFlag32);
case O::V_FRACT_F32: return FloatUnary(inst, IR::ValueOpcode::FPFract32);
case O::V_FRACT_F64: return FloatUnary(inst, IR::ValueOpcode::FPFract64);
case O::V_FRACT_F32: return V_FRACT(inst, IR::Type::F32);
case O::V_FRACT_F64: return V_FRACT(inst, IR::Type::F64);
case O::V_TRUNC_F32: return FloatUnary(inst, IR::ValueOpcode::FPTrunc32);
case O::V_TRUNC_F64: return FloatUnary(inst, IR::ValueOpcode::FPTrunc64);
case O::V_CEIL_F32: return FloatUnary(inst, IR::ValueOpcode::FPCeil32);