diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 16549f109..cbdd4ba36 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -632,6 +632,7 @@ struct Operand { uint32_t dpp_ctrl = 0; uint32_t dpp_row_mask = 0xf; uint32_t dpp_bank_mask = 0xf; + bool explicit_sdwa_dst = false; bool sdwa_sext = false; bool dpp_fetch_inactive = false; bool dpp_bound_ctrl = false; diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index da620b482..600b6a410 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -13,6 +13,7 @@ enum class Vop2SdwaProfile { None, Float32, Float16, + PackedFloat16, IntegerFullDestination, IntegerPartialDestination, ReverseRightShift, @@ -66,7 +67,7 @@ constexpr Vop2OpcodeInfo VOP2_OPCODE_LIST[] = { {0x2bu, Opcode::V_MAC_F32}, {0x2cu, Opcode::V_MADMK_F32}, {0x2du, Opcode::V_MADAK_F32}, - {0x2fu, Opcode::V_CVT_PKRTZ_F16_F32}, + {0x2fu, Opcode::V_CVT_PKRTZ_F16_F32, Vop2SdwaProfile::PackedFloat16}, {0x32u, Opcode::V_ADD_F16, Vop2SdwaProfile::Float16}, {0x33u, Opcode::V_SUB_F16, Vop2SdwaProfile::Float16}, {0x34u, Opcode::V_SUBREV_F16, Vop2SdwaProfile::Float16}, @@ -417,14 +418,6 @@ bool IsPermlaneOpcode(Opcode opcode) { return opcode == Opcode::V_PERMLANE16_B32 || opcode == Opcode::V_PERMLANEX16_B32; } -bool IsVop2LowHalfF16Opcode(Opcode opcode) { - return opcode == Opcode::V_ADD_F16 || opcode == Opcode::V_SUB_F16 || - opcode == Opcode::V_SUBREV_F16 || opcode == Opcode::V_MUL_F16 || - opcode == Opcode::V_FMAC_F16 || opcode == Opcode::V_FMAMK_F16 || - opcode == Opcode::V_FMAAK_F16 || opcode == Opcode::V_MAX_F16 || - opcode == Opcode::V_MIN_F16; -} - bool IsNativeVop3F16TernaryOpcode(Opcode opcode) { return opcode == Opcode::V_MIN3_F16 || opcode == Opcode::V_MAX3_F16 || opcode == Opcode::V_MED3_F16 || opcode == Opcode::V_FMA_F16; @@ -451,12 +444,6 @@ bool IsNativeVop3B16BinaryOpcode(Opcode opcode) { } } -void ApplyDefaultVop2F16Destination(Instruction& inst) { - if (IsVop2LowHalfF16Opcode(inst.opcode)) { - inst.dst.sdwa_sel = 4; - } -} - bool IsVop1FloatResultOpcode(Opcode opcode); bool IsVopcCompareExec(Opcode opcode); @@ -471,6 +458,8 @@ bool IsVop1FloatSourceOpcode(Opcode opcode) { case Opcode::V_FREXP_EXP_I32_F32: case Opcode::V_FREXP_MANT_F32: case Opcode::V_CVT_F16_F32: + case Opcode::V_CVT_U16_F16: + case Opcode::V_CVT_I16_F16: case Opcode::V_RCP_F32: case Opcode::V_RCP_IFLAG_F32: case Opcode::V_FRACT_F32: @@ -482,7 +471,11 @@ bool IsVop1FloatSourceOpcode(Opcode opcode) { case Opcode::V_LOG_F32: case Opcode::V_RSQ_F32: case Opcode::V_SQRT_F32: + case Opcode::V_RCP_F16: case Opcode::V_SQRT_F16: + case Opcode::V_RSQ_F16: + case Opcode::V_LOG_F16: + case Opcode::V_EXP_F16: case Opcode::V_FLOOR_F16: case Opcode::V_CEIL_F16: case Opcode::V_TRUNC_F16: @@ -528,33 +521,34 @@ constexpr Vop1SdwaRule VOP1_SDWA_RULES[] = { {Opcode::V_NOT_B32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false}, {Opcode::V_FFBL_B32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false}, {Opcode::V_CVT_F32_F16, SdwaSelWords() | SdwaSelFull(), 0, 0, true}, - {Opcode::V_CVT_F16_F32, SdwaSelFull(), SdwaSelWords(), SdwaSelFull(), false}, + {Opcode::V_CVT_F16_F32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), SdwaSelWords(), + SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), true}, {Opcode::V_CVT_F16_U16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(), SdwaSelWords() | SdwaSelFull(), false}, {Opcode::V_CVT_U16_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(), - SdwaSelWords() | SdwaSelFull(), false}, + SdwaSelWords() | SdwaSelFull(), true}, {Opcode::V_CVT_F16_I16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(), SdwaSelWords() | SdwaSelFull(), false}, {Opcode::V_CVT_I16_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(), - SdwaSelWords() | SdwaSelFull(), false}, + SdwaSelWords() | SdwaSelFull(), true}, {Opcode::V_RCP_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(), SdwaSelWords() | SdwaSelFull(), true}, {Opcode::V_SQRT_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(), - SdwaSelWords() | SdwaSelFull(), false}, + SdwaSelWords() | SdwaSelFull(), true}, {Opcode::V_RSQ_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(), - SdwaSelWords() | SdwaSelFull(), false}, + SdwaSelWords() | SdwaSelFull(), true}, {Opcode::V_LOG_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(), SdwaSelWords() | SdwaSelFull(), true}, {Opcode::V_EXP_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(), SdwaSelWords() | SdwaSelFull(), true}, {Opcode::V_FLOOR_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(), - SdwaSelWords() | SdwaSelFull(), false}, + SdwaSelWords() | SdwaSelFull(), true}, {Opcode::V_CEIL_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(), - SdwaSelWords() | SdwaSelFull(), false}, + SdwaSelWords() | SdwaSelFull(), true}, {Opcode::V_TRUNC_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(), - SdwaSelWords() | SdwaSelFull(), false}, + SdwaSelWords() | SdwaSelFull(), true}, {Opcode::V_RNDNE_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(), - SdwaSelWords() | SdwaSelFull(), false}, + SdwaSelWords() | SdwaSelFull(), true}, {Opcode::V_SIN_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(), SdwaSelWords() | SdwaSelFull(), true}, {Opcode::V_COS_F16, SdwaSelWords() | SdwaSelFull(), SdwaSelWords(), @@ -586,7 +580,7 @@ bool IsValidFullSdwaDestinationUnused(uint32_t dst_u) { bool IsVop1SdwaSourceSupported(Opcode opcode, uint32_t src_sel, bool src_neg, bool src_abs) { if (src_sel == 6u) { - return true; + return IsVop1FloatSourceOpcode(opcode) || (!src_neg && !src_abs); } const auto* rule = FindVop1SdwaRule(opcode); if (rule == nullptr || !HasSdwaSelector(rule->source_selectors, src_sel)) { @@ -597,29 +591,24 @@ bool IsVop1SdwaSourceSupported(Opcode opcode, uint32_t src_sel, bool src_neg, bo bool IsVop1SdwaDestinationSupported(Opcode opcode, uint32_t dst_sel, uint32_t dst_u, uint32_t src_sel) { - if ((opcode == Opcode::V_CVT_U16_F16 || opcode == Opcode::V_CVT_I16_F16) && dst_sel == 6u) { - return false; - } if (dst_sel == 6u) { return IsValidFullSdwaDestinationUnused(dst_u); } const auto* rule = FindVop1SdwaRule(opcode); - if (rule == nullptr || dst_u != 2u) { + if (rule == nullptr || dst_u == 3u) { return false; } return HasSdwaSelector(rule->partial_dst_selectors, dst_sel) && HasSdwaSelector(rule->partial_dst_source_selectors, src_sel); } -bool HasUnsupportedVop1SdwaSourceModifiers(Opcode opcode, bool src_neg, bool src_abs) { - switch (opcode) { - case Opcode::V_CVT_U16_F16: - case Opcode::V_CVT_I16_F16: - case Opcode::V_SQRT_F16: - case Opcode::V_RSQ_F16: return src_neg || src_abs; - case Opcode::V_LOG_F16: return src_neg; - default: return false; - } +bool UsesInexactClampControl(Opcode opcode) { + return opcode == Opcode::V_CVT_U32_F32 || opcode == Opcode::V_CVT_I32_F32 || + opcode == Opcode::V_CVT_U16_F16 || opcode == Opcode::V_CVT_I16_F16; +} + +bool SupportsVop1Clamp(Opcode opcode) { + return IsVop1FloatResultOpcode(opcode) || UsesInexactClampControl(opcode); } bool ValidateVop1Sdwa(Instruction& inst, uint32_t opcode, uint32_t modifier) { @@ -635,7 +624,8 @@ bool ValidateVop1Sdwa(Instruction& inst, uint32_t opcode, uint32_t modifier) { SetUnsupported(inst, Family::VOP1, opcode, "VOP1 SDWA selector is invalid"); return false; } - if ((clamp != 0u || omod != 0u) && !IsVop1FloatResultOpcode(inst.opcode)) { + if ((clamp != 0u && !SupportsVop1Clamp(inst.opcode)) || + (omod != 0u && !IsVop1FloatResultOpcode(inst.opcode))) { SetUnsupported(inst, Family::VOP1, opcode, "VOP1 SDWA output modifiers are not supported"); return false; } @@ -644,10 +634,6 @@ bool ValidateVop1Sdwa(Instruction& inst, uint32_t opcode, uint32_t modifier) { "VOP1 SDWA destination selector is not supported"); return false; } - if (HasUnsupportedVop1SdwaSourceModifiers(inst.opcode, src0_neg != 0u, src0_abs != 0u)) { - SetUnsupported(inst, Family::VOP1, opcode, "VOP1 SDWA source modifiers are not supported"); - return false; - } if (!IsVop1SdwaSourceSupported(inst.opcode, src0_sel, src0_neg != 0u, src0_abs != 0u)) { SetUnsupported(inst, Family::VOP1, opcode, "VOP1 SDWA source selector is not supported"); return false; @@ -685,10 +671,11 @@ bool DecodeVop1Sdwa(uint32_t pc, std::span code, uint32_t word_i if (!DecodeScalarSource(src0 + (s0 == 0u ? 256u : 0u), pc, inst.src0, error)) { return false; } - inst.dst.sdwa_sel = dst_sel; - inst.dst.sdwa_dst_unused = dst_u; - inst.dst.clamp = clamp != 0u; - inst.dst.omod = omod; + inst.dst.sdwa_sel = dst_sel; + inst.dst.sdwa_dst_unused = dst_u; + inst.dst.explicit_sdwa_dst = true; + inst.dst.clamp = clamp != 0u; + inst.dst.omod = omod; inst.src0.sdwa_sel = src0_sel; inst.src0.sdwa_sext = src0_sext != 0u; inst.src0.negate = src0_neg != 0u; @@ -780,6 +767,7 @@ bool IsVop2FloatSourceOpcode(Opcode opcode) { case Opcode::V_MAC_F32: case Opcode::V_MADMK_F32: case Opcode::V_MADAK_F32: + case Opcode::V_CVT_PKRTZ_F16_F32: case Opcode::V_ADD_F16: case Opcode::V_SUB_F16: case Opcode::V_SUBREV_F16: @@ -804,6 +792,7 @@ bool IsVop2FloatResultOpcode(Opcode opcode) { case Opcode::V_MAC_F32: case Opcode::V_MADMK_F32: case Opcode::V_MADAK_F32: + case Opcode::V_CVT_PKRTZ_F16_F32: case Opcode::V_ADD_F16: case Opcode::V_SUB_F16: case Opcode::V_SUBREV_F16: @@ -822,6 +811,9 @@ bool IsVop1FloatResultOpcode(Opcode opcode) { case Opcode::V_CVT_F32_I32: case Opcode::V_CVT_F32_U32: case Opcode::V_CVT_F32_F16: + case Opcode::V_CVT_F16_F32: + case Opcode::V_CVT_F16_U16: + case Opcode::V_CVT_F16_I16: case Opcode::V_CVT_OFF_F32_I4: case Opcode::V_CVT_F32_UBYTE0: case Opcode::V_CVT_F32_UBYTE1: @@ -840,9 +832,14 @@ bool IsVop1FloatResultOpcode(Opcode opcode) { case Opcode::V_RSQ_F32: case Opcode::V_SQRT_F32: case Opcode::V_RCP_F16: + case Opcode::V_SQRT_F16: case Opcode::V_RSQ_F16: case Opcode::V_LOG_F16: case Opcode::V_EXP_F16: + case Opcode::V_FLOOR_F16: + case Opcode::V_CEIL_F16: + case Opcode::V_TRUNC_F16: + case Opcode::V_RNDNE_F16: case Opcode::V_SIN_F16: case Opcode::V_COS_F16: case Opcode::V_SIN_F32: @@ -957,6 +954,7 @@ constexpr Vop2SdwaRule VOP2_SDWA_RULES[] = { {SdwaSelFull(), SdwaSelFull(), SdwaSelFull(), false, true}, {SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(), SdwaSelWords() | SdwaSelFull(), true, true}, + {SdwaSelAll(), SdwaSelAll(), SdwaSelAll(), true, true}, {SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false}, {SdwaSelAll(), SdwaSelAll(), SdwaSelAll(), true, false}, {SdwaSelFull(), SdwaSelAll(), SdwaSelWords() | SdwaSelFull(), false, false}, @@ -981,7 +979,7 @@ bool IsVop2SdwaDestinationSupported(const Vop2SdwaRule& rule, const Vop2SdwaFiel if (fields.dst_sel == 6u) { return IsValidFullSdwaDestinationUnused(fields.dst_u); } - return fields.dst_u == 2u && rule.partial_dst; + return fields.dst_u != 3u && rule.partial_dst; } bool IsFullWidthVop2Sdwa(const Vop2SdwaFields& fields) { @@ -1087,10 +1085,11 @@ bool DecodeVop2Sdwa(uint32_t pc, std::span code, uint32_t word_i !DecodeScalarSource(vsrc1 + (fields.s1 == 0u ? 256u : 0u), pc, inst.src1, error)) { return false; } - inst.dst.sdwa_sel = fields.dst_sel; - inst.dst.sdwa_dst_unused = fields.dst_u; - inst.dst.clamp = fields.clamp != 0u; - inst.dst.omod = fields.omod; + inst.dst.sdwa_sel = fields.dst_sel; + inst.dst.sdwa_dst_unused = fields.dst_u; + inst.dst.explicit_sdwa_dst = true; + inst.dst.clamp = fields.clamp != 0u; + inst.dst.omod = fields.omod; inst.src0.sdwa_sel = fields.src0_sel; inst.src0.sdwa_sext = fields.src0_sext != 0u; inst.src0.negate = fields.src0_neg != 0u; @@ -1117,7 +1116,6 @@ bool DecodeVop2Dpp(uint32_t pc, std::span code, uint32_t word_in ApplyDppModifier(inst.src0, modifier); inst.src1.negate = ((modifier >> 22u) & 0x1u) != 0u; inst.src1.absolute = ((modifier >> 23u) & 0x1u) != 0u; - ApplyDefaultVop2F16Destination(inst); const bool packed_fmac = inst.opcode == Opcode::V_PK_FMAC_F16; if (packed_fmac) { inst.src0.negate_hi = inst.src0.negate; @@ -1451,6 +1449,7 @@ bool SupportsNativeVop3ResultModifiers(Opcode opcode) { case Opcode::V_MAD_F32: case Opcode::V_FMA_F32: case Opcode::V_FMA_F16: + case Opcode::V_CVT_PKRTZ_F16_F32: case Opcode::V_LDEXP_F32: case Opcode::V_MIN3_F32: case Opcode::V_MAX3_F32: @@ -1459,12 +1458,17 @@ bool SupportsNativeVop3ResultModifiers(Opcode opcode) { } } +bool SupportsNativeVop3Clamp(Opcode opcode) { + return SupportsNativeVop3ResultModifiers(opcode) || UsesInexactClampControl(opcode); +} + bool HasUnsupportedNativeVop3Modifiers(Opcode opcode, bool permlane, bool mad_mix, bool carry_in_out, bool scalar_dst, uint32_t abs, uint32_t op_sel, uint32_t clamp, uint32_t omod, uint32_t neg) { const bool source_modifiers = SupportsNativeVop3SourceModifiers(opcode); const bool result_modifiers = SupportsNativeVop3ResultModifiers(opcode); + const bool clamp_modifier = SupportsNativeVop3Clamp(opcode); if (permlane) { return abs != 0u || (op_sel & ~0x3u) != 0u || clamp != 0u || omod != 0u || neg != 0u; @@ -1496,7 +1500,7 @@ bool HasUnsupportedNativeVop3Modifiers(Opcode opcode, bool permlane, bool mad_mi } if (source_modifiers) { return op_sel != 0u || (omod != 0u && !result_modifiers) || - (clamp != 0u && !result_modifiers); + (clamp != 0u && !clamp_modifier); } if (result_modifiers) { return abs != 0u || op_sel != 0u || neg != 0u; @@ -1589,7 +1593,6 @@ bool DecodeVop2(uint32_t pc, std::span code, uint32_t word_index if (!DecodeScalarSource(src0, pc, inst.src0, error)) { return false; } - ApplyDefaultVop2F16Destination(inst); return FinalizeVop2Instruction(pc, code, word_index, inst, error); } @@ -1713,6 +1716,7 @@ bool DecodeVop3(uint32_t pc, std::span code, uint32_t word_index const bool scalar_modifier_limits = UsesScalarDestination(inst.opcode); const bool native_source_modifiers = SupportsNativeVop3SourceModifiers(inst.opcode); const bool native_result_modifiers = SupportsNativeVop3ResultModifiers(inst.opcode); + const bool native_clamp_modifier = SupportsNativeVop3Clamp(inst.opcode); const bool modifiers = HasUnsupportedNativeVop3Modifiers(inst.opcode, permlane, mad_mix, vop3b_uses_sdst, scalar_modifier_limits, abs, op_sel, clamp, omod, neg); @@ -1747,7 +1751,7 @@ bool DecodeVop3(uint32_t pc, std::span code, uint32_t word_index if (!dst_ok || !DecodeScalarSource(src0, pc, inst.src0, error)) { return false; } - inst.dst.clamp = native_result_modifiers && clamp != 0u; + inst.dst.clamp = native_clamp_modifier && clamp != 0u; inst.dst.omod = native_result_modifiers ? omod : 0u; if (permlane) { inst.dst.op_sel = (op_sel & 0x1u) != 0u; diff --git a/src/graphics/shader/recompiler/frontend/translate/Convert.cpp b/src/graphics/shader/recompiler/frontend/translate/Convert.cpp index 720290c86..6c12aac33 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Convert.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Convert.cpp @@ -141,8 +141,10 @@ void Translator::V_CVT_OFF_F32_I4(const Decoder::Instruction& inst) { } void Translator::V_CVT_PKRTZ_F16_F32(const Decoder::Instruction& inst) { - const auto lhs = ReadOperand(SourceAt(inst, 0), IR::Type::F32); - const auto rhs = ReadOperand(SourceAt(inst, 1), IR::Type::F32); + const auto lhs = ApplyF32ResultModifiers( + inst.dst, IR::F32(ReadOperand(SourceAt(inst, 0), IR::Type::F32))); + const auto rhs = ApplyF32ResultModifiers( + inst.dst, IR::F32(ReadOperand(SourceAt(inst, 1), IR::Type::F32))); WriteOperand(DestinationOperand(inst), ir.Emit(IR::ValueOpcode::PackFloat2x16Rtz, {lhs, rhs})); } diff --git a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp index 0af641c86..123010712 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp @@ -37,6 +37,7 @@ Decoder::Operand OffsetDecodedRegister(const Decoder::Operand& operand, uint32_t result.dpp_ctrl = 0; result.dpp_row_mask = 0xf; result.dpp_bank_mask = 0xf; + result.explicit_sdwa_dst = false; result.dpp_fetch_inactive = false; result.dpp_bound_ctrl = false; result.dpp = false; diff --git a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp index 5f3fbddec..e0f09499f 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp @@ -104,6 +104,7 @@ Decoder::Operand Translator::PlainOperand(const Decoder::Operand& operand) { result.dpp_ctrl = 0; result.dpp_row_mask = 0xf; result.dpp_bank_mask = 0xf; + result.explicit_sdwa_dst = false; result.dpp_fetch_inactive = false; result.dpp_bound_ctrl = false; result.dpp = false; @@ -392,12 +393,13 @@ void Translator::WriteOperand(const Decoder::Operand& operand, IR::Value value) } } if (type == IR::Type::U16) { - WriteRawU32(operand, IR::U32(ir.Emit(IR::ValueOpcode::ConvertU32U16, {IR::U16(value)}))); + Write16Bits(operand, + IR::U32(ir.Emit(IR::ValueOpcode::ConvertU32U16, {IR::U16(value)}))); return; } if (type == IR::Type::F16) { const auto bits = IR::U16(ir.Emit(IR::ValueOpcode::BitCastU16F16, {value})); - WriteRawU32(operand, IR::U32(ir.Emit(IR::ValueOpcode::ConvertU32U16, {bits}))); + Write16Bits(operand, IR::U32(ir.Emit(IR::ValueOpcode::ConvertU32U16, {bits}))); return; } if (type == IR::Type::U64) { @@ -417,38 +419,32 @@ IR::U32 Translator::PackHalf2x16(IR::F32 low, IR::F32 high) { return IR::U32(ir.Emit(IR::ValueOpcode::PackHalf2x16, {pair})); } +void Translator::Write16Bits(const Decoder::Operand& operand, IR::U32 value) { + auto destination = operand; + // Native GFX10 16-bit results preserve the unselected half. Plain/DPP destinations use the + // low half; native VOP3 may already select either half. Explicit SDWA retains encoded DST_U. + if (!destination.explicit_sdwa_dst) { + if (destination.sdwa_sel == 6u) { + destination.sdwa_sel = 4u; + } + destination.sdwa_dst_unused = 2u; + } + destination.omod = 0u; + destination.op_sel = false; + destination.clamp = false; + WriteRawU32(destination, ir.BitwiseAnd(value, IR::U32(IR::Value(0xffffu)))); +} + void Translator::WriteF16(const Decoder::Operand& operand, IR::F32 value) { value = ApplyF32ResultModifiers(operand, value); const auto half = IR::F16(ir.Emit(IR::ValueOpcode::ConvertF16F32, {value})); const auto bits = IR::U32(ir.Emit(IR::ValueOpcode::ConvertU32U16, {IR::U16(ir.Emit(IR::ValueOpcode::BitCastU16F16, {half}))})); - auto merged = bits; - if (operand.sdwa_sel == 4u || operand.sdwa_sel == 5u) { - merged = IR::U32(ir.Emit(IR::ValueOpcode::BitFieldInsert, - {ReadRawU32(PlainOperand(operand)), bits, - IR::Value(operand.sdwa_sel == 5u ? 16u : 0u), IR::Value(16u)})); - } - auto raw = operand; - raw.sdwa_sel = 6u; - raw.omod = 0u; - raw.op_sel = false; - raw.clamp = false; - WriteRawU32(raw, merged); + Write16Bits(operand, bits); } void Translator::WriteU16(const Decoder::Operand& operand, IR::U32 value) { - auto merged = ir.BitwiseAnd(value, IR::U32(IR::Value(0xffffu))); - if (operand.sdwa_sel == 4u || operand.sdwa_sel == 5u) { - merged = IR::U32(ir.Emit(IR::ValueOpcode::BitFieldInsert, - {ReadRawU32(PlainOperand(operand)), merged, - IR::Value(operand.sdwa_sel == 5u ? 16u : 0u), IR::Value(16u)})); - } - auto raw = operand; - raw.sdwa_sel = 6u; - raw.omod = 0u; - raw.op_sel = false; - raw.clamp = false; - WriteRawU32(raw, merged); + Write16Bits(operand, value); } IR::U32 Translator::ReadU32(const Decoder::Operand& operand) { diff --git a/src/graphics/shader/recompiler/frontend/translate/Translator.h b/src/graphics/shader/recompiler/frontend/translate/Translator.h index 4cf389536..fd3918361 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translator.h +++ b/src/graphics/shader/recompiler/frontend/translate/Translator.h @@ -34,6 +34,7 @@ private: IR::F32 ApplyF32ResultModifiers(const Decoder::Operand& operand, IR::F32 value); void WriteOperand(const Decoder::Operand& operand, IR::Value value); IR::U32 PackHalf2x16(IR::F32 low, IR::F32 high); + void Write16Bits(const Decoder::Operand& operand, IR::U32 value); void WriteF16(const Decoder::Operand& operand, IR::F32 value); void WriteU16(const Decoder::Operand& operand, IR::U32 value); IR::U32 ReadU32(const Decoder::Operand& operand); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 4392a7ff6..3714cf48b 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -553,6 +553,12 @@ constexpr u32 EncodeVop1Sdwa(u32 src0, u32 dst_sel = 6, u32 dst_u = 0, ((s0 & 0x1u) << 23u); } +constexpr u32 EncodeVop1Dpp(u32 src0, u32 dpp_ctrl = 0, u32 row_mask = 0xf, + u32 bank_mask = 0xf) { + return (src0 & 0xffu) | ((dpp_ctrl & 0x1ffu) << 8u) | + ((bank_mask & 0xfu) << 24u) | ((row_mask & 0xfu) << 28u); +} + constexpr u32 EncodeVop2(u32 opcode, u32 dst, u32 src0, u32 src1) { return ((opcode & 0x3fu) << 25u) | ((dst & 0xffu) << 17u) | ((src1 & 0xffu) << 9u) | (src0 & 0x1ffu); @@ -14368,6 +14374,54 @@ TestCase CvtPkrtzF16F32SubnormalRoundsTowardZero() { O::S_ENDPGM}}; } +TestCase CvtPkrtzF16F32SdwaAndOutputModifiers() { + using O = ShaderOpcode; + + std::vector code; + AppendVMovLiteral(&code, 0, 0x3f000000u); // 0.5f + AppendVMovLiteral(&code, 1, 0x40000000u); // 2.0f + AppendVMovLiteral(&code, 2, 0x3f802000u); // exactly 0x3c01h + AppendVMovLiteral(&code, 3, 0x00000000u); + AppendVMovLiteral(&code, 4, 0xbf000000u); // -0.5f + for (u32 dst = 10; dst <= 18; dst++) { + AppendVMovLiteral(&code, dst, 0xa1b2c3d4u); + } + + code.push_back(EncodeVop2(0x2f, 10, 249, 1)); + code.push_back(EncodeVop2Sdwa(0, 6, 2, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 1)); + code.push_back(EncodeVop2(0x2f, 11, 249, 3)); + code.push_back(EncodeVop2Sdwa(2, 0, 0)); + code.push_back(EncodeVop2(0x2f, 12, 249, 1)); + code.push_back(EncodeVop2Sdwa(0, 6, 0, 3, 5)); + code.push_back(EncodeVop2(0x2f, 13, 249, 1)); + code.push_back(EncodeVop2Sdwa(4, 6, 0, 6, 6, 0, 0, 0, 1, 1)); + + code.push_back(EncodeVop2(0x2f, 14, 250, 1)); + code.push_back(EncodeVop2Dpp(0, 0, 0xf, 0xf, 1)); + AppendVop3(&code, 0x12f, 15, Vgpr(0), Vgpr(1), 0, 0, 0, true); + code.push_back(EncodeVop2(0x2f, 16, 249, 1)); + code.push_back(EncodeVop2Sdwa(0, 4, 2)); + code.push_back(EncodeVop2(0x2f, 17, 249, 1)); + code.push_back(EncodeVop2Sdwa(0, 6, 2, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1)); + AppendVop3(&code, 0x12f, 18, Vgpr(0), Vgpr(1), 0, 0, 0, false, 1); + + for (u32 dst = 10; dst <= 18; dst++) { + AppendStoreVgpr(&code, dst, dst - 10); + } + AppendEnd(&code); + + return {"CvtPkrtzF16F32SdwaAndOutputModifiers", + code, + {}, + {0x3c003800u, 0x00000001u, 0x00000000u, 0xc0003800u, + 0x4000b800u, 0x3c003800u, 0xa1b23800u, 0x44003c00u, + 0x44003c00u}, + {O::V_MOV_B32, O::V_CVT_PKRTZ_F16_F32, O::BUFFER_STORE_DWORD, + O::S_ENDPGM}}; +} + TestCase PackedMinMaxF16NanAndSignedZeroEdges() { using O = ShaderOpcode; @@ -14475,6 +14529,99 @@ TestCase VectorCvtU16F16Sdwa() { {O::V_MOV_B32, O::V_CVT_U16_F16, O::BUFFER_STORE_DWORD, O::S_ENDPGM}}; } +TestCase NativeAndSdwa16BitDestinationWrites() { + using O = ShaderOpcode; + + std::vector code; + AppendVMovLiteral(&code, 0, 0x3fc00000u); // 1.5f -> 0x3e00h + AppendVMovLiteral(&code, 1, 0x00004200u); // low=3.0h + AppendVMovLiteral(&code, 2, 0x0000c000u); // low=-2.0h + AppendVMovLiteral(&code, 3, 0x00003c00u); // low=1.0h + AppendVMovLiteral(&code, 4, 0x00004000u); // low=2.0h + + const u32 sentinels[] = { + 0x3c001234u, 0xbbbb2345u, 0xcccc3456u, 0xdddd4567u, 0xeeee5678u, + 0xffff6789u, 0xf00d789au, 0x11118888u, 0x22229999u, 0x3333aaaau, + 0xaaaa0000u, 0xbbbb0000u, 0xcccc0000u, 0xabcd1234u, 0xbcde2345u, + 0xcdef3456u, 0xdef01234u, 0xef012345u, 0xf0123456u, 0x01234567u, + 0xaaaa1234u, 0xbbbb5678u, 0x24681234u, 0x13572468u, 0x89abcdefu, + 0xfedcba98u, 0x0bad1234u, + }; + // AppendStoreVgpr uses v31 as its address scratch register. + const auto output_vgpr = [](u32 index) { return 10u + index + (index >= 21u ? 1u : 0u); }; + for (u32 i = 0; i < static_cast(std::size(sentinels)); i++) { + AppendVMovLiteral(&code, output_vgpr(i), sentinels[i]); + } + + code.push_back(EncodeVop1(0x0a, 10, Vgpr(0))); + code.push_back(EncodeVop1(0x0a, 11, 250)); + code.push_back(EncodeVop1Dpp(0)); + AppendVop3(&code, 0x18a, 12, Vgpr(0), 0); + code.push_back(EncodeVop1(0x52, 13, Vgpr(1))); + code.push_back(EncodeVop1(0x53, 14, Vgpr(2))); + + code.push_back(EncodeVop1(0x0a, 15, 249)); + code.push_back(EncodeVop1Sdwa(0)); + code.push_back(EncodeVop1(0x54, 16, 249)); + code.push_back(EncodeVop1Sdwa(10, 4, 2, 5)); + code.push_back(EncodeVop1(0x52, 17, 249)); + code.push_back(EncodeVop1Sdwa(1, 6, 0, 4)); + code.push_back(EncodeVop1(0x52, 18, 249)); + code.push_back(EncodeVop1Sdwa(1, 4, 0, 4)); + code.push_back(EncodeVop1(0x53, 19, 249)); + code.push_back(EncodeVop1Sdwa(2, 4, 1, 4)); + + code.push_back(EncodeVop2(0x32, 20, 250, 4)); + code.push_back(EncodeVop2Dpp(3)); + AppendVop3(&code, 0x132, 21, Vgpr(3), Vgpr(4)); + code.push_back(EncodeVop2(0x32, 22, 249, 4)); + code.push_back(EncodeVop2Sdwa(3, 4, 0, 4, 4)); + + code.push_back(EncodeVop1(0x0a, 23, 249)); + code.push_back(EncodeVop1Sdwa(0, 4, 2, 3)); + code.push_back(EncodeVop1(0x0a, 24, 249)); + code.push_back(EncodeVop1Sdwa(0, 4, 2, 5)); + code.push_back(EncodeVop1(0x0a, 25, 249)); + code.push_back(EncodeVop1Sdwa(0, 4, 2, 6, 0, 1, 1)); + + code.push_back(EncodeVop1(0x0a, 26, 249)); + code.push_back(EncodeVop1Sdwa(0, 4, 2, 6, 0, 0, 0, 0, 0, 1)); + code.push_back(EncodeVop1(0x0a, 27, 249)); + code.push_back(EncodeVop1Sdwa(0, 4, 2, 6, 0, 0, 0, 0, 1)); + AppendVop3(&code, 0x18a, 28, Vgpr(0), 0, 0, 0, 0, false, 1); + AppendVop3(&code, 0x18a, 29, Vgpr(0), 0, 0, 0, 0, true); + AppendVop3(&code, 0x34b, 30, Vgpr(3), Vgpr(4), Vgpr(3)); + AppendVop3(&code, 0x34b, 32, Vgpr(3), Vgpr(4), Vgpr(3), 0, 0x8); + code.push_back(EncodeVop1(0x52, 33, 249)); + code.push_back(EncodeVop1Sdwa(1, 4, 2, 4, 0, 1, 1, 0, 1)); + AppendVop3(&code, 0x1d3, 34, Vgpr(1), 0, 0, 1, 0, true, 0, 1); + code.push_back(EncodeVop1(0x07, 35, 249)); + code.push_back(EncodeVop1Sdwa(0, 6, 0, 6, 0, 1, 1, 0, 1)); + AppendVop3(&code, 0x188, 36, Vgpr(0), 0, 0, 1, 0, true, 0, 1); + code.push_back(EncodeVop1(0x54, 37, 249)); + code.push_back(EncodeVop1Sdwa(4, 4, 2, 4, 0, 1, 1)); + + for (u32 i = 0; i < static_cast(std::size(sentinels)); i++) { + AppendStoreVgpr(&code, output_vgpr(i), i); + } + AppendEnd(&code); + + return {"NativeAndSdwa16BitDestinationWrites", + code, + {}, + {0x3c003e00u, 0xbbbb3e00u, 0xcccc3e00u, 0xdddd0003u, + 0xeeeefffeu, 0x00003e00u, 0xf00d3c00u, 0x00000003u, + 0x00000003u, 0xfffffffeu, 0xaaaa4200u, 0xbbbb4200u, + 0x00004200u, 0xabcd0000u, 0xbcde0000u, 0xcdefbe00u, + 0xdef04200u, 0xef013c00u, 0xf0124200u, 0x01233c00u, + 0xaaaa4200u, 0x42005678u, 0x24680000u, 0x1357fffdu, + 0x00000000u, 0xffffffffu, 0x0badb800u}, + {O::V_MOV_B32, O::V_CVT_F16_F32, O::V_CVT_U32_F32, + O::V_CVT_I32_F32, O::V_CVT_U16_F16, O::V_CVT_I16_F16, + O::V_RCP_F16, O::V_ADD_F16, O::V_FMA_F16, O::BUFFER_STORE_DWORD, + O::S_ENDPGM}}; +} + TestCase VectorMinMaxMed3F16Ops() { using O = ShaderOpcode; @@ -20852,9 +20999,11 @@ std::vector MakeCases() { AddCase(Vop3pOpselHiUsesArchitecturalSourceBits); AddCase(CvtPkU8F32PacksSelectedByte); AddCase(CvtPkrtzF16F32SubnormalRoundsTowardZero); + AddCase(CvtPkrtzF16F32SdwaAndOutputModifiers); AddCase(PackedMinMaxF16NanAndSignedZeroEdges); AddCase(VectorMinMaxF16Ops); AddCase(VectorCvtU16F16Sdwa); + AddCase(NativeAndSdwa16BitDestinationWrites); AddCase(VectorMinMaxMed3F16Ops); AddCase(VectorSpecialF16Ops); AddCase(VectorCosF16CapturedSdwaAndEdges); diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 9a7d8f07e..e720f0c59 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -3169,16 +3169,16 @@ void TestNewShaderRecompilerStagedShaderOps() { "new decoder did not decode RDNA2 S_SUBB_U32"); Check(Common::ContainsStr(result.decoded_dump, "s_bitset0_b32 s3, s1"), "new decoder did not decode RDNA2 S_BITSET0_B32"); - Check(Common::ContainsStr(result.decoded_dump, "v_fmac_f16 v70.sdwa(sel=4"), + Check(Common::ContainsStr(result.decoded_dump, "v_fmac_f16 v70, v5, v6"), "new decoder did not decode RDNA2 V_FMAC_F16"); Check( Common::ContainsStr( result.decoded_dump, - "v_fmamk_f16 v71.sdwa(sel=4,sext=0), v7, 0x3c003c00, v8"), + "v_fmamk_f16 v71, v7, 0x3c003c00, v8"), "new decoder did not consume V_FMAMK_F16 literal as the multiply source"); Check(Common::ContainsStr( result.decoded_dump, - "v_fmaak_f16 v72.sdwa(sel=4,sext=0), v9, v10, 0x40004000"), + "v_fmaak_f16 v72, v9, v10, 0x40004000"), "new decoder did not consume V_FMAAK_F16 literal as the add source"); Check(Common::ContainsStr(result.ir_dump, "ScalarSubBorrowCarryU32 s2, s0, s1, scc"), @@ -3189,15 +3189,15 @@ void TestNewShaderRecompilerStagedShaderOps() { Check( Common::ContainsStr( result.ir_dump, - "FmaF16 v70.sdwa(sel=4,sext=0), v5, v6, v70.sdwa(sel=4,sext=0)"), + "FmaF16 v70, v5, v6, v70"), "V_FMAC_F16 did not lower using the destination as the FMA accumulator"); Check( Common::ContainsStr(result.ir_dump, - "FmaF16 v71.sdwa(sel=4,sext=0), v7, 0x3c003c00, v8"), + "FmaF16 v71, v7, 0x3c003c00, v8"), "V_FMAMK_F16 did not lower with the literal in source 1"); Check( Common::ContainsStr(result.ir_dump, - "FmaF16 v72.sdwa(sel=4,sext=0), v9, v10, 0x40004000"), + "FmaF16 v72, v9, v10, 0x40004000"), "V_FMAAK_F16 did not lower with the literal in source 2"); Check(SpirvContainsOpcode(result.spirv, 130), "SPIR-V binary does not contain OpISub for S_SUBB_U32");