Skip to content

Special register 0x84 reads as a 1 x 1 fragment, so a pixel shader that divides by its fragment's size no longer makes +Inf and a black sea - #2373

Closed
bymaxmarc wants to merge 1 commit into
seedds:mainfrom
bymaxmarc:pr/sr84-fragment-size
Closed

bymaxmarc wants to merge 1 commit into
seedds:mainfrom
bymaxmarc:pr/sr84-fragment-size

Conversation

@bymaxmarc

@bymaxmarc bymaxmarc commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

What and why

Pixel shaders of a Switch 2 game read special register 0x84 with S2UR and take its bytes 1 and 2 with PRMT, multiply them and divide by the product. Every one of Nintendo Switch Sports Resort's (04008E001F098000) 612 reads, in 611 pixel shaders, does this. The bytes are the fragment's width and height under variable-rate shading; that meaning is inferred from this use. The register is not in Ryujinx's SReg list, and EmitSReg read it as 0, so 1 / (0 x 0) gave +Inf. The sea shaders (agl AnnaSea: 01a6b85e2f6ea3d4, b3203175c76cda20, 2271b2e0293b3c55) made NaN at the horizon and the auto-exposure spread it: the main menu's sea was black with a band of coloured noise above it. The avatar material c30f0edd6c669a61 takes the register into a shading factor, and the avatar's hair and clothes drew flat black.

The fix, in the translator (InstEmitAmpereAlu.cs AS2r): register 0x84 answers a 1 x 1 fragment (0x00010100), since the host shades every pixel. Any other special register the SReg list does not know now leaves a translator note (S2R of special register 0x.., which the translator does not know: read as 0) instead of a silent 0.

This is a chain change. dmsh_xlate was rebuilt with dm/tools/native_tools.sh dmsh_xlate from main's sources plus this change (Microsoft's .NET SDK 10.0.403, universal arm64 + x86_64, no weak imports; --source-sha fcb37fccd2f53012 on both, as main, since that digest covers only Program.cs and the csproj). The lock was updated with tool_lock.py --update-lock (dmsh_xlate 90c1264b7831364c -> 09dfad6902250b49), and dmsh_chain.py --header gives d24f05cf9d78 -> 3df0ed1c9c0a. The build is not byte-reproducible (native_tools.sh: each link gets its own UUID). If you would rather build the binary yourself, take the source and test commit and rebuild with native_tools.sh, tool_lock.py --update-lock and dmsh_chain.py --header.

Reference entry: The fragment size register (special register 0x84) reads 1 x 1, symptom index regenerated.

This PR and the uniform-tests PR (#2372) each change dm/gfx/nvn/dmsh_chain.h, so whichever lands second needs dmsh_chain.py --header run again on top of the first.

Checked

  • Tests on this branch (main ccd834e + 1 commit), Apple-silicon Mac, macOS 27.2:
    • DMSH_TEST_XLATE=dm/gfx/tools/dmsh_xlate_bin/dmsh_xlate python3 dm/gfx/tests/test_dmsh_ampere_alu.py: 47 tests pass, FragmentSize among them (the game's own S2UR, UPRMT and UIMAD encodings; the product folds to 1, and the register is 65792). With main's translator, the two FragmentSize tests fail and the other 45 pass.
    • python3 dm/gfx/tools/tool_lock.py --check-tools: passes. dm/tools/tests/test_nsp_chain.py: 17 tests OK.
    • python -m unittest discover -s dm/tools/tests: 1,469 tests OK, 27 skipped, as on main.
    • python -m unittest discover -s dm/gfx/tests -p 'test_*.py' with DMSH_TEST_XLATE set: 491 tests, the 489 that main has plus the 2 new ones. The same 10 modules fail to import on this branch and on main, because they load a game profile at import and DM_GAME was not set (test_gfpak, test_kpka, test_monolith, test_mt_framework, test_needle, test_pg_shader, test_pkz, test_shader_split_progress, test_snowdrop, test_sumo). No other failure.
    • make -k -C dm/gfx/tests GAME=010015100B514000 -j8 run (M2 Max): exit 0, every check passed.
  • Translator output against main's, as native_tools.sh asks: I ran dmsh_build.py on 304 of Nintendo Switch Sports Resort's shaders once in a main tree and once in this branch, each with its own translator. The 304 were the 4 named above and 300 vertex and pixel shader keys drawn at random from its 10,718. The MSL of 412 of the 486 outputs is identical. The other 74 shaders all read 0x84. That is more than the 611 above suggests. The 612 reads were counted in the code stored in the game's shader packages, so the likely reason is that many shader keys share one code body; this was not checked. Their MSL differs in one line only, the folded value of that read: float _N = float(0); becomes float(1). The reflection JSON differs only in its chain field, the metallibs only for those 74, and no new translator note appears.
  • Games: Nintendo Switch Sports Resort 04008E001F098000 v65536 on the user's Mac (Apple silicon, macOS 27.2). On the app rebuilt on the fork's work branch (an earlier main with the same source change), the main menu's sea is blue, the noise band and washed-out exposure are gone, and two default avatars show their colours in the avatar editor. That app was not rebuilt from this branch: the game has no profile on main.
  • regress.py: not run. Only this game has a build on this Mac. The change reaches only shaders that read special register 0x84 (Ampere, NVN2), so it most likely reaches the other Switch 2 games (Mario Kart World, Donkey Kong Bananza, PRAGMATA). Their shaders were not rebuilt here.

Docs

  • The finding is in docs/reference/shaders.md, and python3 dm/tools/docs_symptoms.py was run (the symptom index)
  • A chain tool changed: dmsh_chain.py --header was run and dm/gfx/nvn/dmsh_chain.h is part of the change, with dmsh_chain.lock and the rebuilt dmsh_xlate
  • No game files, keys or data extracted from a game in the change or the attachments

…s Resort's island sea is no longer black

Pixel shaders read special register 0x84 (S2UR) and divide by the product of its bytes 1 and 2, the fragment's size under variable-rate shading (inferred from the reads in Nintendo Switch Sports Resort, which all take bytes 1 and 2 with PRMT, multiply them and divide by the product). The register is not in Ryujinx's SReg list and EmitSReg read it as 0, so 1 / (0 x 0) gave +Inf: the sea shaders (agl AnnaSea, 01a6b85e2f6ea3d4, b3203175c76cda20, 2271b2e0293b3c55) made NaN at the horizon, the auto-exposure spread it, and the main menu's sea was black with a band of coloured noise above it. The avatar material c30f0edd6c669a61 takes the register into a shading factor, and the avatar's hair and clothes drew flat black.

- Translator: AS2r answers 0x84 with 0x00010100 (the host shades every pixel); any other special register the SReg list lacks leaves a translator note instead of a silent 0
- dmsh_xlate rebuilt with dm/tools/native_tools.sh dmsh_xlate on main ccd834e (Microsoft .NET SDK 10.0.403, universal arm64 + x86_64, no weak imports, --source-sha fcb37fccd2f53012 on both as before); lock updated with tool_lock.py --update-lock (dmsh_xlate 90c1264b7831364c -> 09dfad6902250b49) and dmsh_chain.h regenerated (chain d24f05cf9d78 -> 3df0ed1c9c0a)
- Test: test_dmsh_ampere_alu.py FragmentSize (the game's S2UR / UPRMT / UIMAD encodings); its two tests fail with main's translator, and all 47 pass with this one
- docs/reference/shaders.md entry (the sea and the avatar); symptom index regenerated

Translator output against main's on 304 of the game's shaders (4 that read 0x84 and 300 vertex and pixel shaders drawn at random; dmsh_build.py with each tree's translator): the MSL of 412 of 486 outputs is identical, and the other 74 differ only in the folded constant of that read, float(0) -> float(1); the reflection JSON differs only in its chain field, and the metallibs only for those 74 shaders. Checked in the game on the fork's work branch with an earlier main: the sea is blue, the noise and the washed-out exposure are gone, and default avatars show their colours. Needs a full shader rebuild of every game.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
biosed pushed a commit to biosed/nxnative-android that referenced this pull request Nov 11, 2026
Report Ampere special register 0x84 as a 1 x 1 fragment and make other unknown special registers visible in translator notes. Cover S2R, S2UR, byte extraction, and the diagnostic with Linux SPIR-V tests.\n\nAdapted from seedds/nxnative#2373.
seedds added a commit that referenced this pull request Nov 12, 2026
…so Nintendo Switch Sports Resort's sea is no longer black and its avatars' hair and clothes are no longer flat black (PR #2103): S2UR of system register 0x84 (Mesa NAK's NAK_SV_VARIABLE_RATE) is the fragment's size in pixels, X in bits 8..15 and Y in bits 16..23, which variable rate shading makes 2 or 4. The translator did not know the register and gave 0, the default for an unknown one; 612 of the game's 11,059 shaders, all pixel shaders, read it, and the sea shaders (01a6b85e2f6ea3d4, b3203175c76cda20, 2271b2e0293b3c55) divide a blend weight by the fragment's area: an infinite weight, NaN, written as 0 by blend_zero, with black dots of NaN noise spread by the auto-exposure; the avatar material c30f0edd6c669a61 takes it into a shading factor. SReg.VariableRate (132, Decoders/InstDecoders.cs) reads 0x10100 in EmitSReg (Instructions/InstEmitMove.cs), which Maxwell's S2R and Ampere's S2R / S2UR (InstEmitAmpereAlu.AS2r) both go through: the host shades every pixel. Chosen over bymaxmarc's PR #2373 (the same value in AS2r only, with the avatars' material found); FifteenPen found the black sea first. The translator was rebuilt with Microsoft's .NET SDK 10.0.404 (native_tools.sh; Homebrew's links its own OpenSSL and is refused), dmsh_chain.lock updated: chain change, DMSH_CHAIN 3856a9225d68, every game's shaders are translated again once. New reference entry (The fragment's shading-rate size reads 1 x 1), symptom index regenerated. Checked: test_dmsh_ampere_mem with DMSH_TEST_XLATE, 86 tests (ShadingRate: float(65792) for colour 0).

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

seedds commented Nov 12, 2026

Copy link
Copy Markdown
Owner

Thanks for this: the same fix landed from #2103 as b581efc, which reads 0x10100 for system register 0x84 in EmitSReg, the path both Maxwell's S2R and Ampere's S2R / S2UR go through, rather than in AS2r only. Your finding about the avatars' material shader (c30f0edd6c669a61) is in the game's note.

@seedds seedds closed this Nov 12, 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