diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/D16Memory.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/D16Memory.cpp new file mode 100644 index 000000000..a4bf4e5b9 --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/tests/execution/D16Memory.cpp @@ -0,0 +1,118 @@ +#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp" +#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp" +#include "Recompiler.hpp" +#include "VulkanTestDevice.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +using AgcDriver::Graphics::Require; +using ShaderRecompiler::ShaderStage; + +constexpr std::uint32_t Threads = 32; +constexpr std::uint32_t InputBytes = 16; +constexpr std::uint32_t Results = 16; +alignas(256) std::array Input{}; +alignas(256) std::array Output{}; + +alignas(256) constexpr std::array D16Code{ + 0x34020084, 0x34060086, 0xbe880300, 0x8709ff01, 0x0000ffff, 0x7e1402ff, 0xabcdabcd, 0x7e1602ff, + 0xabcdabcd, 0x7e1802ff, 0xabcdabcd, 0x7e1a02ff, 0xabcdabcd, 0x7e1c02ff, 0xabcdabcd, 0x7e1e02ff, + 0xabcdabcd, 0x7e2002ff, 0xabcdabcd, 0x7e2202ff, 0xabcdabcd, 0x7e2402ff, 0xabcdabcd, 0x7e2602ff, + 0xabcdabcd, 0x7e2802ff, 0xabcdabcd, 0x7e2a02ff, 0xabcdabcd, 0xe0801001, 0x80000a01, 0xe0841002, + 0x80000a01, 0xe0881003, 0x80000b01, 0xe08c1005, 0x80000b01, 0xe0901006, 0x80000c01, 0xe0941008, + 0x80000c01, 0xdc808001, 0x0d080001, 0xdc848002, 0x0d080001, 0xdc888003, 0x0e080001, 0xdc8c8005, + 0x0e080001, 0xdc908006, 0x0f080001, 0xdc948008, 0x0f080001, 0x34040082, 0xbf8c3f70, 0xe0301000, + 0x80000401, 0xbf8c3f70, 0xd8340000, 0x00000402, 0xbf8cc07f, 0xda880001, 0x10000002, 0xda8c0002, + 0x10000002, 0xda900003, 0x11000002, 0xda940001, 0x11000002, 0xda980002, 0x12000002, 0xda9c0000, + 0x12000002, 0xbf8cc07f, 0x7e2602ff, 0x80c1a2b3, 0xe0641028, 0x80011303, 0xe06c102c, 0x80011303, + 0xda800001, 0x00001302, 0xbf8cc07f, 0xd8d80000, 0x14000002, 0xbf8c0070, 0xe0701000, 0x80010a03, + 0xe0701004, 0x80010b03, 0xe0701008, 0x80010c03, 0xe070100c, 0x80010d03, 0xe0701010, 0x80010e03, + 0xe0701014, 0x80010f03, 0xe0701018, 0x80011003, 0xe070101c, 0x80011103, 0xe0701020, 0x80011203, + 0xe0701024, 0x80011403, 0xbf810000, +}; + +std::array BufferDescriptor(const void* data, std::uint32_t bytes) { + const auto address = reinterpret_cast(data); + return {static_cast(address), static_cast((address >> 32u) & 0xffffu), bytes, 0x01016facu}; +} + +void Run(AgcDriver::VulkanDevice& device) { + for (std::uint32_t i = 0; i < Input.size(); ++i) Input[i] = static_cast(i * 37u + 0x81u); + Output.fill(0xdeadbeefu); + std::vector userData(8, 0u); + const auto input = BufferDescriptor(Input.data(), static_cast(Input.size())); + const auto output = BufferDescriptor(Output.data(), static_cast(Output.size() * 4u)); + std::copy(input.begin(), input.end(), userData.begin()); + std::copy(output.begin(), output.end(), userData.begin() + 4); + const std::span code(D16Code); + const std::array memory{{{reinterpret_cast(code.data()), std::as_bytes(code)}}}; + const ShaderRecompiler::ShaderComputeStageInfo compute{{Threads, 1, 1}, 4u * Threads, {false, false, false}, false, 1}; + ShaderRecompiler::RecompileRequest request{ + {ShaderStage::Compute, reinterpret_cast(code.data()), code, 0, {}}, + {32, 0, userData, compute, std::nullopt, std::nullopt, memory}, + device.Target(), + {0, 0, 0, 128} + }; + request.useCache = false; + const auto result = ShaderRecompiler::Recompile(request); + device.Dispatch(result, 1, 1, 1, {}, reinterpret_cast(code.data())); + device.WaitIdle(); +} + +std::string Hex(std::uint32_t value) { + char text[16]; + std::snprintf(text, sizeof(text), "0x%08x", value); + return text; +} + +void Check() { + constexpr std::array names{ + "buffer_load_ubyte_d16(_hi)", "buffer_load_sbyte_d16(_hi)", "buffer_load_short_d16(_hi)", + "global_load_ubyte_d16(_hi)", "global_load_sbyte_d16(_hi)", "global_load_short_d16(_hi)", + "ds_read_u8_d16(_hi)", "ds_read_i8_d16(_hi)", "ds_read_u16_d16(_hi)", "ds_write_b8_d16_hi", + "buffer_store_byte_d16_hi", "buffer_store_short_d16_hi", + }; + for (std::uint32_t tid = 0; tid < Threads; ++tid) { + const std::uint8_t* in = &Input[tid * InputBytes]; + const auto u8 = [&](std::uint32_t at) { return static_cast(in[at]); }; + const auto i8 = [&](std::uint32_t at) { return static_cast(static_cast(static_cast(in[at]))) & 0xffffu; }; + const auto u16 = [&](std::uint32_t at) { return u8(at) | (u8(at + 1u) << 8u); }; + const std::uint32_t loads[3] = {u8(1) | (u8(2) << 16u), i8(3) | (i8(5) << 16u), u16(6) | (u16(8) << 16u)}; + std::uint32_t dword = 0; + std::memcpy(&dword, in, 4); + const std::uint32_t written = (dword & 0xffff00ffu) | (0xc1u << 8u); + std::array expected{ + loads[0], loads[1], loads[2], loads[0], loads[1], loads[2], + u8(1) | (u8(2) << 16u), i8(3) | (i8(1) << 16u), u16(2) | (u16(0) << 16u), written, + 0xdeadbec1u, 0xdead80c1u, + }; + const auto* out = &Output[tid * Results]; + for (std::uint32_t j = 0; j < expected.size(); ++j) { + Require(out[j] == expected[j], std::string("d16 memory: lane ") + std::to_string(tid) + " " + names[j] + " is " + Hex(out[j]) + ", expected " + Hex(expected[j])); + } + } +} + +} + +int main() { + try { + const auto device = OpenVulkanTestDevice(); + if (!device) return VulkanTestSkipped; + Run(*device); + Check(); + std::puts("d16 memory tests passed"); + return 0; + } catch (const std::exception& error) { + std::cerr << error.what() << '\n'; + return 1; + } +} diff --git a/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaOpcode.hpp b/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaOpcode.hpp index e622c4f72..bb74fb55c 100644 --- a/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaOpcode.hpp +++ b/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaOpcode.hpp @@ -638,6 +638,14 @@ enum class RdnaOpcode : std::uint16_t { BufferLoadDwordx3, BufferStoreByte, BufferStoreShort, + BufferLoadUbyteD16, + BufferLoadUbyteD16Hi, + BufferLoadSbyteD16, + BufferLoadSbyteD16Hi, + BufferLoadShortD16, + BufferLoadShortD16Hi, + BufferStoreByteD16Hi, + BufferStoreShortD16Hi, BufferStoreDwordx2, BufferStoreDwordx3, TbufferLoadFormatX, @@ -673,6 +681,14 @@ enum class RdnaOpcode : std::uint16_t { FlatLoadDwordx4, FlatStoreByte, FlatStoreShort, + FlatLoadUbyteD16, + FlatLoadUbyteD16Hi, + FlatLoadSbyteD16, + FlatLoadSbyteD16Hi, + FlatLoadShortD16, + FlatLoadShortD16Hi, + FlatStoreByteD16Hi, + FlatStoreShortD16Hi, FlatStoreDword, FlatStoreDwordx2, FlatStoreDwordx3, @@ -736,6 +752,11 @@ enum class RdnaOpcode : std::uint16_t { DsCmpstRtnF32, DsWrapRtnB32, DsNop, + DsReadU8D16, + DsReadU8D16Hi, + DsReadI8D16, + DsReadI8D16Hi, + DsWriteB8D16Hi, DsWrite2st64B32, DsWrite2B64, DsWrite2st64B64, diff --git a/core/shader/recompiler/RdnaDecoder/src/RdnaMemoryOpDecoder.cpp b/core/shader/recompiler/RdnaDecoder/src/RdnaMemoryOpDecoder.cpp index f5ebd6200..da5b0ab52 100644 --- a/core/shader/recompiler/RdnaDecoder/src/RdnaMemoryOpDecoder.cpp +++ b/core/shader/recompiler/RdnaDecoder/src/RdnaMemoryOpDecoder.cpp @@ -8,6 +8,37 @@ namespace ShaderRecompiler { namespace { +RdnaOperand d16Half(RdnaOperand operand, RdnaOpcode opcode) { + switch (opcode) { + case RdnaOpcode::BufferLoadUbyteD16: + case RdnaOpcode::BufferLoadSbyteD16: + case RdnaOpcode::BufferLoadShortD16: + case RdnaOpcode::FlatLoadUbyteD16: + case RdnaOpcode::FlatLoadSbyteD16: + case RdnaOpcode::FlatLoadShortD16: + case RdnaOpcode::DsReadU8D16: + case RdnaOpcode::DsReadI8D16: + case RdnaOpcode::DsReadU16D16: operand.sdwaSel = 4u; break; + case RdnaOpcode::BufferLoadUbyteD16Hi: + case RdnaOpcode::BufferLoadSbyteD16Hi: + case RdnaOpcode::BufferLoadShortD16Hi: + case RdnaOpcode::BufferStoreByteD16Hi: + case RdnaOpcode::BufferStoreShortD16Hi: + case RdnaOpcode::FlatLoadUbyteD16Hi: + case RdnaOpcode::FlatLoadSbyteD16Hi: + case RdnaOpcode::FlatLoadShortD16Hi: + case RdnaOpcode::FlatStoreByteD16Hi: + case RdnaOpcode::FlatStoreShortD16Hi: + case RdnaOpcode::DsReadU8D16Hi: + case RdnaOpcode::DsReadI8D16Hi: + case RdnaOpcode::DsReadU16D16Hi: + case RdnaOpcode::DsWriteB8D16Hi: + case RdnaOpcode::DsWriteB16D16Hi: operand.sdwaSel = 5u; break; + default: break; + } + return operand; +} + struct MemoryOpcodeInfo { std::uint32_t encoding; RdnaOpcode opcode; @@ -54,6 +85,14 @@ constexpr MemoryOpcodeInfo mubufOpcodes[] = { {0x1du, RdnaOpcode::BufferStoreDwordx2, 2, 32, false, false, false}, {0x1eu, RdnaOpcode::BufferStoreDwordx4, 4, 32, false, false, false}, {0x1fu, RdnaOpcode::BufferStoreDwordx3, 3, 32, false, false, false}, + {0x20u, RdnaOpcode::BufferLoadUbyteD16, 1, 8, false, false, false}, + {0x21u, RdnaOpcode::BufferLoadUbyteD16Hi, 1, 8, false, false, false}, + {0x22u, RdnaOpcode::BufferLoadSbyteD16, 1, 8, true, false, false}, + {0x23u, RdnaOpcode::BufferLoadSbyteD16Hi, 1, 8, true, false, false}, + {0x24u, RdnaOpcode::BufferLoadShortD16, 1, 16, false, false, false}, + {0x25u, RdnaOpcode::BufferLoadShortD16Hi, 1, 16, false, false, false}, + {0x19u, RdnaOpcode::BufferStoreByteD16Hi, 1, 8, false, false, false}, + {0x1bu, RdnaOpcode::BufferStoreShortD16Hi, 1, 16, false, false, false}, {0x30u, RdnaOpcode::BufferAtomicSwap, 1, 32, false, false, false}, {0x31u, RdnaOpcode::BufferAtomicCmpswap, 1, 32, false, false, false}, {0x32u, RdnaOpcode::BufferAtomicAdd, 1, 32, false, false, false}, @@ -97,6 +136,14 @@ constexpr MemoryOpcodeInfo flatOpcodes[] = { {0x1du, RdnaOpcode::FlatStoreDwordx2, 2, 32, false, false, false}, {0x1eu, RdnaOpcode::FlatStoreDwordx4, 4, 32, false, false, false}, {0x1fu, RdnaOpcode::FlatStoreDwordx3, 3, 32, false, false, false}, + {0x20u, RdnaOpcode::FlatLoadUbyteD16, 1, 8, false, false, false}, + {0x21u, RdnaOpcode::FlatLoadUbyteD16Hi, 1, 8, false, false, false}, + {0x22u, RdnaOpcode::FlatLoadSbyteD16, 1, 8, true, false, false}, + {0x23u, RdnaOpcode::FlatLoadSbyteD16Hi, 1, 8, true, false, false}, + {0x24u, RdnaOpcode::FlatLoadShortD16, 1, 16, false, false, false}, + {0x25u, RdnaOpcode::FlatLoadShortD16Hi, 1, 16, false, false, false}, + {0x19u, RdnaOpcode::FlatStoreByteD16Hi, 1, 8, false, false, false}, + {0x1bu, RdnaOpcode::FlatStoreShortD16Hi, 1, 16, false, false, false}, }; constexpr MemoryOpcodeInfo dsOpcodes[] = { @@ -160,7 +207,12 @@ constexpr MemoryOpcodeInfo dsOpcodes[] = { {0x76u, RdnaOpcode::DsReadB64, 2, 32, false, false, false}, {0x77u, RdnaOpcode::DsRead2B64, 4, 32, false, false, false}, {0x78u, RdnaOpcode::DsRead2st64B64, 4, 32, false, false, false}, + {0xa0u, RdnaOpcode::DsWriteB8D16Hi, 1, 8, false, false, false}, {0xa1u, RdnaOpcode::DsWriteB16D16Hi, 1, 16, false, false, false}, + {0xa2u, RdnaOpcode::DsReadU8D16, 1, 8, false, false, false}, + {0xa3u, RdnaOpcode::DsReadU8D16Hi, 1, 8, false, false, false}, + {0xa4u, RdnaOpcode::DsReadI8D16, 1, 8, true, false, false}, + {0xa5u, RdnaOpcode::DsReadI8D16Hi, 1, 8, true, false, false}, {0xa6u, RdnaOpcode::DsReadU16D16, 1, 16, false, false, false}, {0xa7u, RdnaOpcode::DsReadU16D16Hi, 1, 16, false, false, false}, {0xb0u, RdnaOpcode::DsWriteAddtidB32, 1, 32, false, false, false}, @@ -282,6 +334,7 @@ bool isDsWriteOpcode(RdnaOpcode opcode) { case RdnaOpcode::DsWriteB8: case RdnaOpcode::DsWriteB16: case RdnaOpcode::DsWriteB16D16Hi: + case RdnaOpcode::DsWriteB8D16Hi: case RdnaOpcode::DsWrite2B32: case RdnaOpcode::DsWrite2st64B32: case RdnaOpcode::DsWrite2B64: @@ -358,6 +411,8 @@ bool isFlatStoreOpcode(RdnaOpcode opcode) { switch (opcode) { case RdnaOpcode::FlatStoreByte: case RdnaOpcode::FlatStoreShort: + case RdnaOpcode::FlatStoreByteD16Hi: + case RdnaOpcode::FlatStoreShortD16Hi: case RdnaOpcode::FlatStoreDword: case RdnaOpcode::FlatStoreDwordx2: case RdnaOpcode::FlatStoreDwordx3: @@ -503,7 +558,7 @@ RdnaInstruction DecodeRdnaMubuf(std::uint32_t programCounter, std::span