fix(shader): allow fetch-shader calls at registration and test the fetched values
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
c48e01977d
commit
c3e16afddd
|
|
@ -314,8 +314,6 @@ std::vector<PreparedShaders::Entry> PrepareRegistered(const ShaderSnapshot& snap
|
|||
const auto codeOffset = static_cast<std::size_t>((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<ShaderRecompiler::ShaderComputeStageInfo> compute;
|
||||
std::optional<ShaderRecompiler::ShaderPixelStageInfo> pixel;
|
||||
std::optional<ShaderRecompiler::ShaderVertexStageInfo> vertex;
|
||||
|
|
@ -350,6 +348,9 @@ std::vector<PreparedShaders::Entry> PrepareRegistered(const ShaderSnapshot& snap
|
|||
}
|
||||
std::vector<std::uint32_t> 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<ShaderRecompiler::MemoryRegion, 2> 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;
|
||||
|
|
|
|||
|
|
@ -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 <array>
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<std::byte, Width * Height * 4> Pixels{};
|
||||
alignas(256) std::array<std::uint32_t, 12> Output{};
|
||||
|
||||
alignas(256) constexpr std::array<std::uint32_t, 7> VertexCode{
|
||||
0xbe822100, 0x34020a84, 0xe0781000, 0x80010801, 0xf80008cf, 0x0b0a0908, 0xbf810000,
|
||||
};
|
||||
|
||||
alignas(256) constexpr std::array<std::uint32_t, 4> PixelCode{
|
||||
0x7e0e02f2, 0xf800180f, 0x07070707, 0xbf810000,
|
||||
};
|
||||
|
||||
constexpr std::array<std::array<float, 4>, 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<std::uint32_t, 4> VertexBufferDescriptor(std::uint32_t select) {
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(Triangle.data());
|
||||
return {static_cast<std::uint32_t>(address), static_cast<std::uint32_t>((address >> 32u) & 0xffffu) | (16u << 16u), static_cast<std::uint32_t>(Triangle.size()), 0x0104d000u | select};
|
||||
}
|
||||
|
||||
std::array<std::uint32_t, 4> OutputDescriptor() {
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(Output.data());
|
||||
return {static_cast<std::uint32_t>(address), static_cast<std::uint32_t>((address >> 32u) & 0xffffu), static_cast<std::uint32_t>(sizeof(Output)), 0x31016facu};
|
||||
}
|
||||
|
||||
ShaderRecompiler::RecompileRequest VertexRequest(const AgcDriver::VulkanDevice& device, std::span<const std::uint32_t> userData, const ShaderRecompiler::ShaderVertexStageInfo& info, std::span<const ShaderRecompiler::MemoryRegion> memory) {
|
||||
ShaderRecompiler::RecompileRequest request{
|
||||
{ShaderStage::Vertex, reinterpret_cast<std::uintptr_t>(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<std::uint32_t> UserData() {
|
||||
std::vector<std::uint32_t> 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<ShaderRecompiler::MemoryRegion, 1> vertexMemory{{{reinterpret_cast<std::uintptr_t>(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<std::uint32_t>(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<std::uint32_t> pixelUserData(8, 0u);
|
||||
const std::array<ShaderRecompiler::MemoryRegion, 1> pixelMemory{{{reinterpret_cast<std::uintptr_t>(PixelCode.data()), std::as_bytes(std::span(PixelCode))}}};
|
||||
ShaderRecompiler::RecompileRequest fragment{
|
||||
{ShaderStage::Fragment, reinterpret_cast<std::uintptr_t>(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<AgcDriver::Graphics::CompiledShader, 2> 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<std::uintptr_t>(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<float>(Height), static_cast<float>(Width), -static_cast<float>(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<std::uint32_t>(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<std::uint32_t>(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<ShaderRecompiler::MemoryRegion, 1> memory{{{reinterpret_cast<std::uintptr_t>(VertexCode.data()), std::as_bytes(std::span(VertexCode))}}};
|
||||
try {
|
||||
static_cast<void>(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;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue