Skip to content

test(agc): skip cull distance cases on devices without shaderCullDistance - #10612

Merged
oneandonlydean merged 2 commits into
boykopovar:mainfrom
imellag:test/agc-skip-cull-distance-without-support
Oct 25, 2026
Merged

oneandonlydean merged 2 commits into
boykopovar:mainfrom
imellag:test/agc-skip-cull-distance-without-support

Conversation

@imellag

@imellag imellag commented Oct 24, 2026

Copy link
Copy Markdown
Contributor

What

On a device without shaderCullDistance the driver refuses a draw that enables a cull distance (ShaderValidation.cpp: vertex clip or cull distance needs shaderClipDistance or shaderCullDistance, which the device lacks). Four tests ran their cull distance cases anyway, so on MoltenVK, which has no shaderCullDistance, all four failed at that refusal: agc_driver_vertex_clip_cull_distances, agc_driver_vertex_clip_cull_draw, agc_driver_vertex_clip_cull_draw_no_key and agc_driver_vertex_user_data_tail.

They now skip only the cases whose PA_CL_VS_OUT_CNTL enables a cull distance (CULL_DIST_ENA, bits 8 to 15) when the target lacks CapabilityCullDistance, read through the existing TargetHasCapability, and print a line saying so, as agc_driver_vertex_clip_cull_distances already does for its mesh case without VK_EXT_mesh_shader. On a device with shaderCullDistance every case runs as before. The clip distance cases still run everywhere.

The shader debt entry for vertex clip and cull distances now records that a device without shaderCullDistance throws on such a draw; nothing emulates it.

Tested

Apple M4 Pro, macOS 27.0.1, MoltenVK 1.4.2 (Vulkan SDK 1.4.363.0), x86-64 Release under Rosetta 2. main does not build on macOS yet, so both sides ran on #8658's feat/macos-port (564e005acb), whose three test files and ShaderValidation.cpp check match main: without and with this PR's test changes, ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation, ctest -R '^agc_driver_vertex_(clip_cull_distances|clip_cull_draw|clip_cull_draw_no_key|user_data_tail)$' -V:

without with this PR (3 runs)
agc_driver_vertex_clip_cull_distances fails, PA_CL_VS_OUT_CNTL=0x00400100 refused passes, cull cases skipped
agc_driver_vertex_clip_cull_draw aborts, 0x00400400 refused passes, cull step skipped
agc_driver_vertex_clip_cull_draw_no_key aborts, 0x00400400 refused passes, cull step skipped
agc_driver_vertex_user_data_tail fails, 0x00400100 refused passes, cull draw skipped
was not found but was requested 0 0
VUID- VUID-VkDeviceCreateInfo-pProperties-04451, 14 lines the same, 14 lines

The validation message is MoltenVK's portability subset at device creation, the same on both sides. Not run on a device with shaderCullDistance; there the new check is true and no case is skipped.

check_conventions.py --base upstream/main passes. pr_overlap.py lists no pull request with conflicting lines or the same exports, only ones it could not compare on docs/dev/debt/shader.md because they conflict with main on their own.

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
  • Fixes: none
  • Unblocks: none
  • GPUs tested: Apple M4 Pro, MoltenVK 1.4.2, macOS 27.0.1
  • AI-assisted: yes

…ance

The driver refuses a draw that enables a cull distance when the device
has no shaderCullDistance, and agc_driver_vertex_clip_cull_distances,
agc_driver_vertex_clip_cull_draw (with and without the draw key) and
agc_driver_vertex_user_data_tail ran their cull distance cases anyway,
so on MoltenVK all four failed at that refusal. They now skip only
those cases when the target lacks CapabilityCullDistance, as they
already skip the mesh case without VK_EXT_mesh_shader, and the shader
debt entry records the refusal.
Comment thread docs/dev/debt/shader.md Outdated
- [Vertex entry merged wave info](../../../core/shader/recompiler/Translation/src/InstructionTranslator.cpp) (recompiler) - `s3` of every vertex-path shader is set to `waveSize | waveSize << 8` (vertex and primitive count of a full wave, wave index 0), or to `32 | 32 << 8` for a wave64 program on a 32-lane host subgroup, which runs it at one lane per invocation and holds no lanes 32-63. On a device whose `subgroupSupportedStages` lacks the vertex stage (lavapipe, MoltenVK), vertex and local programs run as one-lane waves: `s3` is `1 | 1 << 8`, the lane id is 0, a ballot is the invocation's own bit, a shuffle or broadcast returns the invocation's value, an elect is true and a subgroup reduction is the value itself, so no `GroupNonUniform` capability is declared. Layout taken from the prologue Mesa's ACO emits for NGG vertex shaders (`s_bfe_u64 exec` from `s3`), not measured on hardware. The driver gives every graphics program user data from `s8`, so the condition does not tell NGG from legacy vertex shaders: they just never read `s3`. A draw whose last wave is partial still reports a full wave, and so does a host wave with idle invocations (an RTX 5070 ran every vertex warp of a non-indexed triangle list with 30 of its 32 invocations).
- [s_barrier in vertex-path programs](../../../core/shader/recompiler/SpirvBackend/src/SpirvFlowEmitter.cpp) (recompiler) - emits no instruction when the program has no LDS or GDS access and no memory write or atomic, so no wave of the subgroup can observe another, or when the barrier runs before every memory access of the program (it is on the entry's straight-line path with no load, store, atomic, image or LDS access ahead of it), so it orders nothing; the `GS_ALLOC_REQ` such a barrier orders on NGG passthrough programs is already dropped on the vertex path. The Vertex execution model has no workgroup barrier. Other programs with LDS, GDS or memory writes throw. Not measured on hardware
- [Vertex clip and cull distances](../../../core/shader/recompiler/Translation/src/ShaderInputInfoBuilder.cpp) (libSceAgcDriver) - `PA_CL_VS_OUT_CNTL` enable bit `s` of `CLIP_DIST_ENA`/`CULL_DIST_ENA` is taken to select component `s` of the `pos1`/`pos2` exports, with the Vulkan array index counted by the enabled bits below it, and `VS_OUT_CCDIST0_VEC_ENA` as bit 22; this follows the recompiler's existing position-export mapping, not hardware or documentation. The guest clip distances share one `gl_ClipDistance` array with the all-zero clip position distance, which takes the index after them. A vertex program whose own code has no `pos1`..`pos3` export is compiled and prepared without these bits (`PositionExportLayout`), so registration and draws agree on its artifact; an export in a subroutine it calls is not seen by that scan
- [Vertex clip and cull distances](../../../core/shader/recompiler/Translation/src/ShaderInputInfoBuilder.cpp) (libSceAgcDriver) - `PA_CL_VS_OUT_CNTL` enable bit `s` of `CLIP_DIST_ENA`/`CULL_DIST_ENA` is taken to select component `s` of the `pos1`/`pos2` exports, with the Vulkan array index counted by the enabled bits below it, and `VS_OUT_CCDIST0_VEC_ENA` as bit 22; this follows the recompiler's existing position-export mapping, not hardware or documentation. The guest clip distances share one `gl_ClipDistance` array with the all-zero clip position distance, which takes the index after them. A vertex program whose own code has no `pos1`..`pos3` export is compiled and prepared without these bits (`PositionExportLayout`), so registration and draws agree on its artifact; an export in a subroutine it calls is not seen by that scan A device without `shaderCullDistance` (MoltenVK) throws on a draw that enables a cull distance; none is emulated.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Small documentation fix: this entry joins the new sentence directly to the previous one (...seen by that scan A device...). Please add a sentence boundary before “A device” so the debt note reads cleanly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks, fixed in 0a8503d73d: the entry now reads "...an export in a subroutine it calls is not seen by that scan. A device without shaderCullDistance (MoltenVK) throws on a draw that enables a cull distance; none is emulated."

AI-assisted: yes

@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

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 5021 docs/dev/debt/shader.md
PR 5371 docs/dev/debt/shader.md
PR 6235 docs/dev/debt/shader.md
PR 6280 docs/dev/debt/shader.md
PR 6412 docs/dev/debt/shader.md
PR 6488 docs/dev/debt/shader.md
PR 6924 docs/dev/debt/shader.md
PR 7410 docs/dev/debt/shader.md
PR 7684 docs/dev/debt/shader.md
PR 8004 docs/dev/debt/shader.md
PR 8077 docs/dev/debt/shader.md
PR 9020 docs/dev/debt/shader.md
PR 9335 docs/dev/debt/shader.md
PR 9387 docs/dev/debt/shader.md
PR 9748 docs/dev/debt/shader.md
PR 9889 docs/dev/debt/shader.md
PR 10233 docs/dev/debt/shader.md

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed updated head 0a8503d. The new filters inspect only CULL_DIST_ENA bits 8–15, leave clip-distance cases enabled, and skip the cull-enabled rows/steps only when TargetHasCapability(..., CapabilityCullDistance) is false. The corrected debt sentence now has a proper sentence boundary. I did not reproduce the GPU run locally; the reported MoltenVK baseline/candidate comparison includes the required validation-layer and VUID comparison, and the current hosted eligibility, conventions, labels, and report checks pass. Hosted product jobs were skipped.

@github-actions github-actions Bot added docs agc libSceAgc* labels Oct 24, 2026
@oneandonlydean oneandonlydean self-assigned this Oct 25, 2026
@oneandonlydean
oneandonlydean merged commit 10a41a0 into boykopovar:main Oct 25, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* docs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants