Skip to content

Primitive ID pipelines: a GPU test, and their pairs left out of the prebuild and the offline archive - #972

Closed
capibawa wants to merge 1 commit into
seedds:mainfrom
capibawa:primitive-id-prebuild-and-test
Closed

capibawa wants to merge 1 commit into
seedds:mainfrom
capibawa:primitive-id-prebuild-and-test

Conversation

@capibawa

@capibawa capibawa commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

What and why

This is split out of #957, as offered in its closing comment. main builds a pipeline whole when its fragment shader reads the primitive ID ([[primitive_id]] in a specialized Metal 4 pipeline), but no test covered it, and two places still built bases for such pairs that no draw uses:

  • The test: t_image_primitive_id draws Fire Emblem Fortune's Weave's case through the host's own draw path: an attachment-less pass of four full-screen triangles whose fragment shader stores into the layer its primitive ID names. The shader is a metallib loaded as game shaders are, with the reflection's reads_prim_id set. Each of the four layers must hold its own primitive's value.
  • The prebuild: pre_desc and pso_offline.py now leave out a pair whose fragment reflection has raster.primitive_id. The offline plan counts these pairs as primitive ID.

Checked

  • Tests:
    • t_image_primitive_id passes, 6 of 6 checks. To check that it catches the bug, I ran it with the flag cleared: layers 1 to 3 stay empty and 3 checks fail.
    • dm/tools/tests/test_pso_offline.py, with a new pair that it leaves out.
    • test_docs.
    • I didn't run the full make -C dm/gfx/tests run on this branch. The test's build compiles nvn_mtl.m with the pre_desc change.
    • clang-tidy isn't installed here, so CI's lint is the check for the new test file.
  • Games: none run on this branch. It changes no draw. Of the local shader libraries, only Fortune's Weave's b6726be4afc44691 reads the primitive ID (Donkey Kong Bananza 0 of 23,638, Hello Kitty Party Land 0 of 12,739).
  • Devices and OS: MacBook Pro M3 Pro, macOS 27.0.1.
  • regress.py: not run, for the same reason: the change reaches only pairs whose fragment shader reads the primitive ID.

Docs

  • The rendering.md entry describes the prebuild and the test, and python3 dm/tools/docs_symptoms.py was run (no change to the index)
  • No game files, keys or data extracted from a game in the change or the attachments

🤖 Generated with Claude Code

…rebuild and the offline archive

A fragment shader that reads the primitive ID gets its pipeline built whole (pso_build_ex), because through a base with unspecialized colour state every primitive reads 0. No test covered this. The prebuild (pre_desc) and the offline archive (pso_offline.py) still made a base for each such pair, and no draw uses that base.

t_image_primitive_id draws Fire Emblem Fortune's Weave's case through the host's draw path: an attachment-less pass of four full-screen triangles whose fragment shader stores into the layer its primitive ID names, from a metallib loaded as game shaders are, with the reflection's flag set. pre_desc and pso_offline.py now leave out a pair whose fragment reflection has raster.primitive_id, and the offline plan counts those pairs as "primitive ID".

Checked: on an M3 Pro (macOS 27.0.1) each of the four layers holds its primitive's value; with the flag cleared, layers 1 to 3 stay empty (3 of 6 checks fail). test_pso_offline.py leaves such a pair out, and test_docs passes. The rendering.md entry describes the prebuild and the test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seedds added a commit that referenced this pull request Nov 2, 2026
…rebuild and the offline archive

A fragment shader that reads the primitive ID gets its pipeline built whole (pso_build_ex), because through a base with unspecialized colour state every primitive reads 0. No test covered this. The prebuild (pre_desc) and the offline archive (pso_offline.py) still made a base for each such pair, and no draw uses that base.

t_image_primitive_id draws Fire Emblem Fortune's Weave's case through the host's draw path: an attachment-less pass of four full-screen triangles whose fragment shader stores into the layer its primitive ID names, from a metallib loaded as game shaders are, with the reflection's flag set. pre_desc and pso_offline.py now leave out a pair whose fragment reflection has raster.primitive_id, and the offline plan counts those pairs as "primitive ID".

Checked: on an M3 Pro (macOS 27.0.1) and an M6 (macOS 27.2) each of the four layers holds its primitive's value; with the flag cleared, layers 1 to 3 stay empty (3 of 6 checks fail). test_pso_offline.py leaves such a pair out, and test_docs passes. The rendering.md entry describes the prebuild and the test.

From PR #972.

Co-authored-by: James <12531452+capibawa@users.noreply.github.com>
@seedds

seedds commented Nov 2, 2026

Copy link
Copy Markdown
Owner

Thank you. This landed on main as fefb16d with your code unchanged, and you're credited as co-author. I'm closing the PR because the commit was rebased rather than merged here.

Checked on an M6, macOS 27.2:

  • t_image_primitive_id passes 6 of 6. With the flag cleared, layers 1 to 3 stay empty and 3 checks fail, the same as you saw on the M3 Pro.
  • test_pso_offline.py passes.

The workaround stays. I tried to find a way to keep a shared base, in small test programs on the M6. With a render target, the ID read 0 whichever colour state was left open: format, write mask or blending. Setting the topology class or reading barycentrics as well made no difference. Without a target it read 0 too, including with binary linking or the front face read as well, except when only the mask and blending were open. That case doesn't help real pipelines, which do have targets.

One gap: the test sets reads_prim_id by hand, so nothing covers the reflection setting it from raster.primitive_id in nvn_refl.m. I didn't think that was worth holding the PR for.

@seedds seedds closed this Nov 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants