Skip to content

fix(agc): draw a last provoking vertex that no pixel input sees without the extension - #6741

Open
DavidSamir wants to merge 14 commits into
boykopovar:mainfrom
DavidSamir:fix/agc-unobserved-last-provoking-vertex
Open

DavidSamir wants to merge 14 commits into
boykopovar:mainfrom
DavidSamir:fix/agc-unobserved-last-provoking-vertex

Conversation

@DavidSamir

@DavidSamir DavidSamir commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

What

A draw with PA_SU_SC_MODE_CNTL.PROVOKING_VTX_LAST throws on a device without VK_EXT_provoking_vertex, whatever its pixel shader reads. MoltenVK 1.4.2 on Apple M2 doesn't expose the extension (vulkaninfo lists neither it nor provokingVertexLast). Once #6535 clears its viewport-depth stop, ProsperoLichess (PPSA99009) stops here on macOS:

libc++abi: terminating due to uncaught exception of type std::runtime_error: AGC graphics: last provoking vertex requires VK_EXT_provoking_vertex with provokingVertexLast enabled

A temporary log line before the refusal showed the draw: a triangle fan on the vertex path whose pixel shader has no inputs (fragmentParameters empty). The provoking vertex selects the values of flat-shaded inputs and the order of per-vertex values. The driver records no transform feedback, so nothing else depends on it.

Without the extension, last mode now throws only when a pixel shader could see the provoking vertex:

  • a flat input, a flat P10 read, or a per-vertex or custom per-vertex input, as the recompiler records them;
  • a Flat or PerVertexKHR decoration in its SPIR-V, on a variable or a block member. This also catches a shader built without the recompiler's metadata, as in agc_driver_provoking_vertex. It skips a built-in the pixel stage generates itself: FragCoord, PointCoord, FrontFacing, SampleId, SamplePosition, SampleMask, HelperInvocation, SubgroupSize and SubgroupLocalInvocationId. VUID-StandaloneSpirv-Flat-04744 requires Flat on every integer pixel input, built-ins included, but no vertex supplies those. Layer, ViewportIndex, PrimitiveId and every other built-in still count;
  • a truncated module, or a pixel shader that was not compiled;
  • another stage whose SPIR-V decorates a Layer or ViewportIndex built-in output (variable or block member). Vulkan takes the layer and viewport from the provoking vertex whether or not the pixel shader reads anything. One also decorated PerPrimitiveEXT, the mesh path's per-primitive layer, is skipped, because it belongs to the primitive. The recompiler declares these outputs only when the guest exports them (PA_CL_VS_OUT_CNTL), so other vertex shaders are unaffected. A stage whose module can't be read also counts.

Otherwise ValidateProvokingVertex accepts the draw. The Pipeline constructor already leaves out the provoking vertex state on such a device, so it draws in first-vertex mode, as it already did for a draw without a pixel shader. Devices with the extension are unchanged. The PROVOKING_VTX_LAST debt entry says when last mode throws.

Commits:

  1. The fix, with the one existing check it reverses: a draw without shaders on a device without the extension now passes.
  2. The new test case.
  3. The debt entry.
  4. Skipping generated built-ins. Cloud Play and PS5 OpenGL Showcase showed it was needed: their only Flat decoration is on a BuiltIn SubgroupLocalInvocationId input.
  5. Its test cases.
  6. Its debt line.
  7. A merge of main.
  8. Review: the SPIR-V decoration scan moved into one helper, with no change in behaviour.
  9. Review: a stage that writes Layer or ViewportIndex observes the provoking vertex.
  10. Its test cases.
  11. Its debt line.

Tested

macOS 26.7.1, Apple M2, MoltenVK 1.4.2 from the LunarG Vulkan SDK 1.4.363, AppleClang 21.0.0, x86-64 under Rosetta, Release.

main doesn't build the system libraries on macOS by itself, so the tests ran in a local tree: main ce991f1 with #4627, #4630, #6124, the four macOS relinker PRs and mugurc's macOS port branches merged, and #6535 applied. This branch's Pipeline.cpp is identical to the one tested.

  • agc_driver_unobserved_provoking_vertex passes 5 runs of 5. It covers points, lines, line strips, triangle lists, strips and fans on a device without the extension:

    • no pixel shader, a pixel shader without inputs, and a smooth input only: these pass;
    • each input kind above, a decorated variable or block member, a truncated module, no module, and no compiled shader: these throw;
    • a vertex-stage program carrying such metadata and decorations beside a smooth pixel shader: this passes.

    It also reruns ProvokingVertexTests.

  • With main's Pipeline.cpp the new test fails. It also fails for each of 12 deliberately wrong versions of the fix, each dropping one of the conditions above or reading every stage's inputs.

  • agc_driver_provoking_vertex (GPU), run with the SDK's loader on the path (as plain ctest it skips: no libvulkan.1.dylib found), passes 3 runs of 3 on MoltenVK. It checks first-vertex rendering and that its flat GLSL shader is still refused in last mode. An earlier version of this branch read only the recompiler's metadata, drew that shader and failed there, which is why the SPIR-V check is in; the two SPIR-V variants among the 12 above fail it too.

  • All 597 agc_ tests in this tree, with this branch and with main's Pipeline.cpp:

    • agc_write_tracking and agc_shared_write fail with both;
    • agc_driver_graphics and the new test fail only with main's;
    • nothing fails only with this branch.
  • Cloud Play (PPSA99600, 1.0.0) and PS5 OpenGL Showcase (PPSA99005, 1.0.2), both with catalog sha256 checked, 40 s runs:

    • with commits 1 to 3, both stop on the refusal above. A log line showed the only match is OpDecorate %10 Flat on %10, a BuiltIn SubgroupLocalInvocationId 32-bit unsigned input. Cloud Play's draw is a triangle list with no recorded inputs; Showcase's is a triangle fan with one smooth input;
    • with this head, 2 runs of 2 each get past it. Cloud Play then stops on Barycentric geometry SPIR-V: geometry shaders are unavailable (Macos m4 #6222's area). Showcase stops on 64-bit float instructions need the Float64 capability, whose only 64-bit operation here is FPInterpolateF16.
  • The built-in cases fail for each of 4 more wrong versions: no built-in skipped, every built-in skipped, block members keyed as variables, and a built-in on one variable excusing another.

  • ProsperoLichess (PPSA99009, catalog release 01.000.000), 60 s runs:

  • tools/check_conventions.py --base origin/main and git diff --check pass.

After review (commits 7 to 11). Windows 11 Pro 26300, NVIDIA RTX 3070 (driver 610.60), WinLibs GCC 15.2.0, Release; and WSL Ubuntu 24.04 with Mesa lavapipe, on main 02fb1ce merged in.

  • New cases in agc_driver_unobserved_provoking_vertex, for vertex, tessellation-evaluation, geometry and mesh stages beside a smooth pixel shader on a device without the extension:

    • BuiltIn Layer or BuiltIn ViewportIndex on a variable or block member, including one where PerPrimitiveEXT is on a different member: these throw. With the extension they pass;
    • the same built-in also decorated PerPrimitiveEXT, in either order: these pass;
    • Position and PointSize only: these pass;
    • an unreadable module: this throws.

    The existing case, vertex-stage metadata and Flat beside a smooth pixel shader, still passes.

  • With commit 8 (no fix) and the new cases, the test fails with expected graphics rejection: last provoking vertex requires VK_EXT_provoking_vertex with provokingVertexLast enabled. With commit 9 it passes.

  • Each of commits 8, 9 and 10 builds, and agc_driver_unobserved_provoking_vertex, agc_driver_provoking_vertex, agc_driver_mesh_provoking_layer and agc_driver_mesh_provoking_vertex pass on the RTX 3070.

  • Validation (as AGENTS.md asks for AGC changes): ctest -R provoking -V on main and on this head, same machine, lavapipe ICD, Khronos validation layer 1.3.275, with ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation.

    • Both pass. The mesh tests skip on lavapipe; they pass on the RTX 3070 above.
    • Neither log contains was not found but was requested by env var VK_INSTANCE_LAYERS.
    • The VUIDs are identical: 9 x VUID-VkDeviceCreateInfo-pNext-pNext, the 1.3.275 layer not knowing a newer device-create struct. Nothing new from this change.

Commits 1 to 6 were not tested on Windows, Linux or devices with the extension. Their path is unchanged, since the new early return needs provokingVertexLast to be false. Commits 7 to 11 were tested on Windows and Linux, as below.

Limits of this Mac's GPU. vulkaninfo on Apple M2 with MoltenVK 1.4.2 reports no geometry shaders, no shaderFloat64, no VK_EXT_depth_range_unrestricted and no VK_EXT_provoking_vertex. AnyPS5 turns MoltenVK's VK_KHR_fragment_shader_barycentric off (5333a20), so barycentric pixel shaders need the geometry-shader fallback. These are limits of the device. Apart from the provoking-vertex case below, this PR doesn't remove them. A title named here as stopping on one of them is not tested past that stop:

Overlap: #6451 and #6683 register tests next to agc_driver_covered_centroid_state in CMakeLists.txt, as this does; whichever lands second rebases. The overlap check couldn't compare the pull requests that conflict with main. Of those, #1896, #5925 and #6222 edit Pipeline.cpp, and none of them changes ValidateProvokingVertex or the provoking vertex state. I found no open pull request that changes this check.

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. Lichess reaches this draw on macOS only with fix(agc): normalize unused viewport depth ranges #6535.
  • Unblocks: PPSA99009 ProsperoLichess (PPSA99009) #4982 (macOS; its next stop is feat(agc): remap a viewport depth range the device cannot express into the vertex shader #4650's case), PPSA99600 and PPSA99005 (macOS; their next stops are named above)
  • GPUs tested: Apple M2, MoltenVK 1.4.2 (LunarG Vulkan SDK 1.4.363), macOS 26.7.1; NVIDIA RTX 3070, driver 610.60, Windows 11 (commits 7 to 11); Mesa lavapipe, WSL Ubuntu 24.04 (validation)
  • AI-assisted: yes

…ut the extension

A draw with PA_SU_SC_MODE_CNTL.PROVOKING_VTX_LAST threw on a device
without VK_EXT_provoking_vertex, whatever its pixel shader reads. MoltenVK
1.4.2 on Apple M2 does not expose the extension, and ProsperoLichess
(PPSA99009) stops there on macOS:

  AGC graphics: last provoking vertex requires VK_EXT_provoking_vertex
  with provokingVertexLast enabled

The provoking vertex selects the values of flat-shaded inputs and the
order of per-vertex values, and nothing else the driver uses: no
transform feedback is recorded. The draw Lichess stops on is a triangle
fan whose pixel shader has no inputs at all.

Without the extension, last mode now throws only when a pixel shader
could see the provoking vertex: a flat input, a flat P10 read, or a
per-vertex or custom per-vertex input as the recompiler records them, a
Flat or PerVertexKHR decoration in its SPIR-V (a shader built without
that metadata, or a member of an input block), a module that is
truncated, or a pixel shader that was not compiled. Otherwise the
pipeline is created without the provoking vertex state, so the device
draws in first-vertex mode, as it already did for a draw without a pixel
shader. Devices with the extension are unchanged.

The existing check that a draw without shaders throws on such a device
now expects it to pass.
…nsion

agc_driver_unobserved_provoking_vertex runs, for points, lines, line
strips, triangle lists, strips and fans on a device without
VK_EXT_provoking_vertex:

- draws without a pixel shader, with a pixel shader without inputs and
  with only a smooth input, which pass;
- a pixel shader with a flat input, a flat P10 read, a per-vertex or a
  custom per-vertex input, a Flat or PerVertexKHR decoration on a
  variable or a block member, a truncated module, no module, or none
  compiled, which throw;
- a vertex-stage program carrying such metadata and decorations beside a
  smooth pixel shader, which passes, since only the pixel shader's
  inputs are read.

It also reruns the existing provoking vertex checks.
@github-actions

github-actions Bot commented Oct 16, 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
PR 6683 core/libs/prx/libSceAgcDriver/CMakeLists.txt L2909, L2913
PR 7883 docs/dev/debt/libSceAgcDriver.md L133
PR 7893 docs/dev/debt/libSceAgcDriver.md L133
PR 8538 core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp L113
docs/dev/debt/libSceAgcDriver.md L133

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 1368 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 1395 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 2434 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 4040 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 4448 docs/dev/debt/libSceAgcDriver.md
PR 4449 docs/dev/debt/libSceAgcDriver.md
PR 5021 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5058 docs/dev/debt/libSceAgcDriver.md
PR 5371 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5372 docs/dev/debt/libSceAgcDriver.md
PR 5431 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5441 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 5499 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 5609 core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
PR 5925 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6044 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6186 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6187 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6188 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6192 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6194 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6209 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6223 docs/dev/debt/libSceAgcDriver.md
PR 6235 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6280 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6300 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6412 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6481 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6501 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6539 core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
PR 6548 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6747 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6788 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6808 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6867 docs/dev/debt/libSceAgcDriver.md
PR 6868 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6946 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6947 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6970 core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6977 docs/dev/debt/libSceAgcDriver.md
PR 7048 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7201 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
PR 7223 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7256 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7410 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 7449 docs/dev/debt/libSceAgcDriver.md
PR 7505 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 7514 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7604 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 7642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7677 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7800 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 7831 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 8014 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 8246 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 8484 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8552 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8564 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 8592 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md

…rovoking vertex check

Without VK_EXT_provoking_vertex, a draw in last-vertex mode threw when its
pixel shader's SPIR-V had any Flat or PerVertexKHR decoration. Vulkan
requires Flat on integer pixel inputs, built-ins included, so a shader
reading gl_SubgroupInvocationID has one, although the pixel stage
generates that value itself and no vertex supplies it. Cloud Play
(PPSA99600, a triangle list with no inputs) and PS5 OpenGL Showcase
(PPSA99005, a triangle fan with one smooth input) stop there on macOS:
their only Flat decoration is on a BuiltIn SubgroupLocalInvocationId
input.

The check now reads BuiltIn decorations on variables and block members
too, and skips a Flat or PerVertexKHR decoration on FragCoord,
PointCoord, FrontFacing, SampleId, SamplePosition, SampleMask,
HelperInvocation, SubgroupSize or SubgroupLocalInvocationId. Every other
built-in, Layer, ViewportIndex and PrimitiveId among them, still counts
as seeing the provoking vertex.
agc_driver_unobserved_provoking_vertex now passes a Flat SampleId,
FrontFacing or SubgroupLocalInvocationId, as a variable in either
decoration order or as a block member, and still refuses a Flat input
next to an unrelated built-in, a Flat block member next to a built-in
member, and a Flat Layer, ViewportIndex or PrimitiveId.
@johnmaia

Copy link
Copy Markdown
Contributor

Windows run, since Windows is untested. Windows 11, WinLibs GCC 15.2.0 r7 (MinGW), Release, BUILD_TESTING=ON, ctest -j4 --timeout 300. Head 3e5785b merged into main 82ddae7, plus #6452 so the MinGW test build works. Both GPUs report VK_EXT_provoking_vertex, so this covers the "devices with the extension" case, not the new path without it.

AI-assisted: yes (Claude Code)

@johnmaia

Copy link
Copy Markdown
Contributor

Follow-up on the guest_shader_link timeout above: on the same UHD 770 it times out 4 of 15 runs on main 82ddae7 + #6452 (measured on #6503), so it's an existing Intel flake, not this PR.

AI-assisted: yes (Claude Code)

ycarogabriel-lgtm added a commit to ycarogabriel-lgtm/AnyPS5 that referenced this pull request Oct 17, 2026
… no pixel input sees without the extension (@DavidSamir)

Open upstream PR that adds macOS or Metal behaviour this branch did not have; merged so the fork carries the community's current macOS work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 17, 2026
ycarogabriel-lgtm added a commit to ycarogabriel-lgtm/AnyPS5 that referenced this pull request Oct 18, 2026
… no pixel input sees without the extension (@DavidSamir)

Open upstream PR that adds macOS or Metal behaviour this branch did not have; merged so the fork carries the community's current macOS work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…st-provoking-vertex

# Conflicts:
#	docs/dev/debt/libSceAgcDriver.md
@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed needs rebase Conflicts with main, rebase needed labels Oct 18, 2026
ycarogabriel-lgtm added a commit to ycarogabriel-lgtm/AnyPS5 that referenced this pull request Oct 18, 2026
Keeps the macOS port current with 50 upstream commits.

- Upstream's boykopovar#7487 keeps the barycentric tessellation stages' clip
  distances in the blocks between the stages, which MoltenVK's
  SPIRV-Cross translates; it replaces the fork's MoltenVK-only switch
  (clipDistancesInPerVertexBlock), so the switch, its device query and
  its debt note are removed and every driver takes upstream's stages.
- The provoking vertex debt entry takes upstream's geometry program and
  strip facts and keeps the fork's rule for devices without
  VK_EXT_provoking_vertex (boykopovar#6741).
- The f16 interpolation debt entries keep the fork's 64-bit integer
  text and take upstream's clip distance description.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the unblocks title Names a title ID on the Unblocks line label Oct 18, 2026
…st-provoking-vertex

# Conflicts:
#	docs/dev/debt/libSceAgcDriver.md
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 18, 2026
@adriadam10 adriadam10 self-assigned this Oct 19, 2026

@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.

Thanks, the approach and the evidence are good. One gap before this can merge:

  • Pipeline.cpp:113-157: only the fragment stage is inspected, but the provoking vertex also selects which vertex supplies Layer and ViewportIndex (Vulkan spec, primitive layer/viewport selection), and that happens whether or not the pixel shader reads anything. The recompiler emits both as vertex-stage outputs (StageOutputKind::Layer/ViewportIndex, SpirvEmitterHelpers.cpp:396-399). A vertex shader writing a per-vertex gl_Layer or gl_ViewportIndex with a pixel shader without inputs now passes ValidateProvokingVertex and renders into the first vertex's layer/viewport instead of the last's, silently.
  • Fix direction: also treat a non-fragment stage whose SPIR-V decorates Layer or ViewportIndex as BuiltIn outputs (a variable or block member) as observing the provoking vertex, so last mode still throws there without the extension. Add cases to UnobservedProvokingVertexTests (vertex stage with BuiltIn Layer/ViewportIndex, and your existing "vertex-stage metadata beside a smooth pixel shader" case must still pass without those built-ins), and mention it in the debt entry.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 19, 2026
ycarogabriel-lgtm added a commit to ycarogabriel-lgtm/AnyPS5 that referenced this pull request Oct 19, 2026
…hrough the barycentric tessellation stages (@DavidSamir)

Apple GPUs under MoltenVK have neither geometry shaders nor
VK_EXT_provoking_vertex, so a last-provoking-vertex draw that needs the
barycentric emulation stopped on macOS; DavidSamir's PR draws it
through the generated tessellation stages instead. In Pipeline.cpp its
early return for those draws sits before this branch's early return
for draws no pixel input sees (boykopovar#6741), and both stay. The barycentric
emulation test takes the PR's flat and provoking parameters and keeps
this branch's built-in switch after them. The debt files take the PR's
entries and keep this branch's short MoltenVK ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ycarogabriel-lgtm added a commit to ycarogabriel-lgtm/AnyPS5 that referenced this pull request Oct 19, 2026
… no pixel input sees without the extension (@DavidSamir)

This branch already carried an earlier head of this PR; DavidSamir has
pushed newer commits since, and this merge brings the branch up to
them. The only conflict is the provoking vertex debt entry, where the
branch already holds boykopovar#7426's entry and its own short entry for the
draws no pixel input sees; both are kept as they are.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…st-provoking-vertex

# Conflicts:
#	core/libs/prx/libSceAgcDriver/CMakeLists.txt
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 19, 2026
@DavidSamir

Copy link
Copy Markdown
Contributor Author

Title evidence for #6741 and #7426 together, from a macOS catalog sweep: Apple M2, MoltenVK 1.4.x (no VK_EXT_provoking_vertex, no geometry shaders), Rosetta 2, on main 00d7e61 with @mugurc's ci/macos-series-2 and #7937 (so libkernel builds on macOS). 30 s per title, same build apart from the two PRs.

On that base, 12 catalog titles stop at AGC graphics: last provoking vertex requires VK_EXT_provoking_vertex with provokingVertexLast enabled. Results:

Title base + #6741 only + #7426 only + both
SymphonyStation5 PPSA17641 stop stop stop still running at 30 s
VITA5 PPSA39410 stop stop stop still running
Signal Companion PPSA69426 stop stop stop still running
PokeMMO Prospero PPSA98001 stop stop stop still running
ProsperoLight PPSA99002 stop stop stop still running
Surround Sound Studio PPSA99051 stop stop stop still running
PS5LM PPSA99575 stop stop stop still running
PS5LM Plus PPSA99581 stop stop stop still running
Cloud Play PPSA99600 stop stop stop still running
PShare PPSA99773 stop stop stop still running
Pulse PPSA99993 stop stop stop still running
Afterglow PPSA47770 stop own exit (code 1, 15 s) stop own exit (code 1, 15 s)

So on a device without the extension these titles need both changes: #6741 for their last-vertex draws whose pixel shader cannot see which vertex provokes, and #7426 for the ones whose pixel shader reads emulated barycentrics through the generated tessellation stages. Either alone still stops at the same check. Nothing past 30 s is tested; MoltenVK-specific limits (#6235, #6528) are not in the way of these 11.

The two PRs touch the same function (ValidateProvokingVertex); merged together, the #7426 block for barycentric patches goes first and #6741's check of whether a pixel input reads the provoking vertex follows, with no other change.

AI-assisted: yes (Claude Code)

@DavidSamir

Copy link
Copy Markdown
Contributor Author

Validation-layer comparison, as AGENTS.md now asks for AGC changes. Apple M2, MoltenVK 1.4.x (Vulkan SDK 1.4.363), macOS 26, x86-64 under Rosetta 2, same machine and build directory, on main with @mugurc's ci/macos-series-2 and #7937 (needed for libkernel on macOS). Each test run with ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation; the loader reports loading libVkLayer_khronos_validation.dylib, and no log contains was not found but was requested by env var VK_INSTANCE_LAYERS.

Test without #7426 and #6741 with both
agc_driver_pixel_barycentric_emulation passes, 0 VUID- passes (now also the flat-input cases), 0 VUID-
agc_driver_graphics passes, 0 VUID- passes, 0 VUID-
agc_driver_provoking_vertex exits: required provoking vertex rendering coverage is unavailable: provokingVertexLast is not supported the same (this device has no VK_EXT_provoking_vertex; without ANYPS5_REQUIRE_VULKAN it skips)

No new validation errors with the two changes. The title results for the pair are in the comment above (11 catalog titles run past the provoking-vertex stop with both).

AI-assisted: yes (Claude Code)

@DavidSamir

Copy link
Copy Markdown
Contributor Author

No-regression run on a GPU with VK_EXT_provoking_vertex: main 567c5fd630 against main plus head 23e22f1d81. Windows 11 Pro 26300, NVIDIA RTX 3070 (driver 610.60), WinLibs GCC 15.2.0, Release. Catalog releases with sha256 checked, 60 s each.

The nine titles listed for the Mac all run the full 60 s on both builds: SymphonyStation5 (PPSA17641), VITA5 (PPSA39410), Signal Companion (PPSA69426), PokeMMO (PPSA98001), ProsperoLight (PPSA99002), PS5LM (PPSA99575), Cloud Play (PPSA99600), PShare (PPSA99773) and Pulse (PPSA99993). None stops early.

This device takes the extension's native path, so this run checks only that nothing regresses. The emulated path is covered by the Mac runs in the description.

AI-assisted: yes

@DavidSamir

Copy link
Copy Markdown
Contributor Author

@adriadam10 thanks, you're right. Addressed in 507e6b5..3684f25:

  • ValidateProvokingVertex now also treats a non-fragment stage whose SPIR-V decorates a BuiltIn Layer or BuiltIn ViewportIndex output, on a variable or a block member, as observing the provoking vertex. Last mode still throws there without the extension. Two details:

    • One that is also PerPrimitiveEXT is skipped. That is the mesh path's per-primitive layer, which the emitter writes per primitive from its last index, so Vulkan's provoking vertex doesn't select it.
    • A stage whose module can't be read counts as observing.
  • The recompiler declares those outputs only when the guest exports them through PA_CL_VS_OUT_CNTL, so plain vertex shaders keep drawing.

  • UnobservedProvokingVertexTests gains cases for vertex, tessellation-evaluation, geometry and mesh stages:

    • Layer and ViewportIndex, each as a variable and as a block member: refused without the extension, accepted with it;
    • the per-primitive variants: accepted;
    • Position/PointSize only: accepted;
    • an unreadable module: refused.

    The existing "vertex-stage metadata beside a smooth pixel shader" case still passes.

  • The new cases fail on the commit before the fix. The debt entry names the built-ins.

Validation-layer comparison with main and per-commit results are in the PR body.

AI-assisted: yes

@DavidSamir

Copy link
Copy Markdown
Contributor Author

Apple M2 check of head 3684f25e85 (same account as the author, run from the Mac): macOS 26, MoltenVK from Vulkan SDK 1.4.363.0, x86-64 build under Rosetta, tree = main 35fa2bdd82 + the macOS port series + this head + #7426. 40 s per title, relinked with --macos --to-rosetta.

The 11 titles that need the two provoking-vertex PRs still run the full 40 s with no driver failure, no terminating and no new throw about Layer or ViewportIndex: PPSA17641, PPSA39410, PPSA69426, PPSA98001, PPSA99002, PPSA99051, PPSA99575, PPSA99581, PPSA99600, PPSA99773, PPSA99993. PPSA47770 (Afterglow) exits after 14 s at its own check, as before this change.

AI-assisted: yes

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@adriadam10 could you take another look when you have a moment? On 3684f25 your Layer/ViewportIndex point looks addressed to us: the non-fragment stages that decorate BuiltIn Layer or ViewportIndex (as a variable or a block member) now count, PerPrimitive outputs are skipped, and an unrecognised case fails closed. The CPU tests cover vertex, tessellation evaluation, geometry and mesh. If it looks right to you too, an updated review would clear your change request.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* build docs unblocks title Names a title ID on the Unblocks line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants