From 44302cbf961d217318ddff6f2944eb7955447af8 Mon Sep 17 00:00:00 2001 From: nmzik Date: Sun, 16 Aug 2026 23:22:28 +0200 Subject: [PATCH] shader: implement RDNA2 V_MED3_I16 --- .../frontend/decode/ShaderDecoder.cpp | 1 + .../frontend/decode/ShaderDecoder.h | 1 + .../frontend/decode/VectorAluOps.cpp | 65 ++++++++++++------- .../recompiler/frontend/translate/Integer.cpp | 5 ++ .../recompiler/ir/opcodes/ShaderIROpcodes.cpp | 1 + .../recompiler/ir/opcodes/ShaderIROpcodes.inc | 1 + tests/ShaderRecompilerComputeTests.cpp | 33 ++++++++++ 7 files changed, 84 insertions(+), 23 deletions(-) diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp index e0cb60a19..6792e1415 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp @@ -647,6 +647,7 @@ std::string OpcodeToString(Opcode opcode) { case Opcode::VMed3I32: return "v_med3_i32"; case Opcode::VMed3U32: return "v_med3_u32"; case Opcode::VMed3F16: return "v_med3_f16"; + case Opcode::VMed3I16: return "v_med3_i16"; case Opcode::VSadU32: return "v_sad_u32"; case Opcode::VAdd3U32: return "v_add3_u32"; case Opcode::VLshlAddU32: return "v_lshl_add_u32"; diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 3ff8dbe90..64edb13f9 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -232,6 +232,7 @@ enum class Opcode { VMed3I32, VMed3U32, VMed3F16, + VMed3I16, VSadU32, VAdd3U32, VLshlAddU32, diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index 5aac76b9c..826cf6338 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -254,29 +254,30 @@ constexpr OpcodeMap VOP3_OPCODE_LIST[] = { {0x155u, Opcode::VMax3I32}, {0x156u, Opcode::VMax3U32}, {0x354u, Opcode::VMax3F16}, {0x157u, Opcode::VMed3F32}, {0x158u, Opcode::VMed3I32}, {0x159u, Opcode::VMed3U32}, - {0x357u, Opcode::VMed3F16}, {0x15du, Opcode::VSadU32}, - {0x15eu, Opcode::VCvtPkU8F32}, {0x178u, Opcode::VXor3B32}, - {0x12fu, Opcode::VCvtPkrtzF16F32}, {0x169u, Opcode::VMulLoU32}, - {0x16au, Opcode::VMulHiU32}, {0x16bu, Opcode::VMulLoI32}, - {0x16cu, Opcode::VMulHiI32}, {0x303u, Opcode::VAddNcU16}, - {0x304u, Opcode::VSubNcU16}, {0x307u, Opcode::VLshrrevB16}, - {0x308u, Opcode::VAshrrevI16}, {0x309u, Opcode::VMaxU16}, - {0x30au, Opcode::VMaxI16}, {0x30bu, Opcode::VMinU16}, - {0x30cu, Opcode::VMinI16}, {0x30du, Opcode::VAddNcI16}, - {0x30eu, Opcode::VSubNcI16}, {0x30fu, Opcode::VAddI32}, - {0x310u, Opcode::VSubI32}, {0x311u, Opcode::VPackB32F16}, - {0x314u, Opcode::VLshlrevB16}, {0x319u, Opcode::VSubrevI32}, - {0x345u, Opcode::VXadU32}, {0x346u, Opcode::VLshlAddU32}, - {0x347u, Opcode::VAddLshlU32}, {0x360u, Opcode::VReadlaneB32}, - {0x361u, Opcode::VWritelaneB32}, {0x362u, Opcode::VLdexpF32}, - {0x363u, Opcode::VBfmB32}, {0x364u, Opcode::VBcntU32B32}, - {0x365u, Opcode::VMbcntLoU32B32}, {0x366u, Opcode::VMbcntHiU32B32}, - {0x368u, Opcode::VCvtPknormI16F32}, {0x369u, Opcode::VCvtPknormU16F32}, - {0x36au, Opcode::VCvtPkU16U32}, {0x36bu, Opcode::VCvtPkI16I32}, - {0x36fu, Opcode::VLshlOrB32}, {0x371u, Opcode::VAndOrB32}, - {0x372u, Opcode::VOr3B32}, {0x377u, Opcode::VPermlane16B32}, - {0x378u, Opcode::VPermlanex16B32}, {0x34bu, Opcode::VFmaF16}, - {0x36du, Opcode::VAdd3U32}, {0x14fu, Opcode::VAlignbyteB32}, + {0x357u, Opcode::VMed3F16}, {0x358u, Opcode::VMed3I16}, + {0x15du, Opcode::VSadU32}, {0x15eu, Opcode::VCvtPkU8F32}, + {0x178u, Opcode::VXor3B32}, {0x12fu, Opcode::VCvtPkrtzF16F32}, + {0x169u, Opcode::VMulLoU32}, {0x16au, Opcode::VMulHiU32}, + {0x16bu, Opcode::VMulLoI32}, {0x16cu, Opcode::VMulHiI32}, + {0x303u, Opcode::VAddNcU16}, {0x304u, Opcode::VSubNcU16}, + {0x307u, Opcode::VLshrrevB16}, {0x308u, Opcode::VAshrrevI16}, + {0x309u, Opcode::VMaxU16}, {0x30au, Opcode::VMaxI16}, + {0x30bu, Opcode::VMinU16}, {0x30cu, Opcode::VMinI16}, + {0x30du, Opcode::VAddNcI16}, {0x30eu, Opcode::VSubNcI16}, + {0x30fu, Opcode::VAddI32}, {0x310u, Opcode::VSubI32}, + {0x311u, Opcode::VPackB32F16}, {0x314u, Opcode::VLshlrevB16}, + {0x319u, Opcode::VSubrevI32}, {0x345u, Opcode::VXadU32}, + {0x346u, Opcode::VLshlAddU32}, {0x347u, Opcode::VAddLshlU32}, + {0x360u, Opcode::VReadlaneB32}, {0x361u, Opcode::VWritelaneB32}, + {0x362u, Opcode::VLdexpF32}, {0x363u, Opcode::VBfmB32}, + {0x364u, Opcode::VBcntU32B32}, {0x365u, Opcode::VMbcntLoU32B32}, + {0x366u, Opcode::VMbcntHiU32B32}, {0x368u, Opcode::VCvtPknormI16F32}, + {0x369u, Opcode::VCvtPknormU16F32}, {0x36au, Opcode::VCvtPkU16U32}, + {0x36bu, Opcode::VCvtPkI16I32}, {0x36fu, Opcode::VLshlOrB32}, + {0x371u, Opcode::VAndOrB32}, {0x372u, Opcode::VOr3B32}, + {0x377u, Opcode::VPermlane16B32}, {0x378u, Opcode::VPermlanex16B32}, + {0x34bu, Opcode::VFmaF16}, {0x36du, Opcode::VAdd3U32}, + {0x14fu, Opcode::VAlignbyteB32}, }; constexpr auto VOP3_OPS = Detail::MakeOpcodeTable<0x400>(VOP3_OPCODE_LIST); @@ -371,6 +372,10 @@ bool IsNativeVop3F16TernaryOpcode(Opcode opcode) { opcode == Opcode::VFmaF16; } +bool IsNativeVop3I16TernaryOpcode(Opcode opcode) { + return opcode == Opcode::VMed3I16; +} + bool IsNativeVop3B16BinaryOpcode(Opcode opcode) { switch (opcode) { case Opcode::VAddNcU16: @@ -1283,6 +1288,14 @@ void ApplyNativeVop3TernaryModifiers(Instruction& inst, uint32_t op_sel, uint32_ inst.dst.sdwa_sel = ((op_sel & 0x8u) != 0) ? 5u : 4u; } +void ApplyNativeVop3I16TernarySelectors(Instruction& inst, uint32_t op_sel) { + Operand* sources[] = {&inst.src0, &inst.src1, &inst.src2}; + for (uint32_t i = 0; i < 3u; i++) { + sources[i]->op_sel = ((op_sel >> i) & 1u) != 0; + } + inst.dst.sdwa_sel = (op_sel & 0x8u) != 0 ? 5u : 4u; +} + void ApplyNativeVop3B16BinaryModifiers(Instruction& inst, uint32_t op_sel) { inst.src0.op_sel = (op_sel & 0x1u) != 0; inst.src1.op_sel = (op_sel & 0x2u) != 0; @@ -1375,6 +1388,9 @@ bool HasUnsupportedNativeVop3Modifiers(Opcode opcode, bool permlane, bool mad_mi if (IsNativeVop3F16TernaryOpcode(opcode)) { return opcode != Opcode::VFmaF16 && (clamp != 0u || omod != 0u); } + if (IsNativeVop3I16TernaryOpcode(opcode)) { + return abs != 0u || clamp != 0u || omod != 0u || neg != 0u; + } if (IsNativeVop3B16BinaryOpcode(opcode)) { return abs != 0u || clamp != 0u || omod != 0u || neg != 0u; } @@ -1595,6 +1611,7 @@ bool DecodeVop3(uint32_t pc, std::span code, uint32_t word_index const bool vop3b_uses_sdst = carry_in_out || vop3b_carry_out || vop3b_mad_u64; const bool mad_mix = false; const bool f16_ternary = IsNativeVop3F16TernaryOpcode(inst.opcode); + const bool i16_ternary = IsNativeVop3I16TernaryOpcode(inst.opcode); const bool b16_binary = IsNativeVop3B16BinaryOpcode(inst.opcode); const bool pack_b32_f16 = inst.opcode == Opcode::VPackB32F16; const bool permlane = IsPermlaneOpcode(inst.opcode); @@ -1721,6 +1738,8 @@ bool DecodeVop3(uint32_t pc, std::span code, uint32_t word_index ApplyNativeVop3TernaryModifiers(inst, op_sel, abs, neg); } else if (f16_ternary) { ApplyNativeVop3TernaryModifiers(inst, op_sel, abs, neg); + } else if (i16_ternary) { + ApplyNativeVop3I16TernarySelectors(inst, op_sel); } else if (b16_binary) { ApplyNativeVop3B16BinaryModifiers(inst, op_sel); } else if (pack_b32_f16) { diff --git a/src/graphics/shader/recompiler/frontend/translate/Integer.cpp b/src/graphics/shader/recompiler/frontend/translate/Integer.cpp index 6c3f18a2d..8f84792d0 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Integer.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Integer.cpp @@ -32,6 +32,11 @@ bool Translator::TranslateInteger16Operation(const IR::Instruction& inst) { case IR::Opcode::ISubI16: result = ir.ISub(ReadU16AsU32(inst.src[0], false), ReadU16AsU32(inst.src[1], false)); break; + case IR::Opcode::IMed3I16: + result = ir.Emit(IR::ValueOpcode::SMedTri32, + {ReadU16AsU32(inst.src[0], true), ReadU16AsU32(inst.src[1], true), + ReadU16AsU32(inst.src[2], true)}); + break; case IR::Opcode::IMinI16: case IR::Opcode::IMaxI16: case IR::Opcode::UMinU16: diff --git a/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.cpp b/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.cpp index cfe2ae777..1bc913513 100644 --- a/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.cpp +++ b/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.cpp @@ -166,6 +166,7 @@ constexpr LowerMap LOWER_OPS[] = { {Decoder::Opcode::VMed3I32, Opcode::IMed3I32}, {Decoder::Opcode::VMed3U32, Opcode::UMed3U32}, {Decoder::Opcode::VMed3F16, Opcode::Med3F16}, + {Decoder::Opcode::VMed3I16, Opcode::IMed3I16}, {Decoder::Opcode::VSadU32, Opcode::SadU32}, {Decoder::Opcode::VAdd3U32, Opcode::IAdd3U32}, {Decoder::Opcode::VLshlAddU32, Opcode::ShiftLeftAddU32}, diff --git a/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.inc b/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.inc index 83f80ad1d..f4cc378e2 100644 --- a/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.inc +++ b/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.inc @@ -251,6 +251,7 @@ IR_OPCODE(IAddU16, General, Integer16) IR_OPCODE(ISubI16, General, Integer16) IR_OPCODE(IMinI16, General, Integer16) IR_OPCODE(IMaxI16, General, Integer16) +IR_OPCODE(IMed3I16, General, Integer16) IR_OPCODE(UMinU16, General, Integer16) IR_OPCODE(UMaxU16, General, Integer16) IR_OPCODE(RcpF32, General, Float) diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 9e755b729..d84bb7895 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -11995,6 +11995,33 @@ TestCase Vop3CvtPkI16I32Captured() { return test; } +TestCase Vop3Med3I16Captured() { + using O = ShaderOpcode; + + std::vector code; + AppendVMovLiteral(&code, 5, 0x8ad003e8u); // high=-30000, low=1000 + AppendSMovLiteral(&code, 25, 0xb1e04e20u); // high=-20000, low=20000 + AppendSMovLiteral(&code, 27, 0x7530fc18u); // high=30000, low=-1000 + AppendVMovLiteral(&code, 3, 0x1234cafeu); + code.push_back(0xd7584803u); + code.push_back(0x006c3305u); // v_med3_i16 v3.hi, v5.hi, s25.lo, s27.lo + AppendStoreVgpr(&code, 3, 0); + AppendEnd(&code); + + TestCase test; + test.name = "Vop3Med3I16Captured"; + test.code = std::move(code); + test.expected = {0xfc18cafeu}; + test.opcodes = {O::VMovB32, O::SMovB32, O::VMed3I16, O::BufferStoreDword, + O::SEndpgm}; + test.decoded_counts = {{"0x00000020: v_med3_i16 v3.sdwa(sel=5,sext=0), " + "v5.opsel(lo=1,hi=0,neghi=0), s25, s27\n", + 1}}; + test.native_ir_counts = {{"IMed3I16", 1}}; + test.required_spirv = {"OpSLessThan"}; + return test; +} + TestCase Vop2SdwaMinU32PreservesWordDestination() { using O = ShaderOpcode; @@ -17757,6 +17784,7 @@ std::vector MakeCases() { AddCase(Vop2SdwaAshrrevCapturedWord0SignExtends); AddCase(Vop2SdwaSubNcPreservesByteAndWordDestinations); AddCase(Vop3CvtPkI16I32Captured); + AddCase(Vop3Med3I16Captured); AddCase(Vop2SdwaMinU32PreservesWordDestination); AddCase(VectorShiftCountsMaskLowBits); AddCase(VectorVop3IntegerOps); @@ -21240,6 +21268,11 @@ int main(int argc, char **argv) { RunCase(&vulkan, Vop3CvtPkI16I32Captured()); return 0; } + if (argc == 2 && std::strcmp(argv[1], "--med3-i16-only") == 0) { + VulkanHarness vulkan; + RunCase(&vulkan, Vop3Med3I16Captured()); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--waitcnt-depctr-only") == 0) { VulkanHarness vulkan; RunCase(&vulkan, ScalarWaitcntDepctrCapturedVmVsrc());