diff --git a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp index bcb4ebbe5..d637f8cee 100644 --- a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp @@ -47,6 +47,7 @@ constexpr MemoryOpcodeInfo MUBUF_OPCODE_LIST[] = { {0x1du, Opcode::BUFFER_STORE_DWORDX2, 2, 32}, {0x1eu, Opcode::BUFFER_STORE_DWORDX4, 4, 32}, {0x1fu, Opcode::BUFFER_STORE_DWORDX3, 3, 32}, + {0x24u, Opcode::BUFFER_LOAD_SHORT_D16, 1, 16}, {0x30u, Opcode::BUFFER_ATOMIC_SWAP, 1, 32}, {0x31u, Opcode::BUFFER_ATOMIC_CMPSWAP, 1, 32}, {0x32u, Opcode::BUFFER_ATOMIC_ADD, 1, 32}, @@ -298,7 +299,8 @@ void DecodeMubuf(uint32_t pc, std::span code, uint32_t word_inde } DecodeVectorGpr(vdata, inst.dst); - if (inst.opcode == Opcode::BUFFER_LOAD_FORMAT_D16_X) { + if (inst.opcode == Opcode::BUFFER_LOAD_FORMAT_D16_X || + inst.opcode == Opcode::BUFFER_LOAD_SHORT_D16) { inst.dst.sdwa_sel = 4u; } DecodeVectorGpr(vaddr, inst.src0); diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp index 85ba3d495..2097dbf8d 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp @@ -627,6 +627,7 @@ std::string InstructionToString(const Instruction& inst) { case Opcode::BUFFER_STORE_FORMAT_D16_X: case Opcode::BUFFER_LOAD_UBYTE: case Opcode::BUFFER_LOAD_USHORT: + case Opcode::BUFFER_LOAD_SHORT_D16: case Opcode::BUFFER_LOAD_DWORD: case Opcode::BUFFER_LOAD_DWORDX2: case Opcode::BUFFER_LOAD_DWORDX3: diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index d39091708..7dfb6c83c 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -518,6 +518,7 @@ enum class Opcode { BUFFER_LOAD_SBYTE, BUFFER_LOAD_USHORT, BUFFER_LOAD_SSHORT, + BUFFER_LOAD_SHORT_D16, BUFFER_LOAD_DWORD, BUFFER_LOAD_DWORDX2, BUFFER_LOAD_DWORDX3, diff --git a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp index 02fbcd32e..330beeb89 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp @@ -973,6 +973,7 @@ void Translator::EmitMemory(const Decoder::Instruction& inst) { case Decoder::Opcode::BUFFER_LOAD_SBYTE: case Decoder::Opcode::BUFFER_LOAD_USHORT: case Decoder::Opcode::BUFFER_LOAD_SSHORT: + case Decoder::Opcode::BUFFER_LOAD_SHORT_D16: case Decoder::Opcode::BUFFER_LOAD_DWORD: case Decoder::Opcode::BUFFER_LOAD_DWORDX2: case Decoder::Opcode::BUFFER_LOAD_DWORDX3: diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index ab01f3f75..9f9942f82 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -19134,6 +19134,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode, case Opcode::BUFFER_LOAD_SBYTE: case Opcode::BUFFER_LOAD_USHORT: case Opcode::BUFFER_LOAD_SSHORT: + case Opcode::BUFFER_LOAD_SHORT_D16: case Opcode::BUFFER_LOAD_DWORDX2: case Opcode::BUFFER_LOAD_DWORDX3: case Opcode::BUFFER_LOAD_DWORDX4: @@ -28650,6 +28651,44 @@ TestCase BufferLoadVariants() { O::BUFFER_STORE_DWORD, O::S_ENDPGM}}; } +TestCase BufferLoadShortD16Captured() { + TestCase test; + test.name = "BufferLoadShortD16Captured"; + auto& code = test.code; + AppendVMovU32(&code, 15, 1); + AppendVMovLiteral(&code, 1, 0xabcd1234u); + // Load an indexed raw halfword at record byte offset 14. + code.insert(code.end(), {0xe090200eu, 0x8000010fu}); + AppendStoreVgpr(&code, 1, 0); + AppendVMovLiteral(&code, 1, 0xdeaf4321u); + code.push_back(EncodeSop1(0x04, 8, 126)); + code.push_back(EncodeSop1(0x04, 126, InlineU32(0))); + code.insert(code.end(), {0xe090200eu, 0x8000010fu}); + code.push_back(EncodeSop1(0x04, 126, 8)); + AppendStoreVgpr(&code, 1, 1); + AppendVMovU32(&code, 15, 2); + AppendVMovLiteral(&code, 1, 0xbeef5678u); + code.insert(code.end(), {0xe090200eu, 0x8000010fu}); + AppendStoreVgpr(&code, 1, 2); + AppendEnd(&code); + test.initial.assign(20, 0xa5a5a5a5u); + test.initial[11] = 0x8001cafeu; // Record 1 byte 14 is the high half at byte 46. + test.expected = test.initial; + test.expected[0] = 0xabcd8001u; + test.expected[1] = 0xdeaf4321u; + test.expected[2] = 0xbeef0000u; + test.user_data = MakeStructuredStorageBufferData(32, 2); + test.user_data[50] = static_cast(test.initial.size() * sizeof(u32)); + test.user_data[51] = 3u << 28u; + test.has_user_data = true; + test.storage_buffer_range_bytes = 64; + test.opcodes = {ShaderOpcode::V_MOV_B32, ShaderOpcode::S_MOV_B64, + ShaderOpcode::BUFFER_LOAD_SHORT_D16, ShaderOpcode::BUFFER_STORE_DWORD, + ShaderOpcode::S_ENDPGM}; + test.decoded_counts = {{"BUFFER_LOAD_SHORT_D16 v1.sdwa(sel=4,sext=0)", 3}}; + return test; +} + TestCase BufferSubwordLoadsAtHostOffset(u32 bytes, u32 component, bool sign) { using O = ShaderOpcode; TestCase test; @@ -36655,6 +36694,7 @@ std::vector MakeCases() { cases.push_back(BufferOffsetsUsePackedWords(false)); cases.push_back(BufferOffsetsUsePackedWords(true)); AddCase(BufferLoadVariants); + AddCase(BufferLoadShortD16Captured); for (const u32 component : {4u, 2u, 0u}) { cases.push_back(BufferSubwordLoadsAtHostOffset(2, component, false)); } @@ -42286,6 +42326,7 @@ int main(int argc, char **argv) { } if (argc == 2 && std::strcmp(argv[1], "--flat-d16-only") == 0) { VulkanHarness vulkan; + RunCase(&vulkan, BufferLoadShortD16Captured()); RunCase(&vulkan, FlatStoreSlcCaptured()); RunCase(&vulkan, FlatLoadDlcCaptured(32)); RunCase(&vulkan, FlatLoadDlcCaptured(64));