Skip to content

feat(agc): draw line-strip output of NGG geometry programs as mesh lines - #5884

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
AnthonyClemens:fix/agc-geometry-line-strip-output
Oct 16, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
AnthonyClemens:fix/agc-geometry-line-strip-output

Conversation

@AnthonyClemens

Copy link
Copy Markdown
Contributor

What

A draw whose NGG geometry program outputs line strips (VGT_GS_OUT_PRIM_TYPE = 1) is refused on main with unsupported geometry input or output assembly. Resident Evil 2 (PPSA04288) hits this with point-list input and VGT_GS_MAX_VERT_OUT = 5.

On GFX10 NGG the geometry program assembles the strip itself: each primitive export is one line whose two vertex indices are packed the same way as a triangle's three (ac_nir_lower_ngg_gs.c, ac_nir_pack_ngg_prim_exp_arg). So the host only has to change the output topology:

  • DecodeShaderStages accepts VGT_GS_OUT_PRIM_TYPE 1 from a geometry program (also with fast launch) and records a two-vertex output primitive in the mesh configuration. The value encoding is from Mesa's gfx10.json.
  • The mesh module declares OutputLinesEXT and a PrimitiveLineIndicesEXT array of two-component indices; validation and the raster class (Line) follow it.
  • Each input primitive gets VGT_GS_MAX_VERT_OUT - 1 lines, and a subgroup gets at least GE_MAX_OUTPUT_PER_SUBGROUP line slots, because Mesa's lowering exports one primitive (possibly null) per emitted vertex and RADV programs PRIM_AMP_FACTOR as vertices_out. Fast launch keeps taking the count from PRIM_AMP_FACTOR. Triangle strips keep their current budget.
  • Line output counts as lines in the polygon offset decode, as lines on the vertex path do.
  • The output size is part of the cache, draw, validation and pipeline keys, and the request serialization version becomes 26.

Triangle-strip geometry is unchanged: the 29 triangle mesh modules from agc_driver_mesh are byte-identical before and after. Points, rectangle lists, per-stream output types and line strips from a vertex program still throw. Line width and the console's line rules are not measured; docs/dev/debt/libSceAgcDriver.md records that and the primitive budget.

Overlap: this bumps the serialization version from 25 to 26, as #4706 also does. Whichever lands second moves to 27. It also sits near #5604 in DecodeShaderStages; neither depends on the other.

Tested

Windows 11, MinGW-w64 GCC 15.2.0, Release, AMD Radeon RX 9070 XT.

  • agc_driver_graphics: the title's register state decodes as geometry rasterized as lines, with 255 line slots for 51 points; the same draw with a triangle strip is unchanged; values 0, 3 and per-stream types still throw; a fast-launch line strip takes 64 lines from PRIM_AMP_FACTOR; triangle input with line output now refuses a triangle-only polygon offset.
  • agc_driver_mesh: a hand-written geometry program draws line strips from points, and pixels are checked on both segments, beside them, inside the strip's triangle and on its closing edge. On main it fails because the second line is drawn as a triangle.
  • agc_shader_memory and request_serializer: the output size round-trips at version 26 and changes the cache keys; version 27 is refused.
  • The new decode test fails on main with the title's message, and the mesh test fails on main as above.
  • The generated line module passes spirv-val --target-env vulkan1.3.
  • Full ctest: 1170 of 1171 pass, 10 expected skips. agc_driver_layered_volume_vertex_no_gpl fails on this GPU on main too.
  • PPSA04288, in a local tree with an earlier revision of this change and other open fixes, gets past its line-strip geometry draw and on to its menus. I have no screen that shows those particular lines.
  • The mesh test is skipped on llvmpipe, as the other mesh tests are, so CI does not run it.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Unblocks: none
  • GPUs tested: AMD Radeon RX 9070 XT, Adrenalin 26.9.1 (driver 32.0.31041.3013), Windows 11
  • AI-assisted: yes

@fdfawef324

Copy link
Copy Markdown
Contributor

Resident Evil 4 (PPSA07411), the RE2 engine sibling, stops on main at the same check this PR changes. Windows Server 2025, RTX 3060 12 GB, driver 591.86, MinGW GCC 15.2 Release.

Rebase notes, in case they help. All source files merged automatically except the request serializer:

request_serializer, agc_shader_memory, agc_driver_graphics and agc_driver_mesh pass on that rebase. Not tested: other GPUs, the frame contents (RE4's output is still black at this point on main), Linux.

AI-assisted: yes

@simonbronk

Copy link
Copy Markdown
Contributor

Tested on a second RE Engine title: Onimusha: Way of the Sword (PPSA27836). Windows 11, RTX 5070 (NVIDIA 617.42).

Base: main 0afa6a6 with the title's other PRs (#5907, #5776, #5770, #5703, #5841). Without this PR, the title stops right after New Game:

AGC graphics: VGT_SHADER_STAGES_EN=0x2030: unsupported geometry input or output assembly

With this PR that draw passes, and the scene load goes on to a different, unrelated stop (a depth target with fewer samples than the rasterizer, handled by #5915 and #5907).

The merge onto current main conflicts in RequestSerializer.cpp, because main already uses serialization versions 26 to 29. I resolved it locally by moving this PR's outputPrimitiveSize read to version >= 30u and writing version 30. The test conflicts (ShaderMemory.cpp, RequestSerializerTests.cpp) were left on main's side for the title run, so I didn't run the PR's own tests.

A geometry program with VGT_GS_OUT_PRIM_TYPE 1 was refused as an
unsupported output assembly. Each primitive export of an NGG line-strip
program is one line whose indices are packed like a triangle's, so the
mesh module now declares OutputLinesEXT with PrimitiveLineIndicesEXT for
it. A subgroup gets VGT_GS_MAX_VERT_OUT - 1 lines per input primitive and
at least GE_MAX_OUTPUT_PER_SUBGROUP line slots; fast launch keeps using
PRIM_AMP_FACTOR. The output size is part of the cache, draw, validation
and pipeline keys, and the request serialization version becomes 31.
Triangle-strip geometry is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@adriadam10

Copy link
Copy Markdown
Collaborator

Checked on the PR merged onto current main: agc_driver_graphics, agc_driver_mesh (runs on RADV, pixels on both segments), agc_shader_memory and the serializer tests pass, and the new tests don't build without the change. Triangle-strip output, points, per-stream types and vertex-program line strips are unchanged and still throw. Line width and the console's line rules are recorded in the debt entry. Merging once CI is green.

@adriadam10
adriadam10 merged commit 645644c into boykopovar:main Oct 16, 2026
8 checks passed
milescoviello added a commit to milescoviello/AnyPS5 that referenced this pull request Oct 16, 2026
The layout guards only build on Linux with libstdc++, so a Windows or
macOS build of main after boykopovar#5884 wrote journals whose mesh records lack
outputPrimitiveSize under the new shader-cache source version. The
journal format is now 3, so such a file is discarded instead of being
read with the new codec. MarcZX100 pointed this out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
adriadam10 pushed a commit that referenced this pull request Oct 16, 2026
#5884 added MeshConfiguration::outputPrimitiveSize, and #5875's pipeline
journal guards the layouts it encodes. The two merged separately, so on
Linux with libstdc++ PipelineJournal.cpp no longer compiles:
MeshConfiguration is 40 bytes, ShaderStages (which holds an optional
one) 112. The guards now name those sizes, and the recipe codec writes
and reads the new field, so a replayed line-strip geometry pipeline
keeps its two-vertex primitives.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
milescoviello added a commit to milescoviello/AnyPS5 that referenced this pull request Oct 16, 2026
boykopovar#5884 took request version 31 on main for the mesh output primitive
size, so a version 31 request from main has no point-list tail. The
point fields now come with version 32, and the tests expect 32 and
reject 33.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
milescoviello added a commit to milescoviello/AnyPS5 that referenced this pull request Oct 16, 2026
boykopovar#5884 took request version 31 on main for the mesh output primitive
size, so a version 31 request from main has no point-list tail. The
point fields now come with version 32, and the tests expect 32 and
reject 33.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
oneandonlydean pushed a commit that referenced this pull request Oct 21, 2026
Brings #5884's request field (output primitive size, read from serialization version 31). This branch's sample order stays at version 32, read after it, so a version-31 request reads as before.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* docs shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants