diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp index 2b50404e9..f899d1654 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp @@ -314,8 +314,6 @@ std::vector PrepareRegistered(const ShaderSnapshot& snap const auto codeOffset = static_cast((address - snapshot.codeAddress) / 4u); const auto code = std::span(snapshot.code).subspan(codeOffset); const auto decoded = ShaderRecompiler::RdnaInstructionDecoder{}.Decode(code); - auto graph = ShaderRecompiler::GraphBuilder{}.Build(decoded); - ShaderRecompiler::Structurizer{}.Structurize(graph); std::optional compute; std::optional pixel; std::optional vertex; @@ -350,6 +348,9 @@ std::vector PrepareRegistered(const ShaderSnapshot& snap } std::vector userData(userCount); if (stage != Stage::Compute && stage != Stage::Fragment && snapshot.type != 6) vertex = Graphics::DecodeVertexStageInfo(snapshot.header, snapshot.headerAddress, userData, nullptr, true); + const ShaderRecompiler::SwappcInfo swappc{vertex.has_value(), firstUser, userCount}; + auto graph = ShaderRecompiler::GraphBuilder{}.Build(decoded, &swappc); + ShaderRecompiler::Structurizer{}.Structurize(graph); const std::array memory{{{snapshot.codeAddress, std::as_bytes(std::span(snapshot.code))}, {snapshot.headerAddress, snapshot.header}}}; ShaderRecompiler::RecompileRequest request{{stage, address, code, snapshot.headerAddress, snapshot.header}, {wave, firstUser, userData, compute, pixel, vertex, memory}, stage == Stage::Compute ? device.ComputeTarget(wave) : device.Target(), {0, 0, 0, 128}, graphics}; if (graphics && graphics->mesh) request.layout.pushConstantSizeBytes = ShaderRecompiler::MeshDrawPushOffsetBytes; diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/ScalarSwappcFetch.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/ScalarSwappcFetch.cpp new file mode 100644 index 000000000..158fb8d50 --- /dev/null +++ b/core/libs/prx/libSceAgcDriver/tests/execution/ScalarSwappcFetch.cpp @@ -0,0 +1,168 @@ +#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 Width = 64; +constexpr std::uint32_t Height = 32; +constexpr std::uint32_t IdentitySelect = 0xfacu; +constexpr std::uint32_t ReverseSelect = 0x977u; +alignas(256) std::array Pixels{}; +alignas(256) std::array Output{}; + +alignas(256) constexpr std::array VertexCode{ + 0xbe822100, 0x34020a84, 0xe0781000, 0x80010801, 0xf80008cf, 0x0b0a0908, 0xbf810000, +}; + +alignas(256) constexpr std::array PixelCode{ + 0x7e0e02f2, 0xf800180f, 0x07070707, 0xbf810000, +}; + +constexpr std::array, 3> Triangle{{ + {-1.0f, -1.0f, 0.5f, 1.0f}, {3.0f, -1.0f, 0.25f, 1.0f}, {-1.0f, 3.0f, 0.75f, 1.0f} +}}; + +std::array VertexBufferDescriptor(std::uint32_t select) { + const auto address = reinterpret_cast(Triangle.data()); + return {static_cast(address), static_cast((address >> 32u) & 0xffffu) | (16u << 16u), static_cast(Triangle.size()), 0x0104d000u | select}; +} + +std::array OutputDescriptor() { + const auto address = reinterpret_cast(Output.data()); + return {static_cast(address), static_cast((address >> 32u) & 0xffffu), static_cast(sizeof(Output)), 0x31016facu}; +} + +ShaderRecompiler::RecompileRequest VertexRequest(const AgcDriver::VulkanDevice& device, std::span userData, const ShaderRecompiler::ShaderVertexStageInfo& info, std::span memory) { + ShaderRecompiler::RecompileRequest request{ + {ShaderStage::Vertex, reinterpret_cast(VertexCode.data()), VertexCode, 0, {}}, + {64u, 0, userData, std::nullopt, std::nullopt, info, memory}, + device.Target(), + {0, 0, 0, 64} + }; + request.useCache = false; + return request; +} + +ShaderRecompiler::ShaderVertexStageInfo VertexInfo(std::uint32_t select, std::int32_t registers) { + ShaderRecompiler::ShaderVertexStageInfo info{}; + info.resourcesNum = 1; + info.resources[0].fields = VertexBufferDescriptor(select); + info.resourcesDst[0] = {8, registers, 0, 0}; + info.fetchAttribReg = 8; + info.fetchBufferReg = 10; + info.fetchEmbedded = true; + return info; +} + +std::vector UserData() { + std::vector userData(12, 0u); + userData[0] = 0x10000000u; + userData[1] = 0x00000001u; + const auto output = OutputDescriptor(); + std::copy(output.begin(), output.end(), userData.begin() + 4); + return userData; +} + +void Draw(AgcDriver::VulkanDevice& device, std::uint32_t select) { + Pixels.fill(std::byte{0x40}); + Output.fill(0xdeadbeefu); + const auto userData = UserData(); + const std::array vertexMemory{{{reinterpret_cast(VertexCode.data()), std::as_bytes(std::span(VertexCode))}}}; + const auto vertexResult = ShaderRecompiler::Recompile(VertexRequest(device, userData, VertexInfo(select, 4), vertexMemory)); + const auto vertexPush = static_cast(vertexResult.pushConstants.size()); + + ShaderRecompiler::ShaderPixelStageInfo pixel{}; + pixel.inputAddr = ShaderRecompiler::PixelInputBit(ShaderRecompiler::PixelInput::PositionX) | ShaderRecompiler::PixelInputBit(ShaderRecompiler::PixelInput::PositionY); + pixel.posX = true; + pixel.posY = true; + pixel.targetOutputMode[0] = 9; + pixel.targetExportMapping.fill(0xe4u); + const std::vector pixelUserData(8, 0u); + const std::array pixelMemory{{{reinterpret_cast(PixelCode.data()), std::as_bytes(std::span(PixelCode))}}}; + ShaderRecompiler::RecompileRequest fragment{ + {ShaderStage::Fragment, reinterpret_cast(PixelCode.data()), PixelCode, 0, {}}, + {64u, 0, pixelUserData, std::nullopt, pixel, std::nullopt, pixelMemory}, + device.Target(), + {0, 0, vertexPush, 128 - vertexPush} + }; + fragment.useCache = false; + const auto pixelResult = ShaderRecompiler::Recompile(fragment); + const std::array shaders{{ + {ShaderStage::Vertex, &vertexResult, 0}, + {ShaderStage::Fragment, &pixelResult, vertexPush} + }}; + + AgcDriver::Graphics::State state{}; + state.stages = {AgcDriver::Graphics::ShaderPath::Vertex, 0u, 64u, 64u, std::nullopt, std::nullopt}; + state.color = {reinterpret_cast(Pixels.data()), {Width, Height}, VK_FORMAT_R8G8B8A8_UNORM, Pixels.size(), 0xe4u}; + state.colors = {state.color}; + state.hasColorTarget = true; + state.renderExtent = {Width, Height}; + state.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + state.viewport = {0, static_cast(Height), static_cast(Width), -static_cast(Height), 0, 1}; + state.negativeOneToOne = false; + state.scissor = {{0, 0}, {Width, Height}}; + state.cullMode = VK_CULL_MODE_NONE; + state.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + state.blend.colorWriteMask = 15; + state.blends = {state.blend}; + state.blendConstants = {}; + const AgcDriver::Pm4::DrawParameters draw{0, static_cast(Triangle.size()), 0, 1, 0, false}; + device.Draw(state, draw, shaders); + device.WaitIdle(); +} + +void Check(std::uint32_t select, const std::string& what) { + for (std::uint32_t vertex = 0; vertex < Triangle.size(); ++vertex) { + for (std::uint32_t component = 0; component < 4u; ++component) { + const auto source = select == ReverseSelect ? 3u - component : component; + const auto expected = std::bit_cast(Triangle[vertex][source]); + const auto actual = Output[vertex * 4u + component]; + Require(actual == expected, what + ": vertex " + std::to_string(vertex) + " v" + std::to_string(8u + component) + " is " + std::to_string(actual) + ", expected " + std::to_string(expected)); + } + } +} + +void CheckRegisterLimit(AgcDriver::VulkanDevice& device) { + const auto userData = UserData(); + const std::array memory{{{reinterpret_cast(VertexCode.data()), std::as_bytes(std::span(VertexCode))}}}; + try { + static_cast(ShaderRecompiler::Recompile(VertexRequest(device, userData, VertexInfo(IdentitySelect, 5), memory))); + } catch (const std::exception& error) { + Require(std::string(error.what()).find("the inline model supports one to four") != std::string::npos, std::string("fetch-shader register limit: unexpected error: ") + error.what()); + return; + } + throw std::runtime_error("fetch-shader register limit: five destination registers compiled"); +} + +} + +int main() { + try { + const auto device = OpenVulkanTestDevice(); + if (!device) return VulkanTestSkipped; + Draw(*device, IdentitySelect); + Check(IdentitySelect, "fetch-shader call, identity dst_sel"); + Draw(*device, ReverseSelect); + Check(ReverseSelect, "fetch-shader call, reversed dst_sel"); + CheckRegisterLimit(*device); + std::puts("scalar swappc fetch tests passed"); + return 0; + } catch (const std::exception& error) { + std::cerr << error.what() << '\n'; + return 1; + } +}