shader: saturate V_CVT_PK_U16_U32 and V_CVT_PK_I16_I32

This commit is contained in:
TheCruZ
2026-10-25 00:04:35 +02:00
committed by nmzik
parent 66e21a6245
commit 9210264d47
5 changed files with 58 additions and 17 deletions
@@ -49,18 +49,11 @@ void Translator::V_CVT_F16_16(const Decoder::Instruction& inst, bool signed_valu
}
void Translator::V_CVT_16_F16(const Decoder::Instruction& inst, bool signed_value) {
const auto value = ReadF16AsF32(inst.src0);
// The 32-bit conversion already saturates and maps NaN to zero.
if (signed_value) {
const auto converted = ir.Emit(IR::ValueOpcode::ConvertS32F32, {value});
const auto low = ir.Emit(IR::ValueOpcode::SMax32, {converted, IR::Value(0xffff8000u)});
Write16Bits(DestinationOperand(inst),
IR::U32(ir.Emit(IR::ValueOpcode::SMin32, {low, IR::Value(0x7fffu)})));
return;
}
const auto converted = ir.Emit(IR::ValueOpcode::ConvertU32F32, {value});
Write16Bits(DestinationOperand(inst),
IR::U32(ir.Emit(IR::ValueOpcode::UMin32, {converted, IR::Value(0xffffu)})));
const auto converted = IR::U32(
ir.Emit(signed_value ? IR::ValueOpcode::ConvertS32F32 : IR::ValueOpcode::ConvertU32F32,
{ReadF16AsF32(inst.src0)}));
Write16Bits(DestinationOperand(inst), Saturate16(converted, signed_value));
}
void Translator::V_CVT_RPI_I32_F32(const Decoder::Instruction& inst) {
@@ -130,6 +123,12 @@ void Translator::V_CVT_PK_U8_F32(const Decoder::Instruction& inst) {
{ReadU32(SourceAt(inst, 2)), byte, shift, IR::Value(8u)}));
}
void Translator::V_CVT_PK_16_32(const Decoder::Instruction& inst, bool signed_value) {
WriteOperand(DestinationOperand(inst),
PackU16Lanes(Saturate16(ReadU32(inst.src0), signed_value),
Saturate16(ReadU32(inst.src1), signed_value)));
}
void Translator::V_PACK_B32_F16(const Decoder::Instruction& inst) {
WriteOperand(DestinationOperand(inst),
PackU16Lanes(Read16LaneBits(inst.src0, false), Read16LaneBits(inst.src1, false)));
@@ -140,4 +139,12 @@ IR::U32 Translator::PackU16Lanes(IR::U32 low, IR::U32 high) {
ir.Emit(IR::ValueOpcode::BitFieldInsert, {low, high, IR::Value(16u), IR::Value(16u)}));
}
IR::U32 Translator::Saturate16(IR::U32 value, bool signed_value) {
if (!signed_value) {
return IR::U32(ir.Emit(IR::ValueOpcode::UMin32, {value, IR::Value(0xffffu)}));
}
const auto low = ir.Emit(IR::ValueOpcode::SMax32, {value, IR::Value(0xffff8000u)});
return IR::U32(ir.Emit(IR::ValueOpcode::SMin32, {low, IR::Value(0x7fffu)}));
}
} // namespace Libs::Graphics::ShaderRecompiler::Frontend
@@ -25,8 +25,7 @@ void Translator::V_MAD_I16(const Decoder::Instruction& inst) {
const auto rhs = ReadU16AsU32(inst.src1, true);
auto result = ir.IAdd(ir.IMul(lhs, rhs), ReadU16AsU32(inst.src2, true));
if (inst.dst.clamp) {
result = IR::U32(ir.Emit(IR::ValueOpcode::SMax32, {result, IR::Value(0xffff8000u)}));
result = IR::U32(ir.Emit(IR::ValueOpcode::SMin32, {result, IR::Value(0x7fffu)}));
result = Saturate16(result, true);
}
Write16Bits(DestinationOperand(inst), result);
}
@@ -114,6 +114,7 @@ private:
IR::F32 SelectF32(IR::U1 condition, IR::F32 true_value, IR::F32 false_value);
IR::U32 PackU16Lanes(IR::U32 low, IR::U32 high);
IR::U32 Saturate16(IR::U32 value, bool signed_value);
void EmitCompareResult(const Decoder::Instruction& inst, IR::U1 value, bool scalar, bool cmpx);
void EmitCompareConstant(const Decoder::Instruction& inst, bool value, bool scalar, bool cmpx);
void EmitIntegerCompare(const Decoder::Instruction& inst, IR::ValueOpcode opcode, IR::Type type,
@@ -141,6 +142,7 @@ private:
void V_CVT_PKRTZ_F16_F32(const Decoder::Instruction& inst);
void V_CVT_PKNORM_F32(const Decoder::Instruction& inst, IR::ValueOpcode opcode);
void V_CVT_PK_U8_F32(const Decoder::Instruction& inst);
void V_CVT_PK_16_32(const Decoder::Instruction& inst, bool signed_value);
void V_PACK_B32_F16(const Decoder::Instruction& inst);
void PackedFloat16(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool accumulator,
bool quiet_snan);
@@ -495,8 +495,8 @@ void Translator::EmitVector(const Decoder::Instruction& inst) {
return;
case O::V_CVT_PK_U8_F32: V_CVT_PK_U8_F32(inst); return;
case O::V_PACK_B32_F16: V_PACK_B32_F16(inst); return;
case O::V_CVT_PK_U16_U32:
case O::V_CVT_PK_I16_I32: return PackB16(inst, false, false);
case O::V_CVT_PK_U16_U32: return V_CVT_PK_16_32(inst, false);
case O::V_CVT_PK_I16_I32: return V_CVT_PK_16_32(inst, true);
case O::V_LSHLREV_B16:
return Integer16Shift(inst, IR::ValueOpcode::ShiftLeftLogical32, false);