Skip to content

feat(agc): implement primitive-ordered pixel shaders with fragment shader interlock - #1130

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
Celegans12:feat/agc-ordered-pixel-shader
Oct 8, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
Celegans12:feat/agc-ordered-pixel-shader

Conversation

@Celegans12

@Celegans12 Celegans12 commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

What

DecodeState and the draw precheck rejected DB_SHADER_CONTROL bit 16, PRIMITIVE_ORDERED_PIXEL_SHADER (field), which orders the pixel shaders of overlapping primitives.

Tested

  • Windows 11, MinGW-w64 GCC 15.2.0, based on main 0e173a4: configure -DBUILD_TESTING=ON, full build, --target libs, ctest:
  • 424/424 tests pass (ctest --output-on-failure)
  • Same build on lavapipe (Mesa 26.2.4 llvmpipe, 8-wide subgroups, VK_DRIVER_FILES): 423/423 pass; agc_driver_flat_store is left out there because it takes several minutes on Windows llvmpipe
  • New orderedPixelShaderTests (agc_driver_graphics) also walks every path of an ordered shader with BDA reads and requires one OpEndInvocationInterlockEXT before each exit, 5 fault kills included; without the End in StopBdaInvocationIf it fails. It does not compile on main.
  • New GPU test agc_driver_pixel_interlock: 64 overlapping triangles add 1 to a dword per pixel; every pixel counts 64 on an RTX 3080 and lavapipe (2 on the RTX 3080 without the interlock); when a BDA read fault kills every 8th column, the others still count 64. It does not compile on main.
  • PPSA26344 v1.008: a 2,700 s run has no 0x30600/0x30640 skips (1,243 in 1,500 s before); those draws now stop at a null SRT read first.
  • Not tested: PPSA21564, Dreaming Sarah, Linux (CI), console behaviour.

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 in TechnicalDebt
  • 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
  • AI-assisted: yes

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
#1139 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp L67
#1252 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp L1887
#1383 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp L759-760, L764
#1442 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp L759-760, L764

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 7, 2026
@Celegans12

Copy link
Copy Markdown
Contributor Author

The linux job here didn't get to the build: it hung on apt-get update (the Ubuntu mirror) and was cancelled at the 30-minute limit. Windows, conventions and report pass, so a re-run of the linux job should be all it needs.

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked on current main (build + ctest 425/425, agc_driver_pixel_interlock runs on a device with fragmentShaderPixelInterlock; the new tests don't compile without the change). The linux job failed on a hung apt-get update; I re-ran it.

One thing before merging:

  • SPV_EXT_fragment_shader_interlock requires each invocation to dynamically execute OpBeginInvocationInterlockEXT and then OpEndInvocationInterlockEXT exactly once. An End before every OpReturn (SpirvFlowEmitter.cpp:91) is fine for that, but StopBdaInvocationIf (SpirvBdaFault.cpp:95) emits OpKill in pixel shaders between Begin and End when a BDA read faults (the four call sites in SpirvBdaRead.cpp), so that invocation leaves the critical section without End. Please emit End before that OpKill when the ordered mode is on, and add a test case where an ordered pixel shader takes the fault path.

Everything else (key/serializer v10, legacy request, TechnicalDebt placement) looks right. Note #423/#448/#1182/#1190 also take request version 10: whichever lands second moves to the next one.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 8, 2026
@Celegans12
Celegans12 force-pushed the feat/agc-ordered-pixel-shader branch from cbbe157 to 6a9fef5 Compare October 8, 2026 08:58
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 8, 2026
…ader interlock

DecodeState and the draw precheck rejected DB_SHADER_CONTROL bit 16,
PRIMITIVE_ORDERED_PIXEL_SHADER (POPS), which runs the ordered section
of overlapping pixels' shaders in primitive order. The bit now reaches
the recompiler as a pixel stage field, and such a pixel shader declares
PixelInterlockOrderedEXT and runs its whole body between
OpBeginInvocationInterlockEXT and OpEndInvocationInterlockEXT; an
invocation that a BDA read fault kills ends the interlock first. The
POPS collision SGPRs read 0, so a title skips its POPS wait and runs
its per-collision loop once. An image_load still reads a sampled
binding, which misses an earlier overlapping primitive's image_store in
the same draw. The device enables VK_EXT_fragment_shader_interlock when
it has fragmentShaderPixelInterlock; without it the recompile throws,
as does a nonzero POPS_OVERLAP_NUM_SAMPLES. The field is part of the
recompile cache key, the draw key and the serialized request (version 10).
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 8, 2026
@adriadam10
adriadam10 force-pushed the feat/agc-ordered-pixel-shader branch from 6a9fef5 to e1d4e69 Compare October 8, 2026 09:33

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main now uses request version 10 for the compute scratch size (#1182), so I rebased this onto it and moved the ordered-pixel field to version 11 (RequestSerializer.cpp write/read/limit, ShaderMemory.cpp: new prefix, unsupported versions 0 and 12, a legacy v10 request added, mapping offset unchanged at +163). Build + ctest 431/431 on Linux (RADV); the new tests don't compile without the change. The OpKill/End fix from the earlier review is in and the mutation without it fails.

Pushing the rebase now; merging once CI is green on the new head. The PR description still says "version 10": it is 11 now.

@Celegans12

Copy link
Copy Markdown
Contributor Author

Thanks for doing the rebase and the move to version 11 yourself, and for checking the mutation. I've updated the description: it now says version 11 and lists which of the other request-changing PRs still write 10. I'll move our #1485 and #1492, which also wrote 10, to 12 and 13 so they don't collide with this one.

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 8, 2026
@adriadam10
adriadam10 merged commit 3069680 into boykopovar:main Oct 8, 2026
5 checks passed
@Celegans12
Celegans12 deleted the feat/agc-ordered-pixel-shader branch October 8, 2026 10:06
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.

2 participants