Repository navigation
Conversation
Reverse all four nibbles when guest RGBA4 storage crosses into native Metal textures. Keep guest-backed sampling, copies, raw format views and writeback coherent, and cover those paths with literal GPU regressions. Document the reproduced dark Mushroom Way battle and corrected scene.
Track written mip levels, array slices and depth planes so converted views do not overwrite independent native writes. Close attachment conversion before auxiliary clears, invalidate views after texture-to-buffer copies, and resolve inverse RGBA4 integer images before checking write usage. Add literal GPU regressions for all four reviewed failures.
MKLOL
force-pushed
the
codex/nvn-rgba4-component-order
branch
from
November 2, 2026 23:37
ada5e80 to
1f493a4
Compare
MKLOL
marked this pull request as ready for review
November 3, 2026 01:49
…ersed The earlier commits converted RGBA4 through a representation layer of its own for format views, integer images, scaled copies and 3D mirrors: cases no game is known to reach. RGBA4 now uses the existing packed-format crossings (uploads, buffer <-> texture copies, ReadTexels, storage readbacks, and the in-place mapping of linear and buffer textures), reversing the four nibbles where the RGB565 family exchanges red and blue. Same-size views in another format keep the RGB565 family's documented limit. Main's own clang-tidy fixture findings are left to their pull requests (seedds#958, seedds#959, seedds#979). Super Mario RPG gets a smoke route from a save on Mushroom Way into the battle the fix lights, so regress.py plays it.
MKLOL
force-pushed
the
codex/nvn-rgba4-component-order
branch
from
November 3, 2026 02:12
1f493a4 to
3898bf6
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
In Super Mario RPG 1.0.1, Mushroom Way's battle ground and lower foliage lose their sunlight while Mario and the UI stay lit. The baked shadowmask is an RGBA4 texture. NVN's RGBA4 holds R, G, B, A from bit 0 up; Metal's
ABGR4Unormholds A, B, G, R. The host kept the guest's bits, so every channel read another channel's nibble. The shadowmask's texels are mostly0x0f00, sampled through a (B, G, R, A) mapping: red should read 1, and read 0 with green 1.RGBA4 now goes through the conversion the RGB565 family already has (#183), reversing all four nibbles where that family exchanges red and blue:
nvn_fmt_to_mtlmaps RGBA4 with reversed components.rb_packed_shiftreturns 4 for it.rb_swap_rowsandrb_swap.metalreverse the nibbles for shift 4.tex_by_idresets all four components, not just red and blue, for host textures that already hold Metal's order.Uploads, initial fills, buffer ↔ texture copies,
ReadTexelsand storage readbacks convert at the same crossings as before. Linear and buffer textures, which read guest memory in place, take the reversed mapping when sampled. The change is general host behaviour, with no game-specific rule; the shader chain is unchanged.Performance note: like the RGB565 family, RGBA4 uploads are now repacked on the CPU and no longer use the GPU deswizzle. The measured cost on Super Mario RPG is in "Checked".
The RGB565 family has a documented limit that applies here too: a same-size view in another format (an R16 view of an RGBA4 texture) sees Metal's order. No game is known to make one.
The PR also adds a smoke route for Super Mario RPG (
smoke.tsv), soregress.pyplays it. The route starts from a save on Mushroom Way and goes into the battle this fix lights.Screenshots
The same copied Mushroom Way checkpoint, unedited native Metal snapshots. These were taken during the first round of this PR. The smoke route below reproduces the same pair: dark on main, lit with this change.
Before (unmodified
65501fbb):After:
Checked
Apple M5 Max, macOS 27.0.1, Xcode 27.2.
t_view_swizzlepasses 491 checks with Metal API validation. New cases sample the RGBA4 texels0x8421and0x0f00(every nibble different): uploaded and on a buffer, with and without the (B, G, R, A) mapping, cleared by Metal, and filled fromCPU_UNCACHEDandCPU_NO_ACCESSpools. Another new case covers RGB565 read in place.t_readbackpasses 148 / 149 cases (fast wake on / off). Its RGB565-family copy, clear andReadTexelscases now also run for RGBA4.t_view_swizzle's 491 checks and 6 oft_readback's 149 cases. Case 425 reproduces the game's red 0 / green 1.make -C dm/gfx/tests GAME=0100BC0018138000 run) passes, as do the CPU texture tests (5,344 / 5,344).GAME: host unit tests, ASan/UBSan/TSan, CPU-only gfx tests, the Mac host for three profiles, every gfx test compiled, the iOS compile check (258 files) and the unsigned iOS app.main. The only findings over the baseline aremain's two int_geometry_hazards.m, which Release the geometry fixture program and check readback allocation #958, Release registered Metal programs after each geometry-hazard test #959 and C lint: t_geometry_hazards checks its readback buffer, and notes who owns its program #979 fix, so this PR's lint check stays red until one of them lands.regress.py check, full mode, one run each, main65501fbbagainst this change):@upload_ms/@upload_gpu_ms). So moving RGBA4 to the CPU costs nothing measurable here.regress.py impact main..HEADThe function-level impact selects the three games that have usage manifests. None of them is on this Mac, so they are unchecked. Whether they use RGBA4 textures at all is unknown: the change only acts on that format.
01007EF00011E000rb_packed_shift,rb_swap_rows)0100A3D008C5C0000100F2C0115B6000nvnTextureReadTexels×250100535012974000rb_swap.metal)126 profiles have no usage manifest.
regress.py check (full) on Super Mario RPG with the new route
Baseline
65501fbb65501fb, candidate: this change; Apple M5 Max, macOS 27.0.1, Xcode 27.2; 795.4 s0100BC0018138000The earlier chain-only check on both local games (Super Mario RPG and 13 Sentinels) passed: 82032/82032 and 751/751 shaders, no changed MSL, no interface failures.
Docs
docs/reference/rendering.mdhas a new entry, "RGBA4 textures in Metal's channel order". The symptom index is regenerated.