mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 19:07:01 +02:00
shader: classify native half values with shared operand decoding
This commit is contained in:
@@ -125,20 +125,23 @@ uint32_t EmitClassMaskBitMatch(EmitterState& state, uint32_t mask, uint32_t bit,
|
||||
return EmitLogicalAndBool(state, selected, class_match);
|
||||
}
|
||||
|
||||
uint32_t EmitClassMaskF32(EmitterState& state, uint32_t value, uint32_t mask) {
|
||||
const auto bits = EmitBitcastF32ToU32(state, value);
|
||||
const auto sign_bits = EmitAndConstant(state, bits, 0x80000000u);
|
||||
const auto abs_bits = EmitAndConstant(state, bits, 0x7fffffffu);
|
||||
const auto exponent_bits = EmitAndConstant(state, abs_bits, 0x7f800000u);
|
||||
const auto mantissa_bits = EmitAndConstant(state, abs_bits, 0x007fffffu);
|
||||
const auto quiet_bits = EmitAndConstant(state, mantissa_bits, 0x00400000u);
|
||||
static uint32_t EmitClassMaskBits(EmitterState& state, uint32_t bits, uint32_t mask, bool half) {
|
||||
const uint32_t sign_mask = half ? 0x8000u : 0x80000000u;
|
||||
const uint32_t exponent_mask = half ? 0x7c00u : 0x7f800000u;
|
||||
const uint32_t mantissa_mask = half ? 0x03ffu : 0x007fffffu;
|
||||
const uint32_t quiet_mask = half ? 0x0200u : 0x00400000u;
|
||||
const auto sign_bits = EmitAndConstant(state, bits, sign_mask);
|
||||
const auto abs_bits = EmitAndConstant(state, bits, sign_mask - 1u);
|
||||
const auto exponent_bits = EmitAndConstant(state, abs_bits, exponent_mask);
|
||||
const auto mantissa_bits = EmitAndConstant(state, abs_bits, mantissa_mask);
|
||||
const auto quiet_bits = EmitAndConstant(state, mantissa_bits, quiet_mask);
|
||||
|
||||
const auto sign = EmitCompareU32Constant(state, spv::OpINotEqual, sign_bits, 0);
|
||||
const auto positive = EmitLogicalNotBool(state, sign);
|
||||
const auto exponent_zero = EmitCompareU32Constant(state, spv::OpIEqual, exponent_bits, 0);
|
||||
const auto exponent_nonzero = EmitLogicalNotBool(state, exponent_zero);
|
||||
const auto exponent_inf =
|
||||
EmitCompareU32Constant(state, spv::OpIEqual, exponent_bits, 0x7f800000u);
|
||||
EmitCompareU32Constant(state, spv::OpIEqual, exponent_bits, exponent_mask);
|
||||
const auto finite_exponent = EmitLogicalNotBool(state, exponent_inf);
|
||||
const auto mantissa_zero = EmitCompareU32Constant(state, spv::OpIEqual, mantissa_bits, 0);
|
||||
const auto mantissa_nonzero = EmitLogicalNotBool(state, mantissa_zero);
|
||||
@@ -178,6 +181,14 @@ uint32_t EmitClassMaskF32(EmitterState& state, uint32_t value, uint32_t mask) {
|
||||
EmitClassMaskBitMatch(state, mask, 9, EmitLogicalAndBool(state, inf, positive)));
|
||||
}
|
||||
|
||||
uint32_t EmitClassMaskF32(EmitterState& state, uint32_t value, uint32_t mask) {
|
||||
return EmitClassMaskBits(state, EmitBitcastF32ToU32(state, value), mask, false);
|
||||
}
|
||||
|
||||
uint32_t EmitClassMaskF16(EmitterState& state, uint32_t bits, uint32_t mask) {
|
||||
return EmitClassMaskBits(state, bits, mask, true);
|
||||
}
|
||||
|
||||
uint32_t EmitMinMaxF32Value(EmitterState& state, uint32_t lhs, uint32_t rhs, bool max_value) {
|
||||
const auto lhs_class = EmitClassifyF32(state, lhs);
|
||||
const auto rhs_class = EmitClassifyF32(state, rhs);
|
||||
|
||||
@@ -152,6 +152,7 @@ inline constexpr auto EmitFPOrdGreaterThanEqual32 = EmitFloatCompare32<spv::OpFO
|
||||
inline constexpr auto EmitFPUnordGreaterThanEqual32 = EmitFloatCompare32<spv::OpFUnordGreaterThanEqual>;
|
||||
uint32_t EmitFPIsNan32(EmitterState& state, uint32_t arg0);
|
||||
inline constexpr auto EmitFPCmpClass32 = EmitClassMaskF32;
|
||||
inline constexpr auto EmitFPCmpClass16 = EmitClassMaskF16;
|
||||
EMIT_NATIVE(FPAdd32, OpFAdd, F32, uint32_t, uint32_t)
|
||||
EMIT_NATIVE(FPSub32, OpFSub, F32, uint32_t, uint32_t)
|
||||
EMIT_NATIVE(FPMul32, OpFMul, F32, uint32_t, uint32_t)
|
||||
|
||||
@@ -510,6 +510,7 @@ uint32_t EmitClassMaskBitMatch(EmitterState& state, uint32_t mask, uint32_t bit,
|
||||
uint32_t class_match);
|
||||
|
||||
uint32_t EmitClassMaskF32(EmitterState& state, uint32_t value, uint32_t mask);
|
||||
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);
|
||||
|
||||
|
||||
@@ -397,6 +397,7 @@ enum class Opcode {
|
||||
V_CMP_T_I32,
|
||||
V_CMP_CLASS_F32,
|
||||
V_CMPX_CLASS_F32,
|
||||
V_CMPX_CLASS_F16,
|
||||
V_CMP_LT_I16,
|
||||
V_CMP_EQ_I16,
|
||||
V_CMP_LE_I16,
|
||||
|
||||
@@ -252,6 +252,7 @@ constexpr VopcOpcodeInfo VOPC_OPCODE_LIST[] = {
|
||||
{0x99u, Opcode::V_CMPX_LT_I16, false}, {0x9au, Opcode::V_CMPX_EQ_I16, false},
|
||||
{0x9bu, Opcode::V_CMPX_LE_I16, false}, {0x9cu, Opcode::V_CMPX_GT_I16, false},
|
||||
{0x9du, Opcode::V_CMPX_NE_I16, false}, {0x9eu, Opcode::V_CMPX_GE_I16, false},
|
||||
{0x9fu, Opcode::V_CMPX_CLASS_F16, false},
|
||||
{0xa9u, Opcode::V_CMP_LT_U16}, {0xaau, Opcode::V_CMP_EQ_U16},
|
||||
{0xabu, Opcode::V_CMP_LE_U16}, {0xacu, Opcode::V_CMP_GT_U16},
|
||||
{0xadu, Opcode::V_CMP_NE_U16}, {0xaeu, Opcode::V_CMP_GE_U16},
|
||||
@@ -917,6 +918,7 @@ bool IsVopcFloatCompareOpcode(Opcode opcode) {
|
||||
case Opcode::V_CMPX_NEQ_F16:
|
||||
case Opcode::V_CMPX_NLT_F16:
|
||||
case Opcode::V_CMP_CLASS_F32:
|
||||
case Opcode::V_CMPX_CLASS_F16:
|
||||
case Opcode::V_CMPX_CLASS_F32: return true;
|
||||
default: return false;
|
||||
}
|
||||
@@ -1540,6 +1542,7 @@ bool IsVopcCompareExec(Opcode opcode) {
|
||||
case Opcode::V_CMPX_NE_I32:
|
||||
case Opcode::V_CMPX_GE_I32:
|
||||
case Opcode::V_CMPX_CLASS_F32:
|
||||
case Opcode::V_CMPX_CLASS_F16:
|
||||
case Opcode::V_CMPX_LT_U32:
|
||||
case Opcode::V_CMPX_EQ_U32:
|
||||
case Opcode::V_CMPX_LE_U32:
|
||||
|
||||
@@ -72,11 +72,13 @@ void Translator::EmitFloatOrderedCompare(const Decoder::Instruction& inst, bool
|
||||
EmitCompareResult(inst, ordered ? ir.LogicalNot(unordered) : unordered, false, cmpx);
|
||||
}
|
||||
|
||||
void Translator::EmitFloatClassCompare(const Decoder::Instruction& inst, bool cmpx) {
|
||||
const auto value = ReadOperand(inst.src0, IR::Type::F32);
|
||||
const auto mask = ReadOperand(inst.src1, IR::Type::U32);
|
||||
EmitCompareResult(inst, IR::U1(ir.Emit(IR::ValueOpcode::FPCmpClass32, {value, mask})), false,
|
||||
cmpx);
|
||||
void Translator::EmitFloatClassCompare(const Decoder::Instruction& inst, bool cmpx, bool half) {
|
||||
const auto value = half ? IR::Value(Read16LaneBits(inst.src0, false))
|
||||
: ReadOperand(inst.src0, IR::Type::F32);
|
||||
const auto mask = half ? IR::Value(Read16LaneBits(inst.src1, false))
|
||||
: ReadOperand(inst.src1, IR::Type::U32);
|
||||
const auto opcode = half ? IR::ValueOpcode::FPCmpClass16 : IR::ValueOpcode::FPCmpClass32;
|
||||
EmitCompareResult(inst, IR::U1(ir.Emit(opcode, {value, mask})), false, cmpx);
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Frontend
|
||||
|
||||
@@ -8,13 +8,13 @@ namespace Libs::Graphics::ShaderRecompiler::Frontend {
|
||||
void Translator::PackedFloat16(const Decoder::Instruction& inst, IR::ValueOpcode opcode,
|
||||
bool accumulator, bool quiet_snan) {
|
||||
const auto translate_lane = [&](bool high) {
|
||||
const auto lhs = ReadF16LaneAsF32(inst.src0, high, true);
|
||||
const auto rhs = ReadF16LaneAsF32(inst.src1, high, true);
|
||||
const auto lhs = ReadF16LaneAsF32(inst.src0, high);
|
||||
const auto rhs = ReadF16LaneAsF32(inst.src1, high);
|
||||
IR::F32 result;
|
||||
if (accumulator) {
|
||||
result = IR::F32(ir.Emit(opcode, {lhs, rhs, ReadF16LaneAsF32(inst.dst, high, true)}));
|
||||
result = IR::F32(ir.Emit(opcode, {lhs, rhs, ReadF16LaneAsF32(inst.dst, high)}));
|
||||
} else if (inst.src_count == 3u) {
|
||||
result = IR::F32(ir.Emit(opcode, {lhs, rhs, ReadF16LaneAsF32(inst.src2, high, true)}));
|
||||
result = IR::F32(ir.Emit(opcode, {lhs, rhs, ReadF16LaneAsF32(inst.src2, high)}));
|
||||
} else {
|
||||
result = IR::F32(ir.Emit(opcode, {lhs, rhs}));
|
||||
}
|
||||
|
||||
@@ -503,41 +503,11 @@ IR::U64 Translator::ReadU64(const Decoder::Operand& operand) {
|
||||
return IR::U64(ReadOperand(operand, IR::Type::U64));
|
||||
}
|
||||
|
||||
IR::F32 Translator::ReadF16LaneAsF32(const Decoder::Operand& operand, bool high_lane, bool packed) {
|
||||
if (operand.kind == Decoder::OperandKind::FloatInlineConstant) {
|
||||
const bool use_zero = packed && (high_lane ? operand.op_sel_hi : operand.op_sel);
|
||||
auto value = use_zero ? IR::F32(IR::Value::F32(0.0f))
|
||||
: ir.BitCastF32(IR::U32(IR::Value(operand.value)));
|
||||
const auto half = IR::F16(ir.Emit(IR::ValueOpcode::ConvertF16F32, {value}));
|
||||
value = IR::F32(ir.Emit(IR::ValueOpcode::ConvertF32F16, {half}));
|
||||
if (operand.absolute) {
|
||||
value = IR::F32(ir.Emit(IR::ValueOpcode::FPAbs32, {value}));
|
||||
}
|
||||
if (high_lane ? operand.negate_hi : operand.negate) {
|
||||
value = IR::F32(ir.Emit(IR::ValueOpcode::FPNeg32, {value}));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
auto raw_operand = operand;
|
||||
raw_operand.sdwa_sel = 6;
|
||||
raw_operand.sdwa_sext = false;
|
||||
const auto bits = ApplyBitSourceModifiers(raw_operand, ReadRawU32(operand));
|
||||
uint32_t offset = (high_lane ? operand.op_sel_hi : operand.op_sel) ? 16u : 0u;
|
||||
if (operand.sdwa_sel == 4u || operand.sdwa_sel == 5u) {
|
||||
offset = operand.sdwa_sel == 5u ? 16u : 0u;
|
||||
}
|
||||
const auto half_u32 = IR::U32(
|
||||
ir.Emit(IR::ValueOpcode::BitFieldUExtract, {bits, IR::Value(offset), IR::Value(16u)}));
|
||||
const auto half_u16 = IR::U16(ir.Emit(IR::ValueOpcode::ConvertU16U32, {half_u32}));
|
||||
IR::F32 Translator::ReadF16LaneAsF32(const Decoder::Operand& operand, bool high_lane) {
|
||||
const auto bits = Read16LaneBits(operand, high_lane);
|
||||
const auto half_u16 = IR::U16(ir.Emit(IR::ValueOpcode::ConvertU16U32, {bits}));
|
||||
const auto half = IR::F16(ir.Emit(IR::ValueOpcode::BitCastF16U16, {half_u16}));
|
||||
auto value = IR::F32(ir.Emit(IR::ValueOpcode::ConvertF32F16, {half}));
|
||||
if (operand.absolute) {
|
||||
value = IR::F32(ir.Emit(IR::ValueOpcode::FPAbs32, {value}));
|
||||
}
|
||||
if (high_lane ? operand.negate_hi : operand.negate) {
|
||||
value = IR::F32(ir.Emit(IR::ValueOpcode::FPNeg32, {value}));
|
||||
}
|
||||
return value;
|
||||
return IR::F32(ir.Emit(IR::ValueOpcode::ConvertF32F16, {half}));
|
||||
}
|
||||
|
||||
IR::F32 Translator::ReadF16AsF32(const Decoder::Operand& operand) {
|
||||
@@ -555,29 +525,6 @@ IR::F32 Translator::ReadMixF32(const Decoder::Operand& operand) {
|
||||
return IR::F32(ReadOperand(value_operand, IR::Type::F32));
|
||||
}
|
||||
|
||||
IR::U32 Translator::ReadU16LaneRaw(const Decoder::Operand& operand, bool high_lane) {
|
||||
auto raw_operand = operand;
|
||||
raw_operand.sdwa_sel = 6;
|
||||
raw_operand.sdwa_sext = false;
|
||||
const auto bits = ApplyBitSourceModifiers(raw_operand, ReadRawU32(operand));
|
||||
uint32_t offset = (high_lane ? operand.op_sel_hi : operand.op_sel) ? 16u : 0u;
|
||||
uint32_t width = 16u;
|
||||
if (operand.sdwa_sel <= 3u) {
|
||||
offset = operand.sdwa_sel * 8u;
|
||||
width = 8u;
|
||||
} else if (operand.sdwa_sel == 4u || operand.sdwa_sel == 5u) {
|
||||
offset = operand.sdwa_sel == 5u ? 16u : 0u;
|
||||
}
|
||||
if (width == 8u && operand.sdwa_sext) {
|
||||
// Extend the selected byte into the 16-bit operand before halfword modifiers.
|
||||
const auto value = IR::U32(
|
||||
ir.Emit(IR::ValueOpcode::BitFieldSExtract, {bits, IR::Value(offset), IR::Value(width)}));
|
||||
return ir.BitwiseAnd(value, IR::U32(IR::Value(0xffffu)));
|
||||
}
|
||||
return IR::U32(
|
||||
ir.Emit(IR::ValueOpcode::BitFieldUExtract, {bits, IR::Value(offset), IR::Value(width)}));
|
||||
}
|
||||
|
||||
IR::U32 Translator::ReadU16LaneAsU32(const Decoder::Operand& operand, bool high_lane,
|
||||
bool sign_extend) {
|
||||
auto value = Read16LaneBits(operand, high_lane);
|
||||
@@ -593,12 +540,36 @@ IR::U32 Translator::ReadU16AsU32(const Decoder::Operand& operand, bool sign_exte
|
||||
}
|
||||
|
||||
IR::U32 Translator::Read16LaneBits(const Decoder::Operand& operand, bool high_lane) {
|
||||
auto value = ReadU16LaneRaw(operand, high_lane);
|
||||
auto raw_operand = operand;
|
||||
raw_operand.sdwa_sel = 6;
|
||||
raw_operand.sdwa_sext = false;
|
||||
auto bits = ReadRawU32(operand);
|
||||
if (operand.kind == Decoder::OperandKind::FloatInlineConstant) {
|
||||
// Inline floats encode the operand's width before SDWA or OP_SEL selects bits.
|
||||
const auto half = IR::F16(ir.Emit(IR::ValueOpcode::ConvertF16F32, {ir.BitCastF32(bits)}));
|
||||
const auto half_bits = IR::U16(ir.Emit(IR::ValueOpcode::BitCastU16F16, {half}));
|
||||
bits = IR::U32(ir.Emit(IR::ValueOpcode::ConvertU32U16, {half_bits}));
|
||||
}
|
||||
bits = ApplyBitSourceModifiers(raw_operand, bits);
|
||||
uint32_t offset = (high_lane ? operand.op_sel_hi : operand.op_sel) ? 16u : 0u;
|
||||
uint32_t width = 16u;
|
||||
if (operand.sdwa_sel <= 3u) {
|
||||
offset = operand.sdwa_sel * 8u;
|
||||
width = 8u;
|
||||
} else if (operand.sdwa_sel == 4u || operand.sdwa_sel == 5u) {
|
||||
offset = operand.sdwa_sel == 5u ? 16u : 0u;
|
||||
}
|
||||
const auto extract = width == 8u && operand.sdwa_sext
|
||||
? IR::ValueOpcode::BitFieldSExtract
|
||||
: IR::ValueOpcode::BitFieldUExtract;
|
||||
auto value = IR::U32(ir.Emit(extract, {bits, IR::Value(offset), IR::Value(width)}));
|
||||
if (width == 8u && operand.sdwa_sext) {
|
||||
value = ir.BitwiseAnd(value, IR::U32(IR::Value(0xffffu)));
|
||||
}
|
||||
if (operand.absolute) {
|
||||
value = ir.BitwiseAnd(value, IR::U32(IR::Value(0x7fffu)));
|
||||
}
|
||||
if (high_lane ? operand.negate_hi : operand.negate) {
|
||||
// RDNA2 source NEG flips the sign bit, including packed integer operands.
|
||||
value = ir.BitwiseXor(value, IR::U32(IR::Value(0x8000u)));
|
||||
}
|
||||
return value;
|
||||
|
||||
@@ -40,10 +40,9 @@ private:
|
||||
IR::U32 ReadU32(const Decoder::Operand& operand);
|
||||
std::array<IR::U32, 2> ReadU32Pair(const Decoder::Operand& operand);
|
||||
IR::U64 ReadU64(const Decoder::Operand& operand);
|
||||
IR::F32 ReadF16LaneAsF32(const Decoder::Operand& operand, bool high_lane, bool packed = false);
|
||||
IR::F32 ReadF16LaneAsF32(const Decoder::Operand& operand, bool high_lane);
|
||||
IR::F32 ReadF16AsF32(const Decoder::Operand& operand);
|
||||
IR::F32 ReadMixF32(const Decoder::Operand& operand);
|
||||
IR::U32 ReadU16LaneRaw(const Decoder::Operand& operand, bool high_lane);
|
||||
IR::U32 ReadU16LaneAsU32(const Decoder::Operand& operand, bool high_lane, bool sign_extend);
|
||||
IR::U32 ReadU16AsU32(const Decoder::Operand& operand, bool sign_extend);
|
||||
IR::U32 Read16LaneBits(const Decoder::Operand& operand, bool high_lane);
|
||||
@@ -127,7 +126,7 @@ private:
|
||||
void EmitFloatCompare(const Decoder::Instruction& inst, IR::ValueOpcode opcode, bool half,
|
||||
bool cmpx);
|
||||
void EmitFloatOrderedCompare(const Decoder::Instruction& inst, bool ordered, bool cmpx);
|
||||
void EmitFloatClassCompare(const Decoder::Instruction& inst, bool cmpx);
|
||||
void EmitFloatClassCompare(const Decoder::Instruction& inst, bool cmpx, bool half = false);
|
||||
void V_CVT_F32_UBYTE(const Decoder::Instruction& inst, uint32_t byte_index);
|
||||
void V_CVT_F32_U32(const Decoder::Instruction& inst);
|
||||
void V_CVT_F32_I32(const Decoder::Instruction& inst);
|
||||
|
||||
@@ -346,6 +346,7 @@ void Translator::EmitVector(const Decoder::Instruction& inst) {
|
||||
case O::V_CMPX_O_F32: EmitFloatOrderedCompare(inst, true, true); return;
|
||||
case O::V_CMP_CLASS_F32: EmitFloatClassCompare(inst, false); return;
|
||||
case O::V_CMPX_CLASS_F32: EmitFloatClassCompare(inst, true); return;
|
||||
case O::V_CMPX_CLASS_F16: EmitFloatClassCompare(inst, true, true); return;
|
||||
|
||||
case O::V_CVT_F32_UBYTE0: V_CVT_F32_UBYTE(inst, 0); return;
|
||||
case O::V_CVT_F32_UBYTE1: V_CVT_F32_UBYTE(inst, 1); return;
|
||||
|
||||
@@ -175,6 +175,7 @@ VALUE_OPCODE(FPOrdGreaterThanEqual32, U1, F32, F32)
|
||||
VALUE_OPCODE(FPUnordGreaterThanEqual32, U1, F32, F32)
|
||||
VALUE_OPCODE(FPIsNan32, U1, F32)
|
||||
VALUE_OPCODE(FPCmpClass32, U1, F32, U32)
|
||||
VALUE_OPCODE(FPCmpClass16, U1, U32, U32)
|
||||
|
||||
VALUE_OPCODE(FPAdd32, F32, F32, F32)
|
||||
VALUE_OPCODE(FPSub32, F32, F32, F32)
|
||||
|
||||
Reference in New Issue
Block a user