diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp index 7e8ef2219..a17b33ae0 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp @@ -725,9 +725,18 @@ uint32_t FormattedLoad(ValueEmitContext& ctx, const IR::Inst& inst, const IR::Me uint32_t EncodeFormattedStoreComponent(ValueEmitContext& ctx, const Format::BufferFormatInfo& info, - uint32_t component, uint32_t data) { + uint32_t component, uint32_t data, uint32_t data_bits) { auto& state = ctx.state; const auto bits = info.component_bits[component]; + if (data_bits == 16u) { + if (info.type == Format::ComponentType::Uint) { + data = EmitBitFieldUExtract(state, data, ConstantU32(state, 0), ConstantU32(state, 16)); + } else if (info.type == Format::ComponentType::Sint) { + data = EmitBitFieldSExtract(state, data, ConstantU32(state, 0), ConstantU32(state, 16)); + } else { + data = EmitBitCastU32F32(state, EmitF16BitsToF32(state, data)); + } + } const bool normalized = info.type == Format::ComponentType::Unorm || info.type == Format::ComponentType::Snorm; const bool scaled = info.type == Format::ComponentType::Uscaled || @@ -779,7 +788,7 @@ void StoreFormattedPrepared(ValueEmitContext& ctx, const IR::Inst& inst, value, data, component); } } - value = EncodeFormattedStoreComponent(ctx, info, component, value); + value = EncodeFormattedStoreComponent(ctx, info, component, value, mem.data_bits); const auto bits = info.component_bits[component]; if (info.packed_bitfield) { packed = EmitBitFieldInsert( diff --git a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp index 4c51be1cd..0f849b9fd 100644 --- a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp @@ -63,6 +63,7 @@ constexpr MemoryOpcodeInfo MUBUF_OPCODE_LIST[] = { {0x50u, Opcode::BUFFER_ATOMIC_SWAP_X2, 2, 32}, {0x59u, Opcode::BUFFER_ATOMIC_AND_X2, 2, 32}, {0x5au, Opcode::BUFFER_ATOMIC_OR_X2, 2, 32}, + {0x84u, Opcode::BUFFER_STORE_FORMAT_D16_X, 1, 16, false, false, true}, }; constexpr MemoryOpcodeInfo MTBUF_OPCODE_LIST[] = { diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp index 0e06241cd..5c6528fac 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp @@ -622,6 +622,7 @@ std::string InstructionToString(const Instruction& inst) { case Opcode::BUFFER_STORE_FORMAT_XY: case Opcode::BUFFER_STORE_FORMAT_XYZ: case Opcode::BUFFER_STORE_FORMAT_XYZW: + case Opcode::BUFFER_STORE_FORMAT_D16_X: case Opcode::BUFFER_LOAD_UBYTE: case Opcode::BUFFER_LOAD_USHORT: case Opcode::BUFFER_LOAD_DWORD: diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index c91292c4a..d54de73b1 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -509,6 +509,7 @@ enum class Opcode { BUFFER_STORE_FORMAT_XY, BUFFER_STORE_FORMAT_XYZ, BUFFER_STORE_FORMAT_XYZW, + BUFFER_STORE_FORMAT_D16_X, BUFFER_LOAD_UBYTE, BUFFER_LOAD_SBYTE, BUFFER_LOAD_USHORT, diff --git a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp index d5aa60526..bfd5eb336 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp @@ -187,7 +187,7 @@ Decoder::Operand MemorySourceAt(const Decoder::Instruction& decoded, uint32_t in if (decoded.family == Decoder::Family::MUBUF || decoded.family == Decoder::Family::MTBUF) { const bool store_or_atomic = (decoded.opcode >= Decoder::Opcode::BUFFER_STORE_FORMAT_X && - decoded.opcode <= Decoder::Opcode::BUFFER_STORE_FORMAT_XYZW) || + decoded.opcode <= Decoder::Opcode::BUFFER_STORE_FORMAT_D16_X) || (decoded.opcode >= Decoder::Opcode::BUFFER_STORE_BYTE && decoded.opcode <= Decoder::Opcode::BUFFER_STORE_DWORDX4) || (decoded.opcode >= Decoder::Opcode::TBUFFER_STORE_FORMAT_X && @@ -473,7 +473,8 @@ void Translator::BUFFER_STORE(const Decoder::Instruction& inst) { const auto data = ReadU32(data_src); IR::ValueOpcode opcode; IR::Value value; - switch (memory.data_bits) { + // Formatted stores carry packed VGPR bits; the format determines their conversion. + switch (memory.formatted ? 32u : memory.data_bits) { case 8u: opcode = IR::ValueOpcode::StoreBufferU8; value = NarrowSubdword(data, 8u); @@ -995,6 +996,7 @@ void Translator::EmitMemory(const Decoder::Instruction& inst) { case Decoder::Opcode::BUFFER_STORE_FORMAT_XY: case Decoder::Opcode::BUFFER_STORE_FORMAT_XYZ: case Decoder::Opcode::BUFFER_STORE_FORMAT_XYZW: + case Decoder::Opcode::BUFFER_STORE_FORMAT_D16_X: case Decoder::Opcode::TBUFFER_STORE_FORMAT_X: case Decoder::Opcode::TBUFFER_STORE_FORMAT_XY: case Decoder::Opcode::TBUFFER_STORE_FORMAT_XYZ: diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index bfff3808e..fde86db41 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -712,7 +712,7 @@ constexpr u32 EncodeVopcSdwa(u32 src0, u32 sdst = 0, u32 sd = 0, constexpr u32 EncodeMubuf0(u32 opcode, u32 offset = 0, bool idxen = false, bool offen = true, bool glc = false) { - return (0x38u << 26u) | ((opcode & 0x7fu) << 18u) | + return (0x38u << 26u) | ((opcode & 0xffu) << 18u) | (offen ? (1u << 12u) : 0u) | (idxen ? (1u << 13u) : 0u) | (glc ? (1u << 14u) : 0u) | (offset & 0xfffu); } @@ -28716,6 +28716,44 @@ TestCase BufferStoreFormatXSnorm16ClampsRoundsAndPreservesHalfwords() { return test; } +std::vector BufferStoreFormatD16XCases() { + using F = Prospero::BufferFormat; + using O = ShaderOpcode; + struct StoreCase { + const char *name; + F format; + u32 input; + u32 expected; + }; + constexpr StoreCase inputs[] = { + {"BufferStoreFormatD16XFloat16", F::k16Float, 0x7e63c000u, 0xffffc000u}, + {"BufferStoreFormatD16XFloat32", F::k32Float, 0x7e633800u, 0x3f000000u}, + {"BufferStoreFormatD16XUint32", F::k32UInt, 0x7e63fffeu, 0x0000fffeu}, + {"BufferStoreFormatD16XSint32", F::k32SInt, 0x7e63fffeu, 0xfffffffeu}, + {"BufferStoreFormatD16XUnorm8", F::k8UNorm, 0x7e633800u, 0xffffff80u}, + {"BufferStoreFormatD16XSnorm16", F::k16SNorm, 0x7e63b800u, 0xffffc000u}, + {"BufferStoreFormatD16XZeroFill", F::k16_16Float, 0x7e633800u, 0x00003800u}, + }; + std::vector cases; + for (const auto &input : inputs) { + TestCase test; + test.name = input.name; + AppendVMovLiteral(&test.code, 66, input.input); + AppendVMovU32(&test.code, 65, 1); + // Match the captured CS824586f35db5c80d instruction at pc 0x484. + test.code.push_back(EncodeMubuf0(0x84u, 0, true, false)); + test.code.push_back(EncodeMubuf1(66, 0, 65)); + AppendEnd(&test.code); + test.initial = {0xdeadbeefu, 0xffffffffu, 0xcafef00du}; + test.expected = {0xdeadbeefu, input.expected, 0xcafef00du}; + test.user_data = MakeStructuredStorageBufferData(4, 3, false, BufferFormat(input.format)); + test.has_user_data = true; + test.opcodes = {O::V_MOV_B32, O::BUFFER_STORE_FORMAT_D16_X, O::S_ENDPGM}; + cases.push_back(std::move(test)); + } + return cases; +} + std::vector FormattedStoreConversionCases() { using F = Prospero::BufferFormat; using O = ShaderOpcode; @@ -35547,6 +35585,9 @@ std::vector MakeCases() { AddCase(BufferStoreFormatXyzwFloat16ConvertsComponents); AddCase(BufferStoreFormatXyzwSnorm16CapturedSkinningVectors); AddCase(BufferStoreFormatXSnorm16ClampsRoundsAndPreservesHalfwords); + for (auto &test : BufferStoreFormatD16XCases()) { + cases.push_back(std::move(test)); + } for (auto &test : FormattedStoreConversionCases()) { cases.push_back(std::move(test)); } @@ -40809,6 +40850,9 @@ int main(int argc, char **argv) { RunCase(&vulkan, BufferStoreFormatXyzwFloat16ConvertsComponents()); RunCase(&vulkan, BufferStoreFormatXyzwSnorm16CapturedSkinningVectors()); RunCase(&vulkan, BufferStoreFormatXSnorm16ClampsRoundsAndPreservesHalfwords()); + for (const auto &test : BufferStoreFormatD16XCases()) { + RunCase(&vulkan, test); + } for (const auto &test : FormattedStoreConversionCases()) { RunCase(&vulkan, test); }