diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp index a17b33ae0..5f0b4bb51 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp @@ -716,10 +716,21 @@ uint32_t FormattedLoad(ValueEmitContext& ctx, const IR::Inst& inst, const IR::Me return LoadWordPrepared(ctx, inst, RebaseRawComponent(mem, 0u), resource); } const auto plan = PrepareFormattedMemory(ctx, inst, mem, resource, info); - return EmitValueOrDefaultIfCondition( + const auto value = EmitValueOrDefaultIfCondition( ctx.state, plan.in_bounds, TypeU32(ctx.state), FormattedOutOfBoundsValue(ctx, mem, plan, 1u), [&]() { return LoadFormattedInBounds(ctx, mem, plan, 0u); }); + if (mem.data_bits != 16u) { + return value; + } + if (info.type == Format::ComponentType::Uint) { + return EmitUMin32(ctx.state, value, ConstantU32(ctx.state, 0xffffu)); + } + if (info.type == Format::ComponentType::Sint) { + const auto lower = EmitSMax32(ctx.state, value, ConstantU32(ctx.state, 0xffff8000u)); + return EmitSMin32(ctx.state, lower, ConstantU32(ctx.state, 0x7fffu)); + } + return EmitConvertF16F32(ctx.state, EmitBitCastF32U32(ctx.state, value)); }); } diff --git a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp index 0f849b9fd..2fd29218f 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}, + {0x80u, Opcode::BUFFER_LOAD_FORMAT_D16_X, 1, 16, false, false, true}, {0x84u, Opcode::BUFFER_STORE_FORMAT_D16_X, 1, 16, false, false, true}, }; @@ -297,6 +298,9 @@ void DecodeMubuf(uint32_t pc, std::span code, uint32_t word_inde } DecodeVectorGpr(vdata, inst.dst); + if (inst.opcode == Opcode::BUFFER_LOAD_FORMAT_D16_X) { + inst.dst.sdwa_sel = 4u; + } DecodeVectorGpr(vaddr, inst.src0); DecodeScalarSource(srsrc * 4u, pc, inst.src1); DecodeScalarSource(soffset, pc, inst.src2); diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp index 5c6528fac..e4f9ed68f 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp @@ -618,6 +618,7 @@ std::string InstructionToString(const Instruction& inst) { case Opcode::BUFFER_LOAD_FORMAT_XY: case Opcode::BUFFER_LOAD_FORMAT_XYZ: case Opcode::BUFFER_LOAD_FORMAT_XYZW: + case Opcode::BUFFER_LOAD_FORMAT_D16_X: case Opcode::BUFFER_STORE_FORMAT_X: case Opcode::BUFFER_STORE_FORMAT_XY: case Opcode::BUFFER_STORE_FORMAT_XYZ: diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index d54de73b1..cbfe143e5 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -505,6 +505,7 @@ enum class Opcode { BUFFER_LOAD_FORMAT_XY, BUFFER_LOAD_FORMAT_XYZ, BUFFER_LOAD_FORMAT_XYZW, + BUFFER_LOAD_FORMAT_D16_X, BUFFER_STORE_FORMAT_X, BUFFER_STORE_FORMAT_XY, BUFFER_STORE_FORMAT_XYZ, diff --git a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp index bfd5eb336..02fbcd32e 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp @@ -427,7 +427,7 @@ void Translator::S_LOAD(const Decoder::Instruction& inst, bool raw) { void Translator::BUFFER_LOAD(const Decoder::Instruction& inst) { const auto memory = MemoryInfoFromDecoded(inst); IR::ValueOpcode opcode; - const auto bits = memory.data_bits; + const auto bits = memory.formatted ? 32u : memory.data_bits; const auto sign = memory.data_signed; switch (bits) { case 8u: opcode = IR::ValueOpcode::LoadBufferU8; break; @@ -981,6 +981,7 @@ void Translator::EmitMemory(const Decoder::Instruction& inst) { case Decoder::Opcode::BUFFER_LOAD_FORMAT_XY: case Decoder::Opcode::BUFFER_LOAD_FORMAT_XYZ: case Decoder::Opcode::BUFFER_LOAD_FORMAT_XYZW: + case Decoder::Opcode::BUFFER_LOAD_FORMAT_D16_X: case Decoder::Opcode::TBUFFER_LOAD_FORMAT_X: case Decoder::Opcode::TBUFFER_LOAD_FORMAT_XY: case Decoder::Opcode::TBUFFER_LOAD_FORMAT_XYZ: diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index fde86db41..277296ee1 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -28716,6 +28716,70 @@ TestCase BufferStoreFormatXSnorm16ClampsRoundsAndPreservesHalfwords() { return test; } +std::vector BufferLoadFormatD16XCases() { + using F = Prospero::BufferFormat; + using O = ShaderOpcode; + struct LoadCase { + const char *name; + F format; + u32 input; + u32 expected; + u32 selector = 4; + u32 index = 0; + bool active = true; + bool overlap = false; + }; + constexpr LoadCase inputs[] = { + {"BufferLoadFormatD16XFloat16", F::k16Float, 0x7e63c000u, 0xabcdc000u}, + {"BufferLoadFormatD16XFloat32", F::k32Float, 0x3f000000u, 0xabcd3800u}, + {"BufferLoadFormatD16XUint32", F::k32UInt, 0x1234fffeu, 0xabcdffffu}, + {"BufferLoadFormatD16XUintAtMax", F::k32UInt, 0x0000ffffu, 0xabcdffffu}, + {"BufferLoadFormatD16XUintMax32", F::k32UInt, 0xffffffffu, 0xabcdffffu}, + {"BufferLoadFormatD16XSint32", F::k32SInt, 0xffff8001u, 0xabcd8001u}, + {"BufferLoadFormatD16XSintAboveMax", F::k32SInt, 0x00008000u, 0xabcd7fffu}, + {"BufferLoadFormatD16XSintBelowMin", F::k32SInt, 0xffff7fffu, 0xabcd8000u}, + {"BufferLoadFormatD16XSintAtMax", F::k32SInt, 0x00007fffu, 0xabcd7fffu}, + {"BufferLoadFormatD16XSintAtMin", F::k32SInt, 0xffff8000u, 0xabcd8000u}, + {"BufferLoadFormatD16XSintMax32", F::k32SInt, 0x7fffffffu, 0xabcd7fffu}, + {"BufferLoadFormatD16XSintMin32", F::k32SInt, 0x80000000u, 0xabcd8000u}, + {"BufferLoadFormatD16XUnorm8", F::k8UNorm, 0xffu, 0xabcd3c00u}, + {"BufferLoadFormatD16XSnorm16", F::k16SNorm, 0x8001u, 0xabcdbc00u}, + {"BufferLoadFormatD16XFloatOne", F::k32Float, 0u, 0xabcd3c00u, 1}, + {"BufferLoadFormatD16XOobFloatOne", F::k32Float, 0u, 0xabcd3c00u, 1, 4}, + {"BufferLoadFormatD16XOobUintOne", F::k32UInt, 0u, 0xabcd0001u, 1, 4}, + {"BufferLoadFormatD16XOobZero", F::k32Float, 0u, 0xabcd0000u, 4, 4}, + {"BufferLoadFormatD16XInactive", F::k32Float, 0u, 0xabcd1234u, 4, 0, false}, + {"BufferLoadFormatD16XOverlappingAddress", F::k16UInt, 7u, 7u, 4, 0, true, true}, + }; + std::vector cases; + for (const auto &input : inputs) { + TestCase test; + test.name = input.name; + AppendVMovLiteral(&test.code, 0, input.overlap ? input.index : 0xabcd1234u); + AppendVMovU32(&test.code, 1, input.index); + if (!input.active) { + test.code.push_back(EncodeSop1(0x04, 126, InlineU32(0))); + } + test.code.push_back(EncodeMubuf0(0x80u, 0, true, false)); + test.code.push_back(EncodeMubuf1(0, 0, input.overlap ? 0 : 1)); + if (!input.active) { + test.code.push_back(EncodeSMovB32(126, InlineU32(1))); + test.code.push_back(EncodeSMovB32(127, InlineU32(0))); + } + AppendStoreVgpr(&test.code, 0, 2); + AppendEnd(&test.code); + test.initial = {input.input, 0xdeadbeefu, 0u, 0xcafef00du}; + test.expected = {input.input, 0xdeadbeefu, input.expected, 0xcafef00du}; + test.user_data = MakeStructuredStorageBufferData(4, 4, false, BufferFormat(input.format)); + test.user_data[3] = (test.user_data[3] & ~7u) | input.selector; + test.has_user_data = true; + test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_FORMAT_D16_X, + O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + cases.push_back(std::move(test)); + } + return cases; +} + std::vector BufferStoreFormatD16XCases() { using F = Prospero::BufferFormat; using O = ShaderOpcode; @@ -35585,6 +35649,9 @@ std::vector MakeCases() { AddCase(BufferStoreFormatXyzwFloat16ConvertsComponents); AddCase(BufferStoreFormatXyzwSnorm16CapturedSkinningVectors); AddCase(BufferStoreFormatXSnorm16ClampsRoundsAndPreservesHalfwords); + for (auto &test : BufferLoadFormatD16XCases()) { + cases.push_back(std::move(test)); + } for (auto &test : BufferStoreFormatD16XCases()) { cases.push_back(std::move(test)); } @@ -40850,6 +40917,9 @@ int main(int argc, char **argv) { RunCase(&vulkan, BufferStoreFormatXyzwFloat16ConvertsComponents()); RunCase(&vulkan, BufferStoreFormatXyzwSnorm16CapturedSkinningVectors()); RunCase(&vulkan, BufferStoreFormatXSnorm16ClampsRoundsAndPreservesHalfwords()); + for (const auto &test : BufferLoadFormatD16XCases()) { + RunCase(&vulkan, test); + } for (const auto &test : BufferStoreFormatD16XCases()) { RunCase(&vulkan, test); }