diff --git a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp index 75974ecbb..fadfef652 100644 --- a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp @@ -107,6 +107,7 @@ constexpr MemoryOpcodeInfo DS_OPCODE_LIST[] = { {0x4du, Opcode::DS_WRITE_B64, 2, 32}, {0x4eu, Opcode::DS_WRITE2_B64, 4, 32}, {0x4fu, Opcode::DS_WRITE2ST64_B64, 4, 32}, {0x76u, Opcode::DS_READ_B64, 2, 32}, {0x77u, Opcode::DS_READ2_B64, 4, 32}, {0x78u, Opcode::DS_READ2ST64_B64, 4, 32}, + {0xa0u, Opcode::DS_WRITE_B8_D16_HI, 1, 8}, {0xa1u, Opcode::DS_WRITE_B16_D16_HI, 1, 16}, {0xa6u, Opcode::DS_READ_U16_D16, 1, 16}, {0xa7u, Opcode::DS_READ_U16_D16_HI, 1, 16}, @@ -147,6 +148,7 @@ bool IsDsWriteOpcode(Opcode opcode) { switch (opcode) { case Opcode::DS_WRITE_B8: case Opcode::DS_WRITE_B16: + case Opcode::DS_WRITE_B8_D16_HI: case Opcode::DS_WRITE_B16_D16_HI: case Opcode::DS_WRITE2_B32: case Opcode::DS_WRITE2ST64_B32: @@ -429,7 +431,9 @@ void DecodeDs(uint32_t pc, std::span code, uint32_t word_index, } DecodeVectorGpr(addr, inst.src0); DecodeVectorGpr(data0, inst.src1); - if (inst.opcode == Opcode::DS_WRITE_B16_D16_HI) { + if (inst.opcode == Opcode::DS_WRITE_B8_D16_HI) { + inst.src1.sdwa_sel = 2u; + } else if (inst.opcode == Opcode::DS_WRITE_B16_D16_HI) { inst.src1.sdwa_sel = 5u; } DecodeVectorGpr(data1, inst.src2); diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp index 3d242d65f..69183a0d1 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp @@ -694,6 +694,7 @@ std::string InstructionToString(const Instruction& inst) { case Opcode::DS_READ_B128: case Opcode::DS_WRITE_B8: case Opcode::DS_WRITE_B16: + case Opcode::DS_WRITE_B8_D16_HI: case Opcode::DS_WRITE_B16_D16_HI: case Opcode::DS_WRITE2_B32: case Opcode::DS_WRITE2ST64_B32: diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index ad9a400fb..efa174d40 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -541,6 +541,7 @@ enum class Opcode { DS_READ_B128, DS_WRITE_B8, DS_WRITE_B16, + DS_WRITE_B8_D16_HI, DS_WRITE_B16_D16_HI, DS_WRITE2_B32, DS_WRITE2ST64_B32, diff --git a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp index df033162a..4877062da 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp @@ -235,6 +235,7 @@ Decoder::Operand MemorySourceAt(const Decoder::Instruction& decoded, uint32_t in return index == 0u ? decoded.src1 : index == 1u ? decoded.src0 : decoded.src2; case Decoder::Opcode::DS_WRITE_B8: case Decoder::Opcode::DS_WRITE_B16: + case Decoder::Opcode::DS_WRITE_B8_D16_HI: case Decoder::Opcode::DS_WRITE_B16_D16_HI: case Decoder::Opcode::DS_WRITE_B32: case Decoder::Opcode::DS_WRITE_B64: @@ -1076,6 +1077,7 @@ bool Translator::EmitMemory(const Decoder::Instruction& inst) { case Decoder::Opcode::DS_WRITE2ST64_B64: return DS_WRITE2(inst); case Decoder::Opcode::DS_WRITE_B8: case Decoder::Opcode::DS_WRITE_B16: + case Decoder::Opcode::DS_WRITE_B8_D16_HI: case Decoder::Opcode::DS_WRITE_B16_D16_HI: case Decoder::Opcode::DS_WRITE_B32: case Decoder::Opcode::DS_WRITE_B64: diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index a82190f15..80a30cf08 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -16416,6 +16416,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode, case Opcode::DS_READ_B128: case Opcode::DS_WRITE_B8: case Opcode::DS_WRITE_B16: + case Opcode::DS_WRITE_B8_D16_HI: case Opcode::DS_WRITE_B16_D16_HI: case Opcode::DS_WRITE2_B32: case Opcode::DS_WRITE2ST64_B32: @@ -24688,6 +24689,38 @@ TestCase DsWriteB16D16HiCapturedUsesHighHalf() { return test; } +TestCase DsWriteB8D16HiWritesByteTwo() { + using O = ShaderOpcode; + + std::vector code; + AppendVMovU32(&code, 1, 0); + AppendVMovLiteral(&code, 3, 0xdeadbeefu); + code.push_back(EncodeDs0(0x0d)); + code.push_back(EncodeDs1(0, 3, 1)); // ds_write_b32 v1, v3 + + // Every byte of the source differs, so selecting any byte but 23:16 fails. + AppendVMovU32(&code, 20, 1); + AppendVMovLiteral(&code, 2, 0x11a22233u); + AppendVMovLiteral(&code, 0, 0x87654321u); + code.push_back(EncodeDs0(0xa0)); + code.push_back(EncodeDs1(0, 2, 20)); // ds_write_b8_d16_hi v20, v2 + + code.push_back(EncodeDs0(0x36)); + code.push_back(EncodeDs1(4, 0, 1)); // ds_read_b32 v4, v1 + AppendStoreVgpr(&code, 4, 0); + AppendStoreVgpr(&code, 0, 1); + AppendEnd(&code); + + TestCase test{"DsWriteB8D16HiWritesByteTwo", + code, + std::vector(2, 0), + {0xdeada2efu, 0x87654321u}, + {O::V_MOV_B32, O::DS_WRITE_B32, O::DS_WRITE_B8_D16_HI, + O::DS_READ_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}}; + test.decoded_counts = {{"DS_WRITE_B8_D16_HI", 1}}; + return test; +} + TestCase DsReadU16D16HiCapturedPreservesLowHalf() { using O = ShaderOpcode; @@ -28177,6 +28210,7 @@ std::vector MakeCases() { AddCase(ScratchIsPrivatePerInvocation); AddCase(DsReadWriteVariants); AddCase(DsWriteB16D16HiCapturedUsesHighHalf); + AddCase(DsWriteB8D16HiWritesByteTwo); AddCase(DsReadU16D16CapturedPreservesHighHalf); AddCase(DsReadU16D16HiCapturedPreservesLowHalf); AddCase(DsAppendConsumeUsesEncodedLdsSelector); diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index b71499546..ddedfd269 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -4458,6 +4458,23 @@ void TestNewShaderDecoderArchitecture() { d16_hi_write.src1.sdwa_sel == 5u, "DS decoder rejected the captured high-half D16 write"); + // Captured from GTA V (PPSA04264) compute shader, pc 0x1598. + const uint32_t d16_hi_byte_write_ds[] = {0xda800200u, 0x00001413u}; + Instruction d16_hi_byte_write; + ShaderRecompiler::Decoder::DecodeInstruction(d16_hi_byte_write_ds, 0u, + d16_hi_byte_write); + Check(d16_hi_byte_write.family == Family::DS && + d16_hi_byte_write.opcode == Opcode::DS_WRITE_B8_D16_HI && + d16_hi_byte_write.word_count == 2u && + d16_hi_byte_write.src_count == 2u && + d16_hi_byte_write.data_dwords == 1u && + d16_hi_byte_write.data_bits == 8u && + d16_hi_byte_write.offset == 0x200u && !d16_hi_byte_write.gds && + d16_hi_byte_write.src0.reg == 19u && + d16_hi_byte_write.src1.reg == 20u && + d16_hi_byte_write.src1.sdwa_sel == 2u, + "DS decoder rejected the captured high-half byte write"); + constexpr uint32_t packed_source_selectors[][2] = { {0xcc0e0000u, 0x0c0a0300u}, // Source 0: instruction bit 59. {0xcc0e0000u, 0x140a0300u}, // Source 1: instruction bit 60.