From 67e7a3029cb52fb1faec1b23bdc8c70bfbcce233 Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Tue, 13 Oct 2026 05:23:21 +0200 Subject: [PATCH] fix(agc): decode primitive type 3 as a line strip DecodeState mapped points, lists, strips, fans, rect lists and patches but threw "unsupported primitive type 3" for VGT_PRIMITIVE_TYPE 3, the line strip (DI_PT_LINESTRIP in Mesa's r600d.h). Record of Lodoss War (PPSA05347) stops on it after a few minutes. Vulkan allows primitive restart for line strips without the list restart extension, and the precheck, the pipeline and the provoking vertex check already treat the strip class that way; only the decode lacked the case. The point and line depth bias enable stays rejected on line strips like on line lists, and a line strip gets the line primitive class for a pixel shader that reads the ancillary word. Co-Authored-By: Claude Sonnet 5.5 --- .../libSceAgcDriver/Graphics/src/State.cpp | 6 +++-- .../prx/libSceAgcDriver/tests/Graphics.cpp | 22 ++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp index dd8f4a511..49b885197 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp @@ -677,6 +677,7 @@ State DecodeState(const QueueState& queue, bool vertexWorkgroup) { switch (primitive) { case 1: Require(result.stages.mesh.has_value(), "point-list vertex rendering requires point-size output support"); result.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST; break; case 2: result.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST; break; + case 3: result.topology = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; break; case 7: case 17: Require(result.stages.path == ShaderPath::Vertex, "rect-list requires vertex routing"); @@ -751,7 +752,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; + 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 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")); @@ -956,7 +957,8 @@ ShaderRecompiler::RasterPrimitive RasterPrimitiveOf(const State& state) { 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; - case VK_PRIMITIVE_TOPOLOGY_LINE_LIST: return ShaderRecompiler::RasterPrimitive::Line; + case VK_PRIMITIVE_TOPOLOGY_LINE_LIST: + case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP: return ShaderRecompiler::RasterPrimitive::Line; default: return ShaderRecompiler::RasterPrimitive::Triangle; } } diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index fcc2e665c..5a11d9a3d 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -1578,6 +1578,8 @@ void DepthBoundsBiasTests() { queue.context[0x80] = 0; queue.userConfig[0x242] = 2; expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "polygon mode, depth bias or nonstandard rasterization"); + queue.userConfig[0x242] = 3; + expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "polygon mode, depth bias or nonstandard rasterization"); queue.userConfig[0x242] = 4; queue.context[0x205] = 0x00001a4au; queue.context[0x2de] = 0x1f0u; @@ -4124,6 +4126,23 @@ void aliasedBufferViewTests() { } } +void LineStripTests() { + using namespace AgcDriver::Graphics; + auto queue = makeState(); + queue.context[0x1b3] = 2; + queue.context[0x1b4] = 2; + queue.userConfig[0x242] = 3; + auto state = DecodeState(queue); + Require(state.topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP && !state.rectList && !state.primitiveRestart, "primitive type 3 did not decode as a line strip"); + Require(RasterPrimitiveOf(state) == ShaderRecompiler::RasterPrimitive::Line, "a line strip draw did not get the line primitive class"); + queue.userConfig[0x24b] = 1; + queue.context[0x103] = 0xffffffffu; + state = DecodeState(queue); + Require(state.topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP && state.primitiveRestart, "primitive restart was lost on a line strip"); + const auto rejection = DrawRejection(queue, true); + Require(rejection.empty(), "the draw precheck rejected an indexed line strip with primitive restart: " + rejection); +} + void ProvokingVertexTests() { using namespace AgcDriver::Graphics; constexpr std::uint32_t lastVertex = 1u << 19u; @@ -4132,7 +4151,7 @@ void ProvokingVertexTests() { queue.context[0x1b4] = 2; const auto first = DecodeState(queue); Require(first.provokingVertexMode == VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT, "the default provoking vertex is not first"); - for (const auto primitive : {2u, 4u, 5u, 6u}) { + for (const auto primitive : {2u, 3u, 4u, 5u, 6u}) { queue.userConfig[0x242] = primitive; for (const auto last : {false, true, false}) { queue.context[0x205] = 0x240u | (last ? lastVertex : 0u); @@ -5173,6 +5192,7 @@ int main(int argc, char** argv) { orderedPixelShaderTests(); stencilRefExportTests(); aliasedBufferViewTests(); + LineStripTests(); ProvokingVertexTests(); ProvokingVertexRenderPassTests(); ConservativeRasterizationTests();