diff --git a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp index 4cb15443d..104c24c91 100644 --- a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp @@ -42,6 +42,7 @@ constexpr MemoryOpcodeInfo MUBUF_OPCODE_LIST[] = { {0x0eu, Opcode::BUFFER_LOAD_DWORDX4, 4, 32}, {0x0fu, Opcode::BUFFER_LOAD_DWORDX3, 3, 32}, {0x18u, Opcode::BUFFER_STORE_BYTE, 1, 8}, + {0x19u, Opcode::BUFFER_STORE_BYTE_D16_HI, 1, 8}, {0x1au, Opcode::BUFFER_STORE_SHORT, 1, 16}, {0x1cu, Opcode::BUFFER_STORE_DWORD, 1, 32}, {0x1du, Opcode::BUFFER_STORE_DWORDX2, 2, 32}, @@ -312,6 +313,8 @@ void DecodeMubuf(uint32_t pc, std::span code, uint32_t word_inde inst.opcode == Opcode::BUFFER_LOAD_SHORT_D16 || inst.opcode == Opcode::BUFFER_LOAD_SHORT_D16_HI) { inst.dst.sdwa_sel = inst.opcode == Opcode::BUFFER_LOAD_SHORT_D16_HI ? 5u : 4u; + } else if (inst.opcode == Opcode::BUFFER_STORE_BYTE_D16_HI) { + inst.dst.sdwa_sel = 2u; } DecodeVectorGpr(vaddr, inst.src0); DecodeScalarSource(srsrc * 4u, pc, inst.src1); diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp index 060eb9490..da6390a41 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp @@ -646,6 +646,7 @@ std::string InstructionToString(const Instruction& inst) { case Opcode::BUFFER_LOAD_DWORDX3: case Opcode::BUFFER_LOAD_DWORDX4: case Opcode::BUFFER_STORE_BYTE: + case Opcode::BUFFER_STORE_BYTE_D16_HI: case Opcode::BUFFER_STORE_SHORT: case Opcode::BUFFER_STORE_DWORD: case Opcode::BUFFER_STORE_DWORDX2: diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 649d469a3..ffac3e163 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -564,6 +564,7 @@ enum class Opcode { BUFFER_LOAD_DWORDX3, BUFFER_LOAD_DWORDX4, BUFFER_STORE_BYTE, + BUFFER_STORE_BYTE_D16_HI, BUFFER_STORE_SHORT, BUFFER_STORE_DWORD, BUFFER_STORE_DWORDX2, diff --git a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp index bda91a11a..980ff870f 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp @@ -998,6 +998,7 @@ void Translator::EmitMemory(const Decoder::Instruction& inst) { case Decoder::Opcode::BUFFER_STORE_DWORDX3: case Decoder::Opcode::BUFFER_STORE_DWORDX4: case Decoder::Opcode::BUFFER_STORE_BYTE: + case Decoder::Opcode::BUFFER_STORE_BYTE_D16_HI: case Decoder::Opcode::BUFFER_STORE_SHORT: case Decoder::Opcode::BUFFER_STORE_FORMAT_X: case Decoder::Opcode::BUFFER_STORE_FORMAT_XY: diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index b8e1edac2..eaeb89d45 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -30527,6 +30527,42 @@ TestCase BufferStoreVariants() { O::BUFFER_STORE_DWORDX4, O::S_ENDPGM}}; } +TestCase BufferStoreByteD16Hi(bool masked) { + using O = ShaderOpcode; + TestCase test; + test.name = masked ? "BufferStoreByteD16HiMasked" : "BufferStoreByteD16HiAdjacentLanes"; + AppendVMovLiteral(&test.code, 4, 0xa5c39b72u); + test.code.push_back(EncodeVop2(0x25, 25, InlineU32(4), 0)); + if (masked) { + AppendSMovLiteral(&test.code, 126, 0x55555555u); + AppendSMovLiteral(&test.code, 127, 0xaaaaaaaau); + } + // Captured b21e373811f28e31 pc 0x1b8c: e0646000 80020419. + test.code.push_back(EncodeMubuf0(0x19u, 0, true, false, true)); + test.code.push_back(EncodeMubuf1(4, 2, 25)); + AppendEnd(&test.code); + test.initial = std::vector(20, 0x11223344u); + test.expected = test.initial; + for (u32 lane = 0; lane < 60; lane++) { + const u32 mask = !masked ? 0xffffffffu : lane < 32 ? 0x55555555u : 0xaaaaaaaau; + if (((mask >> (lane % 32)) & 1u) == 0) continue; + const u32 byte = lane + 4, shift = (byte % 4) * 8; + test.expected[byte / 4] = (test.expected[byte / 4] & ~(0xffu << shift)) | (0xc3u << shift); + } + const auto descriptor = MakeStructuredStorageBufferData(1, 64); + std::copy_n(descriptor.begin(), 4, test.user_data.begin() + 8); + test.has_user_data = true; + test.storage_buffer_range_bytes = 64; + test.compute_info.threads_num[0] = 64; + test.compute_info.threads_num[1] = test.compute_info.threads_num[2] = 1; + test.compute_info.thread_ids_num = 1; + test.compute_info.wave_size = 64; + test.has_compute_info = true; + test.opcodes = {O::V_MOV_B32, O::V_ADD_NC_U32, O::BUFFER_STORE_BYTE_D16_HI, O::S_ENDPGM}; + test.forbidden_spirv = {"OpAtomicLoad", "OpAtomicCompareExchange"}; + return test; +} + TestCase BufferFormatVariants() { using O = ShaderOpcode; @@ -39383,6 +39419,8 @@ std::vector MakeCases() { AddCase(BufferLoadFormatXyRejectsPartialRecord); AddCase(BufferStoreFormatXyRejectsPartialRecord); AddCase(BufferStoreVariants); + cases.push_back(BufferStoreByteD16Hi(false)); + cases.push_back(BufferStoreByteD16Hi(true)); AddCase(BufferFormatVariants); AddCase(BufferLoadFormatXyzwSnapshotsOverlappingAddress); AddCase(BufferLoadFormatXyzwResource32_32FloatAppliesXy01); @@ -45086,6 +45124,8 @@ int main(int argc, char **argv) { RunCase(&vulkan, BufferStoreFormatXRejectsPartialRecord()); RunCase(&vulkan, BufferStoreFormatXyRejectsPartialRecord()); RunCase(&vulkan, BufferFormatStoreVariants()); + RunCase(&vulkan, BufferStoreByteD16Hi(false)); + RunCase(&vulkan, BufferStoreByteD16Hi(true)); return 0; } if (argc == 2 && std::strcmp(argv[1], "--buffer-float16-store-only") == 0) {