Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions core/libs/prx/libSceAgcDriver/Graphics/src/ShaderValidation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ struct Module {
return;
}
// One gl_ClipDistance array holds the guest clip distances and the recompiler's all-zero position distance.
if (!input && ((value == spv::BuiltInClipDistance && (vertex || stage == Stage::TessellationEvaluation)) || (value == spv::BuiltInCullDistance && vertex))) {
if (!input && ((value == spv::BuiltInClipDistance && (vertex || stage == Stage::TessellationEvaluation || stage == Stage::Mesh)) || (value == spv::BuiltInCullDistance && (vertex || stage == Stage::Mesh)))) {
const auto signature = Signature(type);
auto& count = value == spv::BuiltInClipDistance ? clipDistances : cullDistances;
Require(count == 0 && signature.size() == 6 && signature.starts_with("f32[") && signature[4] >= '1' && signature[4] <= '8' && signature[5] == ']', "invalid or duplicate clip or cull distance output");
Expand Down Expand Up @@ -250,10 +250,10 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi
fragment &&
capability == spv::CapabilityStencilExportEXT;

if ((vertex || evaluation) && (capability == spv::CapabilityClipDistance || capability == spv::CapabilityCullDistance)) {
if ((vertex || evaluation || mesh) && (capability == spv::CapabilityClipDistance || capability == spv::CapabilityCullDistance)) {
char word[16];
std::snprintf(word, sizeof(word), "0x%08x", state.paClVsOutCntl);
Require(capability == spv::CapabilityClipDistance ? shaderClipDistance : vertex && shaderCullDistance, std::string("vertex clip or cull distance needs shaderClipDistance or shaderCullDistance, which the device lacks (PA_CL_VS_OUT_CNTL=") + word + ")");
Require(capability == spv::CapabilityClipDistance ? shaderClipDistance : (vertex || mesh) && shaderCullDistance, std::string("vertex clip or cull distance needs shaderClipDistance or shaderCullDistance, which the device lacks (PA_CL_VS_OUT_CNTL=") + word + ")");
}

// Enabled unconditionally or by the device setup in VulkanDevice.
Expand All @@ -262,12 +262,12 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi
capability == spv::CapabilityImage1D ||
capability == spv::CapabilityImageGatherExtended ||
capability == spv::CapabilityImageQuery ||
((vertex || evaluation) && shaderClipDistance && capability == spv::CapabilityClipDistance) ||
((vertex || evaluation || mesh) && shaderClipDistance && capability == spv::CapabilityClipDistance) ||
(fragment && geometryShader && capability == spv::CapabilityGeometry) ||
(fragment && sampleRateShading && capability == spv::CapabilitySampleRateShading) ||
capability == spv::CapabilityImageMSArray ||
capability == spv::CapabilityStorageImageMultisample ||
(vertex && shaderCullDistance && capability == spv::CapabilityCullDistance) ||
((vertex || mesh) && shaderCullDistance && capability == spv::CapabilityCullDistance) ||
capability == spv::CapabilityStorageImageWriteWithoutFormat ||
capability == spv::CapabilityStorageImageReadWithoutFormat ||
capability == spv::CapabilityInt64 ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,25 @@ alignas(256) constexpr std::array<std::uint32_t, 10> VertexCode{
0xf80000cf, 0x03020100, 0xf80008df, 0x07060504, 0xbf810000,
};

alignas(256) constexpr std::array<std::uint32_t, 106> MeshCode{
0x8f6a9003, 0x94fe6ac1, 0xbf88000b, 0xd7650006, 0x000100c1, 0xd7660006, 0x00020cc1, 0x93ebff03,
0x00040018, 0xd7460006, 0x04190c6b, 0x340c0c82, 0xd8340000, 0x00000506, 0xbf8cc07f, 0xbefe04c1,
0xbf8a0000, 0x938dff02, 0x00090016, 0x9382ff03, 0x00040018, 0x938cff03, 0x00080008, 0xd7650009,
0x000100c1, 0xd7660009, 0x000212c1, 0xd746000a, 0x04250c02, 0x7da8120c, 0xbf88002e, 0x361600ff,
0x0000ffff, 0x2c180090, 0x361a02ff, 0x0000ffff, 0xd8d80000, 0x0b00000b, 0xd8d80000, 0x0c00000c,
0xd8d80000, 0x0d00000d, 0xbf8cc07f, 0xe0382000, 0x8002100b, 0xe0382010, 0x8002140b, 0xe0382000,
0x8002180c, 0xe0382010, 0x80021c0c, 0xe0382000, 0x8002200d, 0xe0382010, 0x8002240d, 0x161c1483,
0x161e14ff, 0x00000060, 0xbf8c3f70, 0xdb7c0400, 0x0000100f, 0xdb7c0410, 0x0000140f, 0xdb7c0420,
0x0000180f, 0xdb7c0430, 0x00001c0f, 0xdb7c0440, 0x0000200f, 0xdb7c0450, 0x0000240f, 0x4a501c81,
0x4a521c82, 0x3450508a, 0x34525294, 0xd772002a, 0x04a6510e, 0xbf8cc07f, 0xbefe04c1, 0xbf8a0000,
0x930e830d, 0xbf078002, 0xbf850003, 0x8f0f8c0d, 0x887c0f0e, 0xbf900009, 0x7da8140d, 0xbf880002,
0xf8000941, 0x0000002a, 0xbefe04c1, 0x7da8140e, 0xbf88000c, 0x34561485, 0xdbfc0400, 0x2c00002b,
0xdbfc0410, 0x3000002b, 0xbf8cc07f, 0xf80000cf, 0x2f2e2d2c, 0xf80008df, 0x33323130, 0xf800020f,
0x33323130, 0xbf810000,
};

constexpr ShaderRecompiler::MeshConfiguration Subgroup{4u, 4u, 12u, 12u, 4u, 64u, 1024u, 0u, 4u};

alignas(256) constexpr std::array<std::uint32_t, 7> PixelCode{
0x7e0002f2, 0x7e0202f2, 0x7e0402f2, 0x7e0602f2, 0xf800080f, 0x03020100, 0xbf810000,
};
Expand Down Expand Up @@ -78,25 +97,29 @@ bool Expected(Visible visible, std::uint32_t pixel) {
return false;
}

void Draw(AgcDriver::VulkanDevice& device, std::uint32_t waveSize, const Row& row) {
void Draw(AgcDriver::VulkanDevice& device, std::uint32_t waveSize, const Row& row, bool mesh) {
Pixels.fill(std::byte{0});
std::array<Vertex, 3> vertices{};
for (std::size_t i = 0; i < vertices.size(); ++i) {
vertices[i] = {{PositionX[i], PositionY[i], 0.5f, 1.0f}, {row.component0[i], row.component1[i], row.component2[i], 0.0f}};
}

std::vector<std::uint32_t> vertexUserData(4, 0u);
std::vector<std::uint32_t> vertexUserData(mesh ? 12u : 4u, 0u);
const auto vertexBuffer = BufferDescriptor(vertices.data(), sizeof(Vertex), static_cast<std::uint32_t>(vertices.size()));
std::copy(vertexBuffer.begin(), vertexBuffer.end(), vertexUserData.begin());
const std::array<ShaderRecompiler::MemoryRegion, 1> vertexMemory{{{reinterpret_cast<std::uintptr_t>(VertexCode.data()), std::as_bytes(std::span(VertexCode))}}};
std::copy(vertexBuffer.begin(), vertexBuffer.end(), vertexUserData.begin() + (mesh ? 8 : 0));
const AgcDriver::Pm4::DrawParameters draw{0, static_cast<std::uint32_t>(vertices.size()), 0, 1, 0, false};
const std::span<const std::uint32_t> code = mesh ? std::span<const std::uint32_t>(MeshCode) : std::span<const std::uint32_t>(VertexCode);
const auto stage = mesh ? ShaderStage::Mesh : ShaderStage::Vertex;
const std::array<ShaderRecompiler::MemoryRegion, 1> vertexMemory{{{reinterpret_cast<std::uintptr_t>(code.data()), std::as_bytes(code)}}};
ShaderRecompiler::ShaderVertexStageInfo vertexInfo{};
vertexInfo.paClVsOutCntl = row.control;
ShaderRecompiler::RecompileRequest vertex{
{ShaderStage::Vertex, reinterpret_cast<std::uintptr_t>(VertexCode.data()), VertexCode, 0, {}},
{stage, reinterpret_cast<std::uintptr_t>(code.data()), code, 0, {}},
{waveSize, 0, vertexUserData, std::nullopt, std::nullopt, vertexInfo, vertexMemory},
device.Target(),
{0, 0, 0, 64}
{0, 0, 0, mesh ? ShaderRecompiler::MeshDrawPushOffsetBytes : 64u}
};
if (mesh) vertex.graphics = ShaderRecompiler::GraphicsCompileContext{0, {}, Subgroup, std::nullopt, {draw.indexAddress, draw.indexCount, draw.indexSize, draw.instanceCount}};
vertex.useCache = false;
const auto vertexResult = ShaderRecompiler::Recompile(vertex);
const auto vertexPush = static_cast<std::uint32_t>(vertexResult.pushConstants.size());
Expand All @@ -115,12 +138,13 @@ void Draw(AgcDriver::VulkanDevice& device, std::uint32_t waveSize, const Row& ro
fragment.useCache = false;
const auto pixelResult = ShaderRecompiler::Recompile(fragment);
const std::array<AgcDriver::Graphics::CompiledShader, 2> shaders{{
{ShaderStage::Vertex, &vertexResult, 0},
{stage, &vertexResult, 0},
{ShaderStage::Fragment, &pixelResult, PixelPushOffset(vertexPush, device.Target())}
}};

AgcDriver::Graphics::State state{};
state.stages = {AgcDriver::Graphics::ShaderPath::Vertex, 0u, waveSize, waveSize, std::nullopt, std::nullopt};
if (mesh) state.stages = {AgcDriver::Graphics::ShaderPath::Geometry, 0x20u, waveSize, waveSize, Subgroup, std::nullopt};
else state.stages = {AgcDriver::Graphics::ShaderPath::Vertex, 0u, waveSize, waveSize, std::nullopt, std::nullopt};
state.paClVsOutCntl = row.control;
state.color = {reinterpret_cast<std::uintptr_t>(Pixels.data()), {Width, Height}, VK_FORMAT_R8G8B8A8_UNORM, Pixels.size(), 0xe4u};
state.colors = {state.color};
Expand All @@ -135,13 +159,12 @@ void Draw(AgcDriver::VulkanDevice& device, std::uint32_t waveSize, const Row& ro
state.blend.colorWriteMask = 15;
state.blends = {state.blend};
state.blendConstants = {};
const AgcDriver::Pm4::DrawParameters draw{0, static_cast<std::uint32_t>(vertices.size()), 0, 1, 0, false};
device.Draw(state, draw, shaders);
device.WaitIdle();
}

void Check(const Row& row, std::uint32_t waveSize) {
const auto what = std::string("vertex clip and cull distances, ") + row.name + ", wave" + std::to_string(waveSize);
void Check(const Row& row, std::uint32_t waveSize, bool mesh) {
const auto what = std::string(mesh ? "mesh" : "vertex") + " clip and cull distances, " + row.name + ", wave" + std::to_string(waveSize);
for (std::uint32_t pixel = 0; pixel < Width * Height; ++pixel) {
const auto value = std::to_integer<std::uint8_t>(Pixels[pixel * 4u]);
Require(value == 0u || value == 255u, what + ": pixel " + std::to_string(pixel) + " stored " + std::to_string(value));
Expand All @@ -163,9 +186,17 @@ int main() {
}
for (const auto waveSize : {64u, 32u}) {
for (const auto& row : Rows) {
Draw(*device, waveSize, row);
Check(row, waveSize);
Draw(*device, waveSize, row, false);
Check(row, waveSize, false);
}
}
if (device->Target().mesh.has_value()) {
for (const auto& row : Rows) {
Draw(*device, 64u, row, true);
Check(row, 64u, true);
}
} else {
std::puts("mesh case skipped, the device has no VK_EXT_mesh_shader");
}
std::puts("vertex clip and cull distance tests passed");
return 0;
Expand Down
50 changes: 45 additions & 5 deletions core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -410,16 +410,20 @@ void EmitAuxPositionExport(SpirvValueEmitContext& ctx, std::uint32_t data, const
state.module.AddFunction(spv::OpStore, state.pointSizeVariable, f32);
continue;
}
const auto storeDistance = [&](std::uint32_t variable, std::uint32_t index) {
const auto storeDistance = [&](StageOutputKind kind, std::uint32_t variable, std::uint32_t index) {
if (index == std::numeric_limits<std::uint32_t>::max()) {
return;
}
if (state.program.Resources().stage == IrShaderStage::Mesh) {
state.module.AddFunction(spv::OpStore, MeshOutputPointer(state, kind, index), f32);
return;
}
const auto pointer = state.module.AllocateId();
state.module.AddFunction(spv::OpAccessChain, TypePointer(state, spv::StorageClassOutput, TypeF32(state)), pointer, variable, ConstantU32(state, index));
state.module.AddFunction(spv::OpStore, pointer, f32);
};
storeDistance(state.clipDistanceVariable, output.clipDistance);
storeDistance(state.cullDistanceVariable, output.cullDistance);
storeDistance(StageOutputKind::ClipDistance, state.clipDistanceVariable, output.clipDistance);
storeDistance(StageOutputKind::CullDistance, state.cullDistanceVariable, output.cullDistance);
}
}

Expand Down Expand Up @@ -500,8 +504,21 @@ std::uint32_t MeshLoad(SpirvEmitterState& state, std::uint32_t variable, std::ui
return value;
}

bool IsMeshDistance(StageOutputKind kind) {
return kind == StageOutputKind::ClipDistance || kind == StageOutputKind::CullDistance;
}

std::uint32_t MeshOutputType(SpirvEmitterState& state, StageOutputKind kind) {
return kind == StageOutputKind::Layer ? TypeU32(state) : TypeF32Vector(state, 4u);
if (kind == StageOutputKind::Layer) {
return TypeU32(state);
}
return IsMeshDistance(kind) ? TypeF32(state) : TypeF32Vector(state, 4u);
}

std::uint32_t MeshDistancePointer(SpirvEmitterState& state, const SpirvOutputBinding& output, std::uint32_t vertex) {
const auto pointer = state.module.AllocateId();
state.module.AddFunction(spv::OpAccessChain, TypePointer(state, spv::StorageClassOutput, TypeF32(state)), pointer, output.variableId, vertex, ConstantU32(state, output.index));
return pointer;
}

}
Expand Down Expand Up @@ -642,7 +659,30 @@ void DefineMeshOutputs(SpirvEmitterState& state) {
throw std::runtime_error("vertex input info is missing for mesh output definition");
}
const auto& mesh = state.inputInfo.vertex->mesh;
std::uint32_t clipDistanceCount = 0u;
std::uint32_t cullDistanceCount = 0u;
for (const auto& output : state.outputs) {
if (output.kind == StageOutputKind::ClipDistance) {
clipDistanceCount = std::max(clipDistanceCount, output.index + 1u);
} else if (output.kind == StageOutputKind::CullDistance) {
cullDistanceCount = std::max(cullDistanceCount, output.index + 1u);
}
}
for (auto& output : state.outputs) {
if (IsMeshDistance(output.kind)) {
const bool clip = output.kind == StageOutputKind::ClipDistance;
auto& variable = clip ? state.clipDistanceVariable : state.cullDistanceVariable;
if (variable == 0u) {
const auto distances = state.module.Type(spv::OpTypeArray, TypeF32(state), ConstantU32(state, clip ? clipDistanceCount : cullDistanceCount));
variable = MeshArray(state, spv::StorageClassOutput, distances, mesh.maxVertices);
state.interfaceVariables.push_back(variable);
state.module.AddName(variable, clip ? "gl_ClipDistance" : "gl_CullDistance");
state.module.AddAnnotation(spv::OpDecorate, variable, spv::DecorationBuiltIn, clip ? spv::BuiltInClipDistance : spv::BuiltInCullDistance);
}
output.variableId = variable;
output.meshDataVariable = MeshArray(state, spv::StorageClassPrivate, TypeF32(state), state.laneCount);
continue;
}
if (output.kind != StageOutputKind::Position && output.kind != StageOutputKind::Parameter && output.kind != StageOutputKind::Layer) {
throw std::runtime_error("unsupported mesh output kind");
}
Expand Down Expand Up @@ -727,7 +767,7 @@ void EmitMeshEntryPoint(SpirvEmitterState& state) {
}
const auto type = MeshOutputType(state, output.kind);
const auto value = MeshLoad(state, output.meshDataVariable, spv::StorageClassPrivate, type, ConstantU32(state, half));
const auto pointer = MeshElement(state, output.variableId, spv::StorageClassOutput, type, index);
const auto pointer = IsMeshDistance(output.kind) ? MeshDistancePointer(state, output, index) : MeshElement(state, output.variableId, spv::StorageClassOutput, type, index);
state.module.AddFunction(spv::OpStore, pointer, value);
}
});
Expand Down
Loading