Skip to content

Read colour-attachment feedback through read-only fragment image slots - #1025

Closed
thehobbit85 wants to merge 2 commits into
seedds:mainfrom
thehobbit85:codex/readonly-image-feedback
Closed

thehobbit85 wants to merge 2 commits into
seedds:mainfrom
thehobbit85:codex/readonly-image-feedback

Conversation

@thehobbit85

@thehobbit85 thehobbit85 commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1009. Dorkington traced Xenoblade Chronicles 3’s alternating cutscene brightness to lighting shader 15816d836842bfb5 and supplied the original fix. While rendering to its R32Uint zone mask, the shader loads it through fragment image slot 0. An open Metal pass can return the previous frame’s mask. Issue and original patch

The draw now reads a snapshot of the attached level and layer through a compatible read-only fragment image. Later draws reuse it until the game’s next barrier, and argument encoding restores the normal binding on the next draw that needs it. Writable, atomic and class-reinterpreting images keep their original bindings. A sampled texture can still use the tile when an image shares its slot number.

Validation on this revision

Tested b8e72249d745af0a6ca03ec2fc02affe11e99772 on main 1440a736860e45c7722f7bd803a0fc74ff62c69f. This integration retains main’s shape-based snapshot cache and program-finalization changes, plus the PR’s image-view ownership checks.

  • On an Apple M2 Max, macOS 26.6, Xcode 27.2, make -j1 -C dm/gfx/tests GAME=010074F013262000 check_t_feedback check_t_queue_origin passed under Metal API validation. The mounted Apple Metal compiler 32023.921 built the tile library and all 13 host libraries. The feedback fixture passed 135 checks. The integer image read 17 through the snapshot after its source was overwritten with 29, then read 29 from the restored binding. It checked level 1 against both attachment levels, SINT reinterpretation, the sampled-slot/image-slot collision, cache reuse across shapes, and the target’s ownership through serial-table teardown. The queue fixture passed 398 checks with each encode-thread setting, including releasing programs bound on the active and saved queues.
  • python3 -m unittest dm.tools.tests.test_docs dm.tools.tests.test_gfx_tests_makefile passed 16 tests. git diff --check passed.
  • LLVM 23.1.4 checked the two changed Objective-C translation units with lint_c.py tidy -j 1 --against 1440a736 dm/gfx/tests/t_feedback.m dm/gfx/nvn/nvn_mtl.m. The feedback fixture had zero findings. Every file/check count matched its baseline, including the included sources.

On the earlier integration, removing the registered-class guard and the binding-kind guard produced exactly two failures. The SINT view received a snapshot, and tile-qualified sampled slot 0 suppressed the image-slot-0 snapshot. The ownership repair subsequently fixed a clang-tidy finding in the test and checked the serial entry before and after release. On that earlier head the full LLVM 23.1.4 run covered 218 translation units and matched all 271 baseline findings.

Game coverage and cost

scripts/regress.py impact 1440a736..HEAD identifies Breath of the Wild, Pokémon Scarlet and Tears of the Kingdom through shared NVN entry points and readable-tile use. One additional game matched a broad rule and 129 profiles lacked manifests. The targeted comparison covers Xenoblade Chronicles 3 for the reported image load, Breath of the Wild for tile-reading decals, Super Mario 3D World + Bowser’s Fury for post-effect feedback, NieR:Automata for separate sampled slots and Paper Mario: The Origami King for anti-aliasing feedback.

The earlier scripts/regress.py check --no-smoke --jobs 1 --games 010074F013262000,01007EF00011E000,010028600EBDA000,010056B015FE8000,0100A3900C3E2000 stopped at discovery with exit 2. A fresh inventory still finds only Pokémon Legends Z-A. The five-game comparison, local gameplay and device validation remain outstanding.

Haseenesss tested the equivalent fix in #1035 on their own two hosts built at 5474c6f, using XC3 2.2.0 on an Apple M2. In Events Theater chapter 1’s Colony Sigma battle, eight of ten baseline bursts showed luminance sign-flip fractions of 0.83–1.00. The highest of eleven fixed bursts was 0.28. The prologue did not show the flicker. The warm-window GPU p50 rose from 50.4 to 53.0 ms and frame rate fell from 18.3 to 17.2 fps. The contributor also suggested the mip-level and signed-class checks included here. Both PRs address the same issue. Contributor’s scene measurements and test suggestions · Overlapping PR #1035

@thehobbit85
thehobbit85 force-pushed the codex/readonly-image-feedback branch 2 times, most recently from 8234401 to 3739434 Compare November 3, 2026 03:11
@Haseenesss

Copy link
Copy Markdown
Contributor

In-game check of this fix, for the gameplay run this PR does not have yet. I opened #1035 for the same issue before I saw this one. It takes the same approach: read-only NVN_RB_IMG slots in fb_prepare, the image resolved through img_binding, and the snapshot bound in encode_args as read-only. Either PR can be merged. #1035 stays open until you decide.

Setup. Apple M2 (8 GB), macOS 27.2, Xenoblade Chronicles 3 2.2.0 (main 82d187fe). Two hosts that differ only by the image snapshot change, built at 5474c6f because the game's shader libraries are of chain d1043b780a74. I captured 48 consecutive distinct presents per burst with DM_CTL and measured the mean luminance of the picture. The sign-flip fraction is how often the frame-to-frame change reverses direction; a value near 1.00 is the alternation in #1009.

Scene matters. The chapter 1 prologue (Events Theater, first scene) does not show the flicker even before the fix. Chapter 1's second scene, the battle with Colony Sigma, does.

host bursts sign-flip fraction example
before 10 0.83–1.00 in 8 of them 38.3 / 40.6 alternating through all 48 frames; 64 / 58 after a cut
after 11 at most 0.28 the same quiet stretch drifts about 0.1 level per frame (flip fraction 0.07)

The difference image of two adjacent frames before the fix shows whole regions of lighting switching. After the fix only character edges differ, from motion. A log milestone names 15816d836842bfb5_b slot 0 on the snapshot path.

Cost. Over a warm window of that scene, GPU p50 went from 50.4 to 53.0 ms and fps from 18.3 to 17.2, from the extra snapshots. No new [RED] lines. After CONTINUE, the first save's field renders and walks normally.

Two cases from #1035's unit test that might be worth adding here: an image view of level 1 of a two-level R32UI is matched to the level-1 attachment and not to level 0, and a SINT-class image of an R32Uint texture is left as is.

@github-actions github-actions Bot added nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests) docs Documentation game: 010074F013262000 Xenoblade Chronicles 3 labels Nov 3, 2026
@thehobbit85
thehobbit85 force-pushed the codex/readonly-image-feedback branch from 3739434 to 8a83745 Compare November 3, 2026 06:52
@thehobbit85
thehobbit85 force-pushed the codex/readonly-image-feedback branch from 000ae14 to b8e7224 Compare November 3, 2026 08:52
seedds added a commit that referenced this pull request Nov 3, 2026
…pshot: Xenoblade Chronicles 3's cutscenes no longer alternate brightness every frame (#1025)

Xenoblade Chronicles 3's lighting shader 15816d836842bfb5 reads its R32Uint zone mask through fragment image slot 0 while rendering into it, between the game's barriers, to skip zones it has already marked. In an open Metal pass the load returned the previous frame's mask, so each zone bit flipped every frame and rendered cutscenes alternated brightness with blocky bands.

fb_prepare now takes the same-pass snapshot for a read-only, non-atomic fragment image whose resolved view is a single-level 2D view in the registered pixel class and is one level and layer of a colour attachment of the pass; encode_args binds that snapshot (read-only for residency) through the lookup the texture path uses (fb_snapshot_slot). Writable, atomic and class-reinterpreting images keep their bindings, and only sampled texture slots take tile reads. t_feedback checks the image read against an overwritten source, a level-1 view, a SINT reinterpretation left as is, a sampled slot and an image slot of the same number, and the view's ownership through serial-table teardown; 7 of its checks fail without the change. In game (Haseenesss, Apple M2, Events Theater chapter 1's Colony Sigma battle) 8 of 10 bursts flipped brightness before and at most 0.28 of frames after, at a GPU p50 of 50.4 -> 53.0 ms. The reference entry's symptom names the flicker, so the symptom index finds it.

Fixes #1009

Co-authored-by: thehobbit85 <4012041+thehobbit85@users.noreply.github.com>
Co-authored-by: Dorkington <43831512+Dorkington@users.noreply.github.com>
Co-authored-by: Haseenesss <18624035+Haseenesss@users.noreply.github.com>
@seedds

seedds commented Nov 3, 2026

Copy link
Copy Markdown
Owner

Landed on main as f5d5327, your two commits squashed into one, with you, Dorkington (the original patch in #1009) and Haseenesss (the in-game measurements and the level/class checks) credited as co-authors. Thank you.

Added on landing: the reference entry's Symptom now names the cutscene flicker, so the symptom index finds it, and the game note gives the measurements and marks the fix resolved. Open there: if the shader loads the mask at its own pixel, a tile read (as sampled textures have) would avoid the extra snapshots' cost (GPU p50 50.4 → 53.0 ms in the Colony Sigma scene).

Checked: make -C dm/gfx/tests GAME=010074F013262000 run passes (feedback 135 checks, 7 fail with main's nvn_mtl.m). regress.py check on Breath of the Wild: PASS (58.6 → 59.8 fps, no new RED). A Tears of the Kingdom run was void (neither baseline nor candidate reached a frame).

@seedds seedds closed this Nov 3, 2026
@thehobbit85
thehobbit85 deleted the codex/readonly-image-feedback branch November 4, 2026 02:47
Know-It-All-Brother pushed a commit to Know-It-All-Brother/nxnative that referenced this pull request Nov 9, 2026
…pshot: Xenoblade Chronicles 3's cutscenes no longer alternate brightness every frame (seedds#1025)

Xenoblade Chronicles 3's lighting shader 15816d836842bfb5 reads its R32Uint zone mask through fragment image slot 0 while rendering into it, between the game's barriers, to skip zones it has already marked. In an open Metal pass the load returned the previous frame's mask, so each zone bit flipped every frame and rendered cutscenes alternated brightness with blocky bands.

fb_prepare now takes the same-pass snapshot for a read-only, non-atomic fragment image whose resolved view is a single-level 2D view in the registered pixel class and is one level and layer of a colour attachment of the pass; encode_args binds that snapshot (read-only for residency) through the lookup the texture path uses (fb_snapshot_slot). Writable, atomic and class-reinterpreting images keep their bindings, and only sampled texture slots take tile reads. t_feedback checks the image read against an overwritten source, a level-1 view, a SINT reinterpretation left as is, a sampled slot and an image slot of the same number, and the view's ownership through serial-table teardown; 7 of its checks fail without the change. In game (Haseenesss, Apple M2, Events Theater chapter 1's Colony Sigma battle) 8 of 10 bursts flipped brightness before and at most 0.28 of frames after, at a GPU p50 of 50.4 -> 53.0 ms. The reference entry's symptom names the flicker, so the symptom index finds it.

Fixes seedds#1009

Co-authored-by: thehobbit85 <4012041+thehobbit85@users.noreply.github.com>
Co-authored-by: Dorkington <43831512+Dorkington@users.noreply.github.com>
Co-authored-by: Haseenesss <18624035+Haseenesss@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation game: 010074F013262000 Xenoblade Chronicles 3 nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Xenoblade Chronicles 3: rendered cutscenes flicker every frame (lighting shader reads its own attachment as a read-only image)

3 participants