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
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ void Driver::lookupDraw(const Submission& submission, const std::shared_ptr<Vulk
mix(graphics.stages.mesh.has_value());
if (graphics.stages.mesh) {
const auto& mesh = *graphics.stages.mesh;
for (const auto value : {mesh.inputPrimitive, mesh.primitivesPerGroup, mesh.verticesPerGroup, mesh.maxVertices, mesh.maxPrimitives, mesh.threadsPerGroup, mesh.ldsSizeDwords, mesh.provokingVertex, mesh.esgsItemSize}) mix(value);
for (const auto value : {mesh.inputPrimitive, mesh.primitivesPerGroup, mesh.verticesPerGroup, mesh.maxVertices, mesh.maxPrimitives, mesh.threadsPerGroup, mesh.ldsSizeDwords, mesh.provokingVertex, mesh.esgsItemSize, mesh.outputPrimitiveSize}) mix(value);
}
mix(graphics.stages.tessellation.has_value());
if (graphics.stages.tessellation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ bool Driver::sameDecode(const DrawDecode& a, const DrawDecode& b) {
const auto sameMesh = [](const std::optional<ShaderRecompiler::MeshConfiguration>& x, const std::optional<ShaderRecompiler::MeshConfiguration>& y) {
if (x.has_value() != y.has_value()) return false;
if (!x) return true;
return x->inputPrimitive == y->inputPrimitive && x->primitivesPerGroup == y->primitivesPerGroup && x->verticesPerGroup == y->verticesPerGroup && x->maxVertices == y->maxVertices && x->maxPrimitives == y->maxPrimitives && x->threadsPerGroup == y->threadsPerGroup && x->ldsSizeDwords == y->ldsSizeDwords && x->provokingVertex == y->provokingVertex && x->esgsItemSize == y->esgsItemSize;
return x->inputPrimitive == y->inputPrimitive && x->primitivesPerGroup == y->primitivesPerGroup && x->verticesPerGroup == y->verticesPerGroup && x->maxVertices == y->maxVertices && x->maxPrimitives == y->maxPrimitives && x->threadsPerGroup == y->threadsPerGroup && x->ldsSizeDwords == y->ldsSizeDwords && x->provokingVertex == y->provokingVertex && x->esgsItemSize == y->esgsItemSize && x->outputPrimitiveSize == y->outputPrimitiveSize;
};
const auto sameTess = [](const std::optional<ShaderRecompiler::TessellationConfiguration>& x, const std::optional<ShaderRecompiler::TessellationConfiguration>& y) {
if (x.has_value() != y.has_value()) return false;
Expand Down
1 change: 1 addition & 0 deletions core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,7 @@ bool ValidationKey(const Context& context, std::span<const CompiledShader> shade
add(mesh.threadsPerGroup);
add(mesh.ldsSizeDwords);
add(mesh.provokingVertex);
add(mesh.outputPrimitiveSize);
}
add(state.stages.tessellation.has_value());
if (state.stages.tessellation) {
Expand Down
1 change: 1 addition & 0 deletions core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,7 @@ void pipelineKey(std::vector<std::byte>& key, const Context& context, const Stat
append(key, mesh.threadsPerGroup);
append(key, mesh.ldsSizeDwords);
append(key, mesh.provokingVertex);
append(key, mesh.outputPrimitiveSize);
}
append(key, state.stages.tessellation.has_value());
if (state.stages.tessellation) {
Expand Down
13 changes: 7 additions & 6 deletions core/libs/prx/libSceAgcDriver/Graphics/src/ShaderValidation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ struct Module {
bool position = false;
std::uint32_t clipDistances = 0;
std::uint32_t cullDistances = 0;
bool primitiveIndices = false;
std::uint32_t primitiveIndices = 0;
bool fragmentBarycentric = false;
bool layerOutput = false;
std::map<std::uint32_t, std::vector<std::uint32_t>> modes;
Expand Down Expand Up @@ -134,8 +134,9 @@ struct Module {
Require(input && ((value == spv::BuiltInPosition && signature == "f32x4") || (value == spv::BuiltInTessCoord && stage == Stage::TessellationEvaluation && signature == "f32x3") || ((value == spv::BuiltInInvocationId || value == spv::BuiltInPrimitiveId || value == spv::BuiltInPatchVertices) && (signature == "i32" || signature == "u32"))), "unsupported tessellation built-in");
return;
}
if (!input && value == spv::BuiltInPrimitiveTriangleIndicesEXT) primitiveIndices = true;
Require((input && ((value == spv::BuiltInWorkgroupId || value == spv::BuiltInLocalInvocationId || value == spv::BuiltInGlobalInvocationId || value == spv::BuiltInNumWorkgroups) && signature == "u32x3")) || (input && value == spv::BuiltInLocalInvocationIndex && signature == "u32") || (!input && value == spv::BuiltInPrimitiveTriangleIndicesEXT && signature == "u32x3") || (!input && value == spv::BuiltInCullPrimitiveEXT && signature == "bool") || (!input && value == spv::BuiltInLayer && signature == "u32"), "unsupported mesh built-in");
if (!input && value == spv::BuiltInPrimitiveLineIndicesEXT) primitiveIndices = 2u;
if (!input && value == spv::BuiltInPrimitiveTriangleIndicesEXT) primitiveIndices = 3u;
Require((input && ((value == spv::BuiltInWorkgroupId || value == spv::BuiltInLocalInvocationId || value == spv::BuiltInGlobalInvocationId || value == spv::BuiltInNumWorkgroups) && signature == "u32x3")) || (input && value == spv::BuiltInLocalInvocationIndex && signature == "u32") || (!input && value == spv::BuiltInPrimitiveLineIndicesEXT && signature == "u32x2") || (!input && value == spv::BuiltInPrimitiveTriangleIndicesEXT && signature == "u32x3") || (!input && value == spv::BuiltInCullPrimitiveEXT && signature == "bool") || (!input && value == spv::BuiltInLayer && signature == "u32"), "unsupported mesh built-in");
return;
}
const auto signature = Signature(type);
Expand Down Expand Up @@ -437,7 +438,7 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi
mode(spv::ExecutionModeLocalSize, {paired ? config.threadsPerGroup / 2u : config.threadsPerGroup, 1, 1});
mode(spv::ExecutionModeOutputVertices, {config.maxVertices});
mode(spv::ExecutionModeOutputPrimitivesEXT, {config.maxPrimitives});
mode(spv::ExecutionModeOutputTrianglesEXT, {});
mode(config.outputPrimitiveSize == 2u ? spv::ExecutionModeOutputLinesEXT : spv::ExecutionModeOutputTrianglesEXT, {});
Require(module.modes.size() == 4, "unsupported mesh execution mode");
} else if (control) {
Require(state.rectList || state.stages.tessellation.has_value(), "tessellation configuration is missing");
Expand Down Expand Up @@ -486,7 +487,7 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi
const auto length = module.constants.find(outer[3]);
Require(length != module.constants.end() && length->second != 0, "invalid per-vertex interface array length");
if (mesh) {
const bool primitive = decoration.perPrimitive || (decoration.builtin && (*decoration.builtin == spv::BuiltInPrimitiveTriangleIndicesEXT || *decoration.builtin == spv::BuiltInCullPrimitiveEXT));
const bool primitive = decoration.perPrimitive || (decoration.builtin && (*decoration.builtin == spv::BuiltInPrimitiveLineIndicesEXT || *decoration.builtin == spv::BuiltInPrimitiveTriangleIndicesEXT || *decoration.builtin == spv::BuiltInCullPrimitiveEXT));
Require(length->second == (primitive ? state.stages.mesh->maxPrimitives : state.stages.mesh->maxVertices), "mesh interface array disagrees with output limits");
} else if (geometry) {
Require(length->second == 3u, "geometry interface array must hold the three vertices of a triangle");
Expand Down Expand Up @@ -594,7 +595,7 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi
Require(descriptors.size() == shader.bindings.size(), "recompiler binding metadata contains undeclared resources");
Require(push == (!shader.pushConstants.empty() || mesh || (control && !rectListControl)), "recompiler push constant metadata disagrees with SPIR-V");
for (const auto id : module.interface) Require(module.variables.contains(id), "entry point interface contains an unknown variable");
if (mesh) Require(module.primitiveIndices, "mesh shader does not export primitive indices");
if (mesh) Require(module.primitiveIndices == state.stages.mesh->outputPrimitiveSize, "mesh shader does not export primitive indices");
if (stage == Stage::Vertex || mesh || evaluation || geometry) Require(module.position, "vertex shader does not export position");
return module;
}
Expand Down
14 changes: 9 additions & 5 deletions core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -876,7 +876,10 @@ ShaderStages DecodeShaderStages(const QueueState& queue, bool vertexWorkgroup) {
validate(!fastLaunch || (!vertexWorkgroup && primitive == 1 && vertices == 1 && primitives == 1), "fast launch other than one point per subgroup is unsupported");
const auto maxVertices = read(queue.context, 0x1ff);
const auto verticesPerPrimitive = vertexWorkgroup ? 3u : read(queue.context, 0x2ce);
validate((primitive == 1 || primitive == 2 || primitive == 4 || primitive == 5 || primitive == 6) && read(queue.context, 0x29b) == 2 && verticesPerPrimitive >= 3, "unsupported geometry input or output assembly");
const auto outputType = read(queue.context, 0x29b);
validate(outputType == 2 || (outputType == 1 && !vertexWorkgroup), "VGT_GS_OUT_PRIM_TYPE other than a triangle strip, or a line strip from a geometry program, is unsupported");
const auto outputSize = outputType == 1 ? 2u : 3u;
validate((primitive == 1 || primitive == 2 || primitive == 4 || primitive == 5 || primitive == 6) && verticesPerPrimitive >= outputSize, "unsupported geometry input or output assembly");
const auto inputSize = primitive == 1 ? 1u : primitive == 2 ? 2u : 3u;
validate(vertices >= inputSize && maxVertices != 0 && (!vertexWorkgroup || maxVertices >= inputSize) && maxVertices <= 256 && verticesPerPrimitive <= 256, "invalid geometry subgroup output");
const auto inputStep = primitive == 5 || primitive == 6 ? 1u : inputSize;
Expand All @@ -886,11 +889,11 @@ ShaderStages DecodeShaderStages(const QueueState& queue, bool vertexWorkgroup) {
const auto resources = read(queue.shader, 0x8b, RegisterBank::Shader);
const auto esgsItemSize = read(queue.context, 0x2ab);
validate(esgsItemSize != 0 && esgsItemSize * vertices <= 0xffffu, "invalid VGT_ESGS_RING_ITEMSIZE");
const auto amplification = fastLaunch ? read(queue.context, 0x2d3) & 0x1ffu : verticesPerPrimitive - 2u;
const auto amplification = fastLaunch ? read(queue.context, 0x2d3) & 0x1ffu : verticesPerPrimitive - outputSize + 1u;
validate(amplification != 0, "fast launch subgroup amplifies to no primitives");
const auto maxPrimitives = primitives * amplification;
const auto maxPrimitives = outputSize == 2u && !fastLaunch ? std::max(primitives * amplification, maxVertices) : primitives * amplification;
const auto threads = std::max({(groupPrimitives - 1u) * inputStep + inputSize, primitives, maxVertices, maxPrimitives});
result.mesh = ShaderRecompiler::MeshConfiguration{primitive, groupPrimitives, (groupPrimitives - 1u) * inputStep + inputSize, maxVertices, maxPrimitives, ((threads + result.vertexWaveSize - 1u) / result.vertexWaveSize) * result.vertexWaveSize, ((resources >> 19u) & 0xffu) * 128u, 0, esgsItemSize};
result.mesh = ShaderRecompiler::MeshConfiguration{primitive, groupPrimitives, (groupPrimitives - 1u) * inputStep + inputSize, maxVertices, maxPrimitives, ((threads + result.vertexWaveSize - 1u) / result.vertexWaveSize) * result.vertexWaveSize, ((resources >> 19u) & 0xffu) * 128u, 0, esgsItemSize, outputSize};
}
return result;
}
Expand Down Expand Up @@ -1054,7 +1057,7 @@ State DecodeState(const QueueState& queue, bool vertexWorkgroup) {
const auto raster = read(cx, 0x205);
// Bits 5-10 give the front/back polygon type (2 = filled triangles), which POLY_MODE (bit 3) turns on
// explicitly; KEEP_TOGETHER_ENABLE (bit 24) only affects primitive distribution across the chip.
const bool pointsOrLines = result.topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST || result.topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST || result.topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
const bool pointsOrLines = result.topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST || result.topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST || result.topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP || (result.stages.mesh.has_value() && result.stages.mesh->outputPrimitiveSize == 2u);
const auto lineOffset = (raster & 0x1800u) != 0 && !pointsOrLines ? 0x2000u : 0u;
const auto rasterMode = raster & ~0x7u & ~(1u << 24u) & ~0x1800u & ~lineOffset & ~(1u << 19u) & ~(1u << 16u);
if (rasterMode != 0 && rasterMode != 0x240u && rasterMode != 0x248u) throw std::runtime_error("AGC graphics: " + zeroMessage(0x205, raster, "polygon mode, depth bias or nonstandard rasterization"));
Expand Down Expand Up @@ -1320,6 +1323,7 @@ std::array<std::uint16_t, 4> QuadSampleMasks(const State& state) {

ShaderRecompiler::RasterPrimitive RasterPrimitiveOf(const State& state) {
if (state.rectList) return ShaderRecompiler::RasterPrimitive::Rectangle;
if (state.stages.mesh.has_value() && state.stages.mesh->outputPrimitiveSize == 2u) return ShaderRecompiler::RasterPrimitive::Line;
if (state.stages.mesh.has_value() || state.stages.tessellation.has_value()) return ShaderRecompiler::RasterPrimitive::Triangle;
switch (state.topology) {
case VK_PRIMITIVE_TOPOLOGY_POINT_LIST: return ShaderRecompiler::RasterPrimitive::Point;
Expand Down
41 changes: 41 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,10 @@ void ShaderStageTests() {
Require(state.stages.path == AgcDriver::Graphics::ShaderPath::Geometry && state.stages.vertexWaveSize == 32 && state.stages.mesh, "a fast-launch draw did not decode as wave32 geometry");
const auto& mesh = *state.stages.mesh;
Require(mesh.inputPrimitive == 1 && mesh.primitivesPerGroup == 1 && mesh.verticesPerGroup == 1 && mesh.maxVertices == 32 && mesh.maxPrimitives == 64 && mesh.threadsPerGroup == 64 && mesh.ldsSizeDwords == 128, "a fast-launch subgroup did not decode as one point amplified by PRIM_AMP_FACTOR");
fast.context[0x29b] = 1;
const auto fastLines = AgcDriver::Graphics::DecodeState(fast);
Require(fastLines.stages.mesh->outputPrimitiveSize == 2 && fastLines.stages.mesh->maxPrimitives == 64 && AgcDriver::Graphics::RasterPrimitiveOf(fastLines) == ShaderRecompiler::RasterPrimitive::Line, "a fast-launch line strip did not take its line count from PRIM_AMP_FACTOR");
fast.context[0x29b] = 2;
fast.context[0x2d5] = 0x502030;
expectFailure([&] { AgcDriver::Graphics::DecodeState(fast); }, "unsupported geometry routing, fast launch or wave-ID state");
fast.context[0x2d5] = 0x482030;
Expand All @@ -828,6 +832,40 @@ void ShaderStageTests() {
fast.context[0x2d3] = 0;
expectFailure([&] { AgcDriver::Graphics::DecodeState(fast); }, "amplifies to no primitives");
}
{
auto lines = makeState();
lines.userConfig[0x242] = 1;
lines.context[0x2d5] = 0x2030;
lines.userConfig[0x25b] = 0x6633;
lines.context[0x1ff] = 255;
lines.context[0x2ce] = 5;
lines.context[0x29b] = 1;
lines.context[0x2ab] = 4;
lines.shader[0x8a] = 3u << 29u;
lines.shader[0x8b] = 3u << 16u;
auto state = AgcDriver::Graphics::DecodeState(lines);
Require(state.stages.path == AgcDriver::Graphics::ShaderPath::Geometry && state.stages.mesh && AgcDriver::Graphics::RasterPrimitiveOf(state) == ShaderRecompiler::RasterPrimitive::Line, "a geometry program that outputs line strips did not decode as geometry rasterized as lines");
auto mesh = *state.stages.mesh;
Require(mesh.inputPrimitive == 1 && mesh.primitivesPerGroup == 51 && mesh.verticesPerGroup == 51 && mesh.maxVertices == 255 && mesh.maxPrimitives == 255 && mesh.threadsPerGroup == 256 && mesh.outputPrimitiveSize == 2, "a line-strip subgroup did not decode as 51 points with a line slot per output vertex");
lines.context[0x29b] = 2;
state = AgcDriver::Graphics::DecodeState(lines);
mesh = *state.stages.mesh;
Require(mesh.maxPrimitives == 153 && mesh.threadsPerGroup == 256 && mesh.outputPrimitiveSize == 3 && AgcDriver::Graphics::RasterPrimitiveOf(state) == ShaderRecompiler::RasterPrimitive::Triangle, "the triangle-strip subgroup of the same draw changed");
lines.context[0x29b] = 1;
lines.context[0x2ce] = 1;
expectFailure([&] { AgcDriver::Graphics::DecodeState(lines); }, "unsupported geometry input or output assembly");
lines.context[0x2ce] = 5;
for (const auto output : {0u, 3u, 0x80000001u, 0x101u}) {
lines.context[0x29b] = output;
expectFailure([&] { AgcDriver::Graphics::DecodeState(lines); }, "VGT_GS_OUT_PRIM_TYPE other than a triangle strip, or a line strip from a geometry program, is unsupported");
}
lines.userConfig[0x242] = 4;
lines.context[0x205] = 0x2a40;
lines.context[0x29b] = 2;
Require(AgcDriver::Graphics::DecodeState(lines).stages.mesh->primitivesPerGroup == 17, "triangle input with a triangle-strip output and a triangle polygon offset did not decode");
lines.context[0x29b] = 1;
expectFailure([&] { AgcDriver::Graphics::DecodeState(lines); }, "polygon mode, depth bias or nonstandard rasterization");
}
queue.context[0x2ab] = 0;
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "invalid VGT_ESGS_RING_ITEMSIZE");
queue.context[0x2ab] = 4;
Expand Down Expand Up @@ -947,6 +985,9 @@ void NggVertexWorkgroupTests() {
queue.context[0x2d5] = stages;
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue, true); }, "requires PRIMGEN_EN without GS_EN, PRIMGEN_PASSTHRU_EN or tessellation");
}
queue.context[0x2d5] = 0x12010;
queue.context[0x29b] = 1;
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue, true); }, "VGT_GS_OUT_PRIM_TYPE other than a triangle strip, or a line strip from a geometry program, is unsupported");
}

constexpr std::array<std::uint8_t, 8> IdentityExports{0xe4u, 0xe4u, 0xe4u, 0xe4u, 0xe4u, 0xe4u, 0xe4u, 0xe4u};
Expand Down
Loading
Loading