diff --git a/CMakeLists.txt b/CMakeLists.txt index b3366e2b1..46f679a9a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -696,6 +696,8 @@ if(BUILD_TESTING) COMMAND $ --vop3-conversions-only) add_test(NAME shader_recompiler_quadmask COMMAND $ --quadmask-only) + add_test(NAME shader_recompiler_integer16_ternary + COMMAND $ --integer16-ternary-only) add_test(NAME shader_recompiler_alignbyte COMMAND $ --alignbyte-only) add_test(NAME graphics_pipeline_rasterization diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index e4450c093..dc0901abb 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -278,16 +278,20 @@ enum class Opcode { V_MIN3_I32, V_MIN3_U32, V_MIN3_F16, + V_MIN3_I16, V_MIN3_U16, V_MAX3_F32, V_MAX3_I32, V_MAX3_U32, V_MAX3_F16, + V_MAX3_I16, + V_MAX3_U16, V_MED3_F32, V_MED3_I32, V_MED3_U32, V_MED3_F16, V_MED3_I16, + V_MED3_U16, V_SAD_U32, V_ADD3_U32, V_LSHL_ADD_U32, diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index 6a9865242..0d39cb2df 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -339,16 +339,20 @@ constexpr OpcodeMap VOP3_OPCODE_LIST[] = { {0x152u, Opcode::V_MIN3_I32}, {0x153u, Opcode::V_MIN3_U32}, {0x351u, Opcode::V_MIN3_F16}, + {0x352u, Opcode::V_MIN3_I16}, {0x353u, Opcode::V_MIN3_U16}, {0x154u, Opcode::V_MAX3_F32}, {0x155u, Opcode::V_MAX3_I32}, {0x156u, Opcode::V_MAX3_U32}, {0x354u, Opcode::V_MAX3_F16}, + {0x355u, Opcode::V_MAX3_I16}, + {0x356u, Opcode::V_MAX3_U16}, {0x157u, Opcode::V_MED3_F32}, {0x158u, Opcode::V_MED3_I32}, {0x159u, Opcode::V_MED3_U32}, {0x357u, Opcode::V_MED3_F16}, {0x358u, Opcode::V_MED3_I16}, + {0x359u, Opcode::V_MED3_U16}, {0x15du, Opcode::V_SAD_U32}, {0x15eu, Opcode::V_CVT_PK_U8_F32}, {0x178u, Opcode::V_XOR3_B32}, @@ -494,7 +498,9 @@ bool IsNativeVop3F16TernaryOpcode(Opcode opcode) { } bool IsNativeVop3B16TernaryOpcode(Opcode opcode) { - return opcode == Opcode::V_MIN3_U16 || opcode == Opcode::V_MED3_I16 || + return opcode == Opcode::V_MIN3_I16 || opcode == Opcode::V_MIN3_U16 || + opcode == Opcode::V_MAX3_I16 || opcode == Opcode::V_MAX3_U16 || + opcode == Opcode::V_MED3_I16 || opcode == Opcode::V_MED3_U16 || opcode == Opcode::V_MAD_I16; } @@ -1530,7 +1536,9 @@ bool SupportsNativeVop3ResultModifiers(Opcode opcode) { bool SupportsNativeVop3Clamp(Opcode opcode) { return SupportsNativeVop3ResultModifiers(opcode) || UsesInexactClampControl(opcode) || - opcode == Opcode::V_MAD_I16; + opcode == Opcode::V_MAD_I16 || opcode == Opcode::V_MIN3_I16 || + opcode == Opcode::V_MAX3_I16 || opcode == Opcode::V_MAX3_U16 || + opcode == Opcode::V_MED3_U16; } bool HasUnsupportedNativeVop3Modifiers(Opcode opcode, bool permlane, bool mad_mix, diff --git a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp index 4490b2324..6024e1e48 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp @@ -378,6 +378,10 @@ void Translator::EmitVector(const Decoder::Instruction& inst) { case O::V_MAD_I16: return V_MAD_I16(inst); case O::V_MED3_I16: return Integer16Ternary(inst, IR::ValueOpcode::SMedTri32, true); case O::V_MIN3_U16: return Integer16Ternary(inst, IR::ValueOpcode::UMinTri32, false); + case O::V_MIN3_I16: return Integer16Ternary(inst, IR::ValueOpcode::SMinTri32, true); + case O::V_MAX3_I16: return Integer16Ternary(inst, IR::ValueOpcode::SMaxTri32, true); + case O::V_MAX3_U16: return Integer16Ternary(inst, IR::ValueOpcode::UMaxTri32, false); + case O::V_MED3_U16: return Integer16Ternary(inst, IR::ValueOpcode::UMedTri32, false); case O::V_MIN_I16: return Integer16Binary(inst, IR::ValueOpcode::SMin32, true); case O::V_MAX_I16: return Integer16Binary(inst, IR::ValueOpcode::SMax32, true); case O::V_MIN_U16: return Integer16Binary(inst, IR::ValueOpcode::UMin32, false); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 33868d5b9..a1862b87a 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -26810,6 +26810,71 @@ TestCase Vop3MadI16CapturedSelectorsAndSaturation() { return test; } +TestCase Vop3Integer16Ternary(u32 wave_size) { + using O = ShaderOpcode; + namespace D = ShaderRecompiler::Decoder; + struct Operation { u32 encoding; O opcode; bool sign; u32 sorted_index; }; + constexpr std::array operations{ + Operation{0x352, O::V_MIN3_I16, true, 0}, + Operation{0x355, O::V_MAX3_I16, true, 2}, + Operation{0x356, O::V_MAX3_U16, false, 2}, + Operation{0x359, O::V_MED3_U16, false, 1}}; + constexpr std::array, 4> inputs{{ + {0x80007fffu, 0xffff0000u, 0x0001ffffu}, + {0x00098000u, 0xffff0007u, 0x7fff0005u}, + {0xffff8000u, 0xffff8000u, 0x00008000u}, + {0x00000000u, 0x7fff7fffu, 0x7fff7fffu}}}; + TestCase test; + test.name = wave_size == 32 ? "Vop3Integer16TernaryWave32" : "Vop3Integer16TernaryWave64"; + for (const auto &input : inputs) test.initial.insert(test.initial.end(), input.begin(), input.end()); + test.expected = test.initial; + auto &code = test.code; + for (const auto &operation : operations) { + // Integer ternaries accept half selectors, but no floating-point modifiers. + for (u32 modifier = 0; modifier < 9; ++modifier) { + const u32 words[] = { + EncodeVop3Word0(operation.encoding, 9, modifier < 3 ? 1u << modifier : 0), + EncodeVop3Word1(Vgpr(2), Vgpr(3), Vgpr(4), + modifier >= 6 ? modifier - 5 : 0, + modifier >= 3 && modifier < 6 ? 1u << (modifier - 3) : 0)}; + D::Instruction decoded; + D::DecodeInstruction(words, 0, decoded); + Require(test.name, "integer modifiers", decoded.opcode == O::UNSUPPORTED, + "16-bit integer ternary accepted a floating-point modifier"); + } + for (u32 i = 0; i < inputs.size(); ++i) { + for (u32 variant = 0; variant < 32; ++variant) { + const u32 selectors = variant & 15u; + std::array selected{}; + for (u32 operand = 0; operand < 3; ++operand) { + AppendVMovU32(&code, 30, (i * 3 + operand) * sizeof(u32)); + AppendBufferLoadDword(&code, 2 + operand, 30); + const u32 half = (inputs[i][operand] >> (((selectors >> operand) & 1u) * 16)) & 0xffffu; + selected[operand] = operation.sign && half >= 0x8000u + ? static_cast(half) - 0x10000 : static_cast(half); + } + code.push_back(EncodeVop1(0x02, 12, Vgpr(3))); // Broadcast source 1 from an SGPR. + const u32 dst = selectors & 1u ? 2u : 9u; // Also cover source/destination aliasing. + AppendVMovLiteral(&code, 9, 0xa5a5beefu); + AppendVop3(&code, operation.encoding, dst, Vgpr(2), 12, Vgpr(4), 0, selectors, variant >= 16); + AppendStoreVgpr(&code, dst, static_cast(test.expected.size())); + std::sort(selected.begin(), selected.end()); + const u32 result = static_cast(selected[operation.sorted_index]) & 0xffffu; + const u32 old = dst == 2 ? inputs[i][0] : 0xa5a5beefu; + test.expected.push_back(selectors & 8u ? (old & 0xffffu) | (result << 16) + : (old & 0xffff0000u) | result); + } + } + test.opcodes.push_back(operation.opcode); + } + AppendEnd(&code); + test.opcodes.insert(test.opcodes.end(), {O::V_MOV_B32, O::V_READFIRSTLANE_B32, + O::BUFFER_LOAD_DWORD, O::BUFFER_STORE_DWORD, O::S_ENDPGM}); + test.compute_info.wave_size = wave_size; + test.has_compute_info = true; + return test; +} + TestCase Vop3Min3U16CapturedAndSelectors() { using O = ShaderOpcode; struct MinCase { @@ -46174,6 +46239,8 @@ std::vector MakeCases() { AddCase(Vop3MulLoU16CapturedAndSelectors); AddCase(Vop3MadI16CapturedSelectorsAndSaturation); AddCase(Vop3F32ClampFlushesNaN); + cases.push_back(Vop3Integer16Ternary(32)); + cases.push_back(Vop3Integer16Ternary(64)); AddCase(Vop3Min3U16CapturedAndSelectors); AddCase(Vop3Med3I16Captured); AddCase(Vop2SdwaMinU32PreservesWordDestination); @@ -52604,6 +52671,14 @@ int main(int argc, char **argv) { std::setvbuf(stdout, nullptr, _IONBF, 0); EnsureConfigInitialized(); CheckLeastRecentlyUsedCacheOrdering(); + if (argc == 2 && std::strcmp(argv[1], "--integer16-ternary-only") == 0) { + VulkanHarness vulkan; + RunCase(&vulkan, Vop3Integer16Ternary(32)); + RunCase(&vulkan, Vop3Integer16Ternary(64)); + RunCase(&vulkan, Vop3Min3U16CapturedAndSelectors()); + RunCase(&vulkan, Vop3Med3I16Captured()); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--quadmask-only") == 0) { VulkanHarness vulkan; RunCase(&vulkan, ScalarQuadmaskB32(32));