diff --git a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp index 64c73b1d5..25d85c462 100644 --- a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp @@ -44,6 +44,7 @@ constexpr MemoryOpcodeInfo MUBUF_OPCODE_LIST[] = { {0x18u, Opcode::BUFFER_STORE_BYTE, 1, 8}, {0x19u, Opcode::BUFFER_STORE_BYTE_D16_HI, 1, 8}, {0x1au, Opcode::BUFFER_STORE_SHORT, 1, 16}, + {0x1bu, Opcode::BUFFER_STORE_SHORT_D16_HI, 1, 16}, {0x1cu, Opcode::BUFFER_STORE_DWORD, 1, 32}, {0x1du, Opcode::BUFFER_STORE_DWORDX2, 2, 32}, {0x1eu, Opcode::BUFFER_STORE_DWORDX4, 4, 32}, @@ -319,6 +320,8 @@ void DecodeMubuf(uint32_t pc, std::span code, uint32_t word_inde 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; + } else if (inst.opcode == Opcode::BUFFER_STORE_SHORT_D16_HI) { + inst.dst.sdwa_sel = 5u; } 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 bd01b6dde..fd0ec7d1f 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp @@ -650,6 +650,7 @@ std::string InstructionToString(const Instruction& inst) { case Opcode::BUFFER_STORE_BYTE: case Opcode::BUFFER_STORE_BYTE_D16_HI: case Opcode::BUFFER_STORE_SHORT: + case Opcode::BUFFER_STORE_SHORT_D16_HI: case Opcode::BUFFER_STORE_DWORD: case Opcode::BUFFER_STORE_DWORDX2: case Opcode::BUFFER_STORE_DWORDX3: diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index b3d679b4c..1630376f6 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -568,6 +568,7 @@ enum class Opcode { BUFFER_STORE_BYTE, BUFFER_STORE_BYTE_D16_HI, BUFFER_STORE_SHORT, + BUFFER_STORE_SHORT_D16_HI, BUFFER_STORE_DWORD, BUFFER_STORE_DWORDX2, BUFFER_STORE_DWORDX3, diff --git a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp index abb18e7f4..8eb843f02 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp @@ -1001,6 +1001,7 @@ void Translator::EmitMemory(const Decoder::Instruction& inst) { 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_SHORT_D16_HI: case Decoder::Opcode::BUFFER_STORE_FORMAT_X: case Decoder::Opcode::BUFFER_STORE_FORMAT_XY: case Decoder::Opcode::BUFFER_STORE_FORMAT_XYZ: diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 8e191b6d5..1fc07dd9f 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -30939,38 +30939,49 @@ TestCase BufferStoreVariants() { O::BUFFER_STORE_DWORDX4, O::S_ENDPGM}}; } -TestCase BufferStoreByteD16Hi(bool masked) { +TestCase BufferStoreD16Hi(bool halfword, bool masked, u32 wave_size) { using O = ShaderOpcode; TestCase test; - test.name = masked ? "BufferStoreByteD16HiMasked" : "BufferStoreByteD16HiAdjacentLanes"; + if (halfword) { + test.name = wave_size == 32u + ? (masked ? "BufferStoreShortD16HiMasked32" : "BufferStoreShortD16HiAdjacentLanes32") + : (masked ? "BufferStoreShortD16HiMasked64" : "BufferStoreShortD16HiAdjacentLanes64"); + } else { + test.name = masked ? "BufferStoreByteD16HiMasked" : "BufferStoreByteD16HiAdjacentLanes"; + } + const u32 element_bytes = halfword ? 2u : 1u; 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); + AppendSMovLiteral(&test.code, 127, wave_size == 64 ? 0xaaaaaaaau : 0u); } // Captured b21e373811f28e31 pc 0x1b8c: e0646000 80020419. - test.code.push_back(EncodeMubuf0(0x19u, 0, true, false, true)); + // Short variant uses 3e8244db54b38b0c pc 0x211c with the same test registers. + test.code.push_back(EncodeMubuf0(halfword ? 0x1bu : 0x19u, 0, true, false, true)); test.code.push_back(EncodeMubuf1(4, 2, 25)); AppendEnd(&test.code); - test.initial = std::vector(20, 0x11223344u); + test.initial = std::vector((wave_size * element_bytes + 16u) / 4u, 0x11223344u); test.expected = test.initial; - for (u32 lane = 0; lane < 60; lane++) { + for (u32 lane = 0; lane < wave_size - 4u; 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 u32 byte = (lane + 4u) * element_bytes, shift = (byte % 4u) * 8u; + const u32 bits = halfword ? 0xffffu : 0xffu, value = halfword ? 0xa5c3u : 0xc3u; + test.expected[byte / 4u] = (test.expected[byte / 4u] & ~(bits << shift)) | (value << shift); } - const auto descriptor = MakeStructuredStorageBufferData(1, 64); + const auto descriptor = MakeStructuredStorageBufferData(element_bytes, wave_size); 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.storage_buffer_range_bytes = wave_size * element_bytes; + test.compute_info.threads_num[0] = wave_size; 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.compute_info.wave_size = static_cast(wave_size); 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.opcodes = {O::V_MOV_B32, O::V_ADD_NC_U32, + halfword ? O::BUFFER_STORE_SHORT_D16_HI : O::BUFFER_STORE_BYTE_D16_HI, O::S_ENDPGM}; + test.required_spirv = {"Volatile"}; test.forbidden_spirv = {"OpAtomicLoad", "OpAtomicCompareExchange"}; return test; } @@ -40772,8 +40783,11 @@ std::vector MakeCases() { AddCase(BufferLoadFormatXyRejectsPartialRecord); AddCase(BufferStoreFormatXyRejectsPartialRecord); AddCase(BufferStoreVariants); - cases.push_back(BufferStoreByteD16Hi(false)); - cases.push_back(BufferStoreByteD16Hi(true)); + for (bool masked : {false, true}) { + cases.push_back(BufferStoreD16Hi(false, masked, 64)); + for (u32 wave_size : {32u, 64u}) + cases.push_back(BufferStoreD16Hi(true, masked, wave_size)); + } AddCase(BufferFormatVariants); AddCase(BufferLoadFormatXyzwSnapshotsOverlappingAddress); AddCase(BufferLoadFormatXyzwResource32_32FloatAppliesXy01); @@ -46482,8 +46496,11 @@ int main(int argc, char **argv) { RunCase(&vulkan, BufferStoreFormatXyRejectsPartialRecord()); RunCase(&vulkan, BufferFormatStoreVariants()); RunCase(&vulkan, TBufferStoreFormatXSintWritesSubDword()); - RunCase(&vulkan, BufferStoreByteD16Hi(false)); - RunCase(&vulkan, BufferStoreByteD16Hi(true)); + for (bool masked : {false, true}) { + RunCase(&vulkan, BufferStoreD16Hi(false, masked, 64)); + for (u32 wave_size : {32u, 64u}) + RunCase(&vulkan, BufferStoreD16Hi(true, masked, wave_size)); + } return 0; } if (argc == 2 && std::strcmp(argv[1], "--buffer-float16-store-only") == 0) { diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index ed02c58ca..0a034dc7c 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -15387,6 +15387,24 @@ void TestCapturedBufferLoadUbyteD16Decode() { "captured BUFFER_LOAD_UBYTE_D16 lost its preserved half, address, or coherence flags"); } +void TestCapturedBufferStoreShortD16HiDecode() { + namespace Decoder = ShaderRecompiler::Decoder; + const uint32_t code[] = {0xe06c6000u, 0x80010326u}; + Decoder::Instruction inst; + Decoder::DecodeInstruction(code, 0, inst); + Check(inst.opcode == Decoder::Opcode::BUFFER_STORE_SHORT_D16_HI && + inst.word_count == 2 && inst.data_dwords == 1 && inst.data_bits == 16 && + !inst.data_signed && !inst.formatted && !inst.typed, + "captured BUFFER_STORE_SHORT_D16_HI lost its raw halfword payload"); + Check(inst.dst.kind == Decoder::OperandKind::Vgpr && inst.dst.reg == 3 && + inst.dst.sdwa_sel == 5 && !inst.dst.sdwa_sext && + inst.src0.kind == Decoder::OperandKind::Vgpr && inst.src0.reg == 38 && + inst.src1.kind == Decoder::OperandKind::Sgpr && inst.src1.reg == 4 && + inst.src2.kind == Decoder::OperandKind::IntegerInlineConstant && inst.src2.value == 0 && + inst.idxen && !inst.offen && inst.offset == 0 && inst.glc && !inst.dlc && !inst.slc, + "captured BUFFER_STORE_SHORT_D16_HI lost its high half, address, or coherence flags"); +} + void TestCapturedBufferStoreD16XyzwDecode() { namespace Decoder = ShaderRecompiler::Decoder; const uint32_t code[] = {0xe21c6000u, 0x8002000eu}; @@ -16089,6 +16107,7 @@ int main() { TestPixelProgramCacheBindingIdentity(); TestGraphicsPushConstantPlacement(); TestCapturedBufferLoadUbyteD16Decode(); + TestCapturedBufferStoreShortD16HiDecode(); TestCapturedBufferStoreD16XyzwDecode(); TestNewShaderRecompilerUnsupportedMemoryDecode();