From 753f285198c43a544e1b828ab63b8381b892e0d1 Mon Sep 17 00:00:00 2001 From: nmzik Date: Thu, 22 Oct 2026 05:06:47 +0200 Subject: [PATCH] shader: generalize D16 buffer format component handling --- .../recompiler/frontend/decode/MemoryOps.cpp | 39 +++++++++---------- .../recompiler/frontend/translate/Memory.cpp | 17 +++++--- tests/shaderCfgTests.cpp | 18 +++++++++ 3 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp index bf7f046d1..4fe12d4fb 100644 --- a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp @@ -6,13 +6,14 @@ namespace Libs::Graphics::ShaderRecompiler::Decoder { namespace { struct MemoryOpcodeInfo { - uint32_t encoding = 0; - Opcode decoded = Opcode::UNSUPPORTED; - uint32_t data_dwords = 1; - uint32_t data_bits = 32; - bool data_signed = false; - bool typed = false; - bool formatted = false; + uint32_t encoding = 0; + Opcode decoded = Opcode::UNSUPPORTED; + uint32_t data_dwords = 1; + uint32_t data_bits = 32; + bool data_signed = false; + bool typed = false; + bool formatted = false; + uint32_t data_components = 0; // D16 formats pack two components per dword }; constexpr MemoryOpcodeInfo SMEM_OPCODE_LIST[] = { @@ -73,9 +74,9 @@ constexpr MemoryOpcodeInfo MUBUF_OPCODE_LIST[] = { {0x71u, Opcode::BUFFER_GL0_INV, 0, 0}, {0x72u, Opcode::BUFFER_GL1_INV, 0, 0}, {0x80u, Opcode::BUFFER_LOAD_FORMAT_D16_X, 1, 16, false, false, true}, - {0x83u, Opcode::BUFFER_LOAD_FORMAT_D16_XYZW, 2, 16, false, false, true}, + {0x83u, Opcode::BUFFER_LOAD_FORMAT_D16_XYZW, 2, 16, false, false, true, 4}, {0x84u, Opcode::BUFFER_STORE_FORMAT_D16_X, 1, 16, false, false, true}, - {0x87u, Opcode::BUFFER_STORE_FORMAT_D16_XYZW, 2, 16, false, false, true}, + {0x87u, Opcode::BUFFER_STORE_FORMAT_D16_XYZW, 2, 16, false, false, true, 4}, }; constexpr MemoryOpcodeInfo MTBUF_OPCODE_LIST[] = { @@ -158,12 +159,13 @@ void ApplyMemoryInfo(Instruction& inst, const MemoryOpcodeInfo* info) { inst.opcode = Opcode::UNSUPPORTED; return; } - inst.opcode = info->decoded; - inst.data_dwords = info->data_dwords; - inst.data_bits = info->data_bits; - inst.data_signed = info->data_signed; - inst.typed = info->typed; - inst.formatted = info->formatted; + inst.opcode = info->decoded; + inst.data_dwords = info->data_dwords; + inst.data_bits = info->data_bits; + inst.data_signed = info->data_signed; + inst.typed = info->typed; + inst.formatted = info->formatted; + inst.data_components = info->data_components; } } // namespace @@ -323,12 +325,7 @@ void DecodeMubuf(uint32_t pc, std::span code, uint32_t word_inde } DecodeVectorGpr(vdata, inst.dst); - if (inst.opcode == Opcode::BUFFER_LOAD_FORMAT_D16_XYZW || - inst.opcode == Opcode::BUFFER_STORE_FORMAT_D16_XYZW) { - inst.data_components = 4u; - } - if (inst.opcode == Opcode::BUFFER_LOAD_FORMAT_D16_X || - inst.opcode == Opcode::BUFFER_LOAD_UBYTE_D16 || + if (inst.opcode == Opcode::BUFFER_LOAD_UBYTE_D16 || 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; diff --git a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp index beaef6372..0b755ef24 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp @@ -455,12 +455,17 @@ void Translator::BUFFER_LOAD(const Decoder::Instruction& inst) { AddMemoryInfo(memory, inst.pc)); if (bits != 32u) { WriteOperand(inst.dst, WidenSubdword(loaded, bits, sign)); - } else if (memory.data_dwords == 1u) { - WriteOperand(inst.dst, loaded); - } else { - for (uint32_t component = 0; component < memory.data_dwords; component++) { - WriteOperand(OffsetOperand(inst.dst, component), - ir.CompositeExtract(loaded, component)); + return; + } + for (uint32_t component = 0; component < memory.data_dwords; component++) { + const auto dst = OffsetOperand(inst.dst, component); + const auto value = + memory.data_dwords == 1u ? IR::U32(loaded) : ir.CompositeExtract(loaded, component); + // D16 formats with an odd component count keep the high half of their final VGPR. + if (memory.data_bits == 16u && component * 2u + 1u == memory.component_count) { + Write16Bits(dst, value); + } else { + WriteOperand(dst, value); } } } diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index c7b01daa7..4aa2fe1e8 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -16068,6 +16068,23 @@ void TestCapturedBufferStoreFormatD16HiXDecode() { "captured BUFFER_STORE_FORMAT_D16_HI_X lost its high half, index, or coherence flags"); } +void TestCapturedBufferLoadD16XyzwDecode() { + namespace Decoder = ShaderRecompiler::Decoder; + const uint32_t code[] = {0xe20c2000u, 0x8001100cu}; + Decoder::Instruction inst; + Decoder::DecodeInstruction(code, 0, inst); + Check(inst.opcode == Decoder::Opcode::BUFFER_LOAD_FORMAT_D16_XYZW && + inst.word_count == 2 && inst.data_dwords == 2 && inst.data_bits == 16 && + inst.data_components == 4 && inst.formatted && !inst.typed, + "captured D16 XYZW load lost its four components in two packed VGPRs"); + Check(inst.dst.kind == Decoder::OperandKind::Vgpr && inst.dst.reg == 16 && + inst.dst.sdwa_sel == 6 && + inst.src0.kind == Decoder::OperandKind::Vgpr && inst.src0.reg == 12 && + inst.src1.kind == Decoder::OperandKind::Sgpr && inst.src1.reg == 4 && + inst.idxen && !inst.offen, + "captured D16 XYZW load lost its full destination VGPRs or index"); +} + void TestCapturedBufferStoreD16XyzwDecode() { namespace Decoder = ShaderRecompiler::Decoder; const uint32_t code[] = {0xe21c6000u, 0x8002000eu}; @@ -16883,6 +16900,7 @@ int main() { TestCapturedBufferLoadUbyteD16Decode(); TestCapturedBufferStoreShortD16HiDecode(); TestCapturedBufferStoreFormatD16HiXDecode(); + TestCapturedBufferLoadD16XyzwDecode(); TestCapturedBufferStoreD16XyzwDecode(); TestNewShaderRecompilerUnsupportedMemoryDecode();