Skip to content

A colour clear under a partial channel mask leaves the other channels: Switch Sports Resort's outfits were black in its menus - #2273

Closed
FifteenPen wants to merge 1 commit into
seedds:mainfrom
FifteenPen:pr/clear-channel-mask
Closed

FifteenPen wants to merge 1 commit into
seedds:mainfrom
FifteenPen:pr/clear-channel-mask

Conversation

@FifteenPen

Copy link
Copy Markdown
Contributor

What and why

In Nintendo Switch Sports Resort (04008E001F098000, v65536), characters' outfits are black in the menus (Customize, Select User) and right in play. Skin, face and hair are lit either way.

Cause. The menu scene blurs its screen-space shadow target back and forth between two 2048x1152 targets. From one frame's records:

chmask bind 0xfffffff6            (slot 0: G and B)
clear slot0 own mask 0xf          then a blur draw, wm=0xfffffff6      x7, alternating the two targets
chmask bind 0xffffffff
  • A pass before the blurs writes all four channels of the target.
  • The material shader c30f0edd6c669a61 samples the target through a view swizzle of (G, A, B, R), takes .xyw, and multiplies its lighting by them. So it reads the target's G, A and R.
  • The host cleared by the clear's own mask alone, so R and A were 0 from the first blur on, and the lighting came out 0.
  • In play the target is written once, with no blur, so nothing was lost.
  • The skin shader binds a white 4x4 texture in that slot, which is why skin stayed lit.

Measured in the outfit's own draw, with a debug copy of its pixel shader that paints the sampled channels as threshold-coded stripes:

Channel of the target Menu Golf round
A < 0.02 ≥ 0.75
R < 0.02 ≥ 0.75
min(R, 1), the light factor < 0.02 ≥ 0.75

The lights were there in the menu: the light loop ran, the cluster held about 3 lights, and the environment cube had data.

Fix. Under a channel mask state that lets only some of the target's channels through, a colour clear writes its own mask's channels among those (clear_mask in nvn_mtl.m). It is drawn as a triangle with that mask, like the existing partial-mask clears. The scans that plan passes ahead of the replay (sdx_walk, hoist_try, fuse_try) follow the state's binds as they follow the scissor, so such a clear is not taken for one that replaces the target's content.

What this does not settle. With every channel of the target off, the clear stays whole, as on main. I first made the clear follow the state in every case, and that broke the same game: it clears its main colour target right after a shadow pass drawn with the mask at 0 (clear slot0 own mask 0xf state 0), and with that clear held back the Select User background came out black with outlines. I don't know what a console does under a mask of 0. This is the narrowest rule both cases bear out: it changes main's behaviour only where a game has masked off some channels but not all. The reference entry says so under Open.

Checked

  • check_t_clear_chmask (new, GPU, 35 checks). On main (6a6a018) cases 1, 2, 3 and 5 fail; with the change all pass:
    • a clear under a mask of G and B keeps R and A, from the state or from a bind in the same records;
    • the clear's own mask and the state's combine;
    • a mask of nothing leaves the clear whole;
    • another target's mask does not count.
  • The other clear tests pass: check_t_clear_layers (30), check_t_clear_ds (59), check_t_clear_msaa (32), check_t_clear_tex (51).
  • check_t_geometry (1,929) and check_t_copy_alias (126) pass.
  • test_docs.py and the symptom index pass.
  • lint_c.py tidy: nothing new. The four file/check pairs above the baseline are in t_blend_zero.m, t_clear_msaa.m, t_mip_extent.m and t_pso_archive.m, none of which this touches.
  • The game (M4 Pro, macOS 27.2): outfits are lit on Customize and Select User, and Select User's background is right. That run was on a tree with this change plus the game's other open PRs; it does not boot on main alone.

Not checked

  • Other games. Any game that clears under a partial channel mask now keeps the masked channels; none was run.
  • What a console does with a clear under a channel mask of 0 (above).

The game's profile is in #2107.

…'s channels through leaves the others: Nintendo Switch Sports Resort's outfits were black in its menus (its shadow target's R and A cleared under a mask of G and B)
@github-actions github-actions Bot added nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests) docs Documentation labels Nov 11, 2026
FifteenPen added a commit to FifteenPen/nxnative that referenced this pull request Nov 11, 2026
seedds#2273 (black outfits in menus); Miis with seedds#2274 and the player's own files; golf's far ground right with the seedds#2040 series
seedds added a commit that referenced this pull request Nov 12, 2026
…ts its profile, note, games table row and README line: it boots from a dump folder through the safety and strap screens, the title, Select User and the sport menu into a doubles tennis match at 60 fps on the M6 (frame time p50 16.67 / p95 17.3-17.9 ms, CPU about 92% of one core), and starts again from its save; bymaxmarc played matches with motion from a Joy-Con 2 and a DualSense in a 37-minute Mac session (PR #2387). The profile is the NSP script's provisional one (heap check: a 7.47 GB heap, mem_reserve, depth_mode zero; address regions alias +2.82 GB of 4.38 GB in the first 15 s) with render_cond on: the game wraps draws in render conditions, and drawn anyway, Select User's background is grey instead of the court and the outfits black (in the menus only; FifteenPen measured the outfit material reading 0 from R and A of a shadow target the menu blurs under a channel mask of G and B). With the key both are right on the same route from the same save (A/B on the M6), so PR #2273 (a clear under a partial channel mask keeps the other channels) is not needed: what a console does there is not in the game's files, its nvnCommandBufferClearColor (sdk+0x652380) jumps through a pointer the system's NVN driver fills. The shader tables are the build's, byte for byte both port PRs'; pso_manifest.tsv is this Mac's own recording (3,474 shaders, 1,868 render, 41 compute and 22 task / mesh pipelines over a first start, the menus and a match), since none of the PRs' render or mesh pipeline keys matches the current host's. No game patch and no rewriter cut. The reference entries that name the game link its note; the conditional rendering entry lists it. Checked: test_docs, test_nsp_chain, test_env_switches, test_patches; the host test suite (make -k -C dm/host/tests run, 50 programs, 0 failed), check_t_pfv under Metal validation, test_dmsh_ampere_mem with the committed translator (86); lint_python (nothing new) and lint_c tidy on the changed C files (at the baseline after the fixes folded into their commits); regress.py check against 96259d4 (one run per side, headless, M6): Bastion 59.7 / 58.3 fps, its 70 s shot's purple border an old flake of both sides (the same colour in 10 of 19 earlier runs, origin/main's included), Obra Dinn 59.5 / 59.8 fps, CHANGED by animation phase, 0 changed MSL and no new red line on either. Not run: a point played on this Mac (the keyboard cannot serve: Shake and the ten scripted moves of PR #2386 move the racket but do not serve), sports other than tennis, the iPhone.

Co-authored-by: FifteenPen <354289956+FifteenPen@users.noreply.github.com>
Co-authored-by: bymaxmarc <354601562+bymaxmarc@users.noreply.github.com>
@seedds

seedds commented Nov 12, 2026

Copy link
Copy Markdown
Owner

Thanks for the careful measurement: the outfit material reading 0 from R and A of the blurred shadow target is right, and it is in the game's note. The cause is earlier, though: the game wraps those draws in render conditions, and the host drew them anyway. With the profile's render_cond on and without this change, Select User shows the court behind the player and the outfits are lit, on the same route from the same save (A/B on an M6); that also explains why following the channel mask everywhere blackened the background. What a console does with a clear under a channel mask is not in the game's files (its nvnCommandBufferClearColor jumps through a pointer the system's NVN driver fills), so the narrower rule is not landed. The profile with render_cond landed as f4b7e77.

@seedds seedds closed this Nov 12, 2026
Kayyluhh added a commit to Kayyluhh/nxnative that referenced this pull request Nov 14, 2026
…nly some of its target's channels through leaves the others: Nintendo Switch Sports Resort's outfits were black in its menus (its shadow target's R and A
Kayyluhh added a commit to Kayyluhh/nxnative that referenced this pull request Nov 14, 2026
…closed, not landed; the cause was render conditions the host drew through, fixed by Nintendo Switch Sports Resort's profile with render_cond (f4b7e77)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation 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.

2 participants