Skip to content

Bound translated fragment uniform reads to the NVN binding length - #1909

Closed
oz-rinkliff wants to merge 1 commit into
seedds:mainfrom
oz-rinkliff:ttyd/fragment-bounds-draft
Closed

oz-rinkliff wants to merge 1 commit into
seedds:mainfrom
oz-rinkliff:ttyd/fragment-bounds-draft

Conversation

@oz-rinkliff

@oz-rinkliff oz-rinkliff commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

What and why

I reproduced #1860 on clean main at a1e155a38524 (chain 38c7b39dc6fd). The M3 Ultra reported three failed Metal command buffers containing fragment shader f1e0f08cc43e21e3, revoked the process's GPU access and exited with code 70.

The NVN command records both the address and the size of a uniform binding. Generated Metal fragment shaders previously received only its address. TTYD's Peach sequence sometimes supplies 16 bytes for slot 11. Variant 6 reads a loop count at byte 2,304. A separate GPU test on clean main read a deliberately placed value at that offset even with the binding shortened to 16 bytes. I didn't capture the actual value from the hanging invocation.

This change supplies the bound length alongside each ordinary fragment constant bank read by the shader. Reads inside the binding still use the original GPU buffer. A partial vector preserves the bytes inside the range, and reads entirely beyond it return zero. The rule uses no title ID or shader key.

Checked

I rebased the draft onto 7479adf8. The gameplay builds and four-title comparison below used a1e155a38524, shader chain 46beb3f34a2d; I reran the shader rewrite unit tests after the rebase. The rebase includes an upstream shader-package lookup change and produces a different chain hash.

  • Before this change, an experimental shader-specific zero-padding build passed the pre-potion checkpoint with MetalFX on both M3 Ultra Mac and M4 iPad Pro. Both logs recorded at least 2,048 affected bindings. The unpatched builds had Metal GPU errors on both Standard and MetalFX.
  • The M3 Ultra fragment regression test passed full, short, partially valid, unbound and rebound cases. It uses the generated shader and the renderer's argument encoding.
  • The shader rewrite, reflection, shader fingerprint, shader-chain and documentation tests passed.
  • All 11,858 TTYD Mac shaders compiled; the shader build reported zero regressions. The complete shader image grew from about 1.7 to 2.7 GiB on the Mac. Compilation and runtime cost still need review.
  • The graphics suite is not green on this Mac. t_render_counter_overlap failed its CopyBuffer0 check and t_render_enable_queue failed nine checks. Both also failed on unmodified a1e155a38524. The targeted fragment regression passed.
  • On the M3 Ultra Mac I replayed the failing inputs, made the invisibility potion, reached the hallway and quit normally on the PR build with MetalFX. The saved log shows host exit 0 and no GPU hang. On the M4 iPad Pro I also reached the hallway on chain 46beb3f34a2d with MetalFX and no logged GPU hang. Performance remains an open problem: I saw about 40 fps. In the final two 300-frame windows, median frame time was 25.14 and 26.06 ms and median GPU time was 22.96 and 23.96 ms. The older shader-specific padding build ended around 16.67 ms per frame at the same MetalFX resolution. These are different revisions and the frames are not synchronized.
  • The four-game quick harness completed with zero newly failing shader builds in TTYD, Mario Kart 8 Deluxe, Smash Ultimate and Super Mario 3D All-Stars. Its report contains no gameplay FPS measurements.

iPad performance follow-up

I also tried a separate build on the same base as the tested PR code, moving the partial-vector copy out of line (experimental chain d26eeb03ba27). I reached the hallway again with MetalFX at 1920×1080 → 2420×1361 and no recorded GPU hang. The last two 300-frame windows had median frame times of 20.56 and 20.60 ms (about 49 fps), versus 25.14 and 26.06 ms (about 38–40 fps) in the first general-bounds run. Median GPU time in those windows fell from 22.96–23.96 ms to 18.48–18.64 ms. The MetalFX present pass stayed around 1.1 ms. These are end-of-run comparisons rather than a synchronized frame benchmark. The experimental iPad shader image was 23% smaller, but this still misses the 16.67 ms frame budget. The experiment is not part of the submitted diff.

Docs

The TTYD game note links the new rendering reference entry and the symptom index has been regenerated. No game data or keys are included.

Pass the binding length alongside the GPU address so reads preserve the valid prefix and return zero outside it. Test short, partially valid and rebound banks on Metal. The TTYD Peach doorway passes on Mac and iPad; the measured iPad frame-time cost remains open.
@github-actions github-actions Bot added nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests) docs Documentation shader chain The shader translation chain (DMSH_CHAIN): a change rebuilds every game's shaders game: 0100ECD018EBE000 Paper Mario: The Thousand-Year Door labels Nov 9, 2026
Fasen24-AI added a commit to Fasen24-AI/swordboiMacNative that referenced this pull request Nov 10, 2026
…nds once to half), seedds#1909 (fragment uniform reads bound to the binding length), seedds#1901 (dispatch_overlap on for Ocarina), seedds#1459 (MetalFX takes agl_ndsr_vr, no-curve variants linear), seedds#1903 (render scale stubs for Ocarina; replaces the seedds#1497 ceiling we carried)

Hand-resolved: the mesh draw keeps the task isolation of seedds#1842 and the copy paths our nvn_tex_level_range / DM_SHARPEN; seedds#1497's stub, test and BINDING entry go; the build-keyed rule names the render scale function (main+0x31d3200). Chain e906c7467690.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014eD437mxrMnW2UwLZhpwc7

Upstream pull requests applied in this commit (their authors' work, brought onto this tree with a three-way merge; the description is in each PR):
- seedds#1459 by PromethOps (open): DM_UPSCALER=metalfx takes agl's agl_ndsr_vr.pck networks too, and a variant with no curve and no power is drawn as one colour space: Ocarina of Time's upscaler is MetalFX's
- seedds#1497 by givethesourceplox (open): Add a render-scale ceiling for Ocarina v131072
- seedds#1842 by ycarogabriel-lgtm (open): Ocarina of Time: opt-in task isolation (DM_NVN_ISOLATE_TASK), the Hi-Z task a5288 in command buffers of its own, a workaround for its GPU hangs on an M1
- seedds#1897 by Know-It-All-Brother (closed): HMMA with an f16 accumulator rounds once per 8 products, as Ampere does (chain change)
- seedds#1901 by FifteenPen (open): The game's compute dispatches with no game barrier between them run side by side (profile key dispatch_overlap, on for Ocarina of Time): Kokiri Forest GPU p50 15.63 → 14.76 ms
- seedds#1902 by fxd0h (open): HFMA2 rounds once to half, as Ampere does (chain change)
- seedds#1903 by FifteenPen (open): Ocarina of Time takes the launcher's Render scale: the 3D views at that fraction of the game's own size, the dynamic resolution held at its top level (Kokiri Forest docked at 0.75: GPU p50 15.77 → 14.31 ms, 60 fps held)
- seedds#1909 by oz-rinkliff (open): Bound translated fragment uniform reads to the NVN binding length
Fasen24-AI added a commit to Fasen24-AI/swordboiMacNative that referenced this pull request Nov 10, 2026
…1909 seedds#1897 seedds#1917 seedds#1920 seedds#1936) and seedds#1902, ahead of merging main

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014eD437mxrMnW2UwLZhpwc7

Upstream pull requests applied in this commit (their authors' work, brought onto this tree with a three-way merge; the description is in each PR):
- seedds#1897 by Know-It-All-Brother (closed): HMMA with an f16 accumulator rounds once per 8 products, as Ampere does (chain change)
- seedds#1902 by fxd0h (open): HFMA2 rounds once to half, as Ampere does (chain change)
- seedds#1909 by oz-rinkliff (open): Bound translated fragment uniform reads to the NVN binding length
- seedds#1917 by FifteenPen (closed): A draw that writes depth and samples its own depth attachment reads a copy of it: Ocarina of Time's light-blue dashes over the rock at Death Mountain Trail's cave entrance are gone
- seedds#1920 by nawangwe (closed): A GPU-written span's sliver inside a texture does not make it the GPU's: Ocarina of Time's terrain tiles no longer vanish or show another tile's surface (seedds#1566)
- seedds#1936 by TheGoldfish11 (closed): Write resolution-family image textures uncompressed
@seedds

seedds commented Nov 10, 2026

Copy link
Copy Markdown
Owner

Thanks. Superseded by 26062f9, which bounds every translated uniform read to the size the game bound (a read past it gives 0, as on Maxwell), with you credited as co-author. Closing.

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

Labels

docs Documentation game: 0100ECD018EBE000 Paper Mario: The Thousand-Year Door nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests) shader chain The shader translation chain (DMSH_CHAIN): a change rebuilds every game's shaders

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants