diff --git a/dm/gfx/nvn/dmsh_chain.h b/dm/gfx/nvn/dmsh_chain.h index 45e097ff9b..694122a041 100644 --- a/dm/gfx/nvn/dmsh_chain.h +++ b/dm/gfx/nvn/dmsh_chain.h @@ -1,3 +1,3 @@ // Generated file (dm/gfx/tools/dmsh_chain.py --header): the B shader library translation chain version this source tree expects (default options), one for every game; // the host loads only /b-/. Run dmsh_chain.py --header after changing the chain's tools and commit this file with the change -#define DMSH_CHAIN "d403eb0683df" +#define DMSH_CHAIN "cdef7a05ad69" diff --git a/dm/gfx/tests/test_dmsh_ampere_mem.py b/dm/gfx/tests/test_dmsh_ampere_mem.py index 7651bb650f..6a77a227d2 100644 --- a/dm/gfx/tests/test_dmsh_ampere_mem.py +++ b/dm/gfx/tests/test_dmsh_ampere_mem.py @@ -205,6 +205,20 @@ def test_another_form_is_refused(self): self.assertIn('0x925 form 2', log) +@unittest.skipUnless(os.environ.get('DMSH_TEST_XLATE'), 'set DMSH_TEST_XLATE to the offline translator') +class ShadingRate(unittest.TestCase): + """S2UR of system register 0x84, the fragment's size in pixels (X in bits 8..15, Y in 16..23), as Nintendo Switch Sports Resort's sea shaders encode it (2271b2e0293b3c55 +0x2600): + the host shades every pixel, so 1 x 1. It read 0 as an unknown register, and the sea's colour, divided by the fragment's area, was NaN""" + run_job = PixelCovered.run_job + SPH = PixelCovered.SPH + S2UR = (0x000e220000008400, 0x00000000000479c3) # S2UR UR4, SR 0x84 + I2F_R0 = (0x03ffe20008201400, 0x0000000400007c45) # I2F R0, UR4 as the game reads it (+0x2d90), waiting on every barrier: colour 0's x, 65792 for 1 x 1 + + def test_reads_one_by_one(self): + msl, log = self.run_job(self.S2UR, self.I2F_R0) + self.assertIsNotNone(msl, log) + self.assertIn('float(65792)', msl) # 0 before (an unknown system register) + @unittest.skipUnless(os.environ.get('DMSH_TEST_XLATE'), 'set DMSH_TEST_XLATE to the offline translator') class DepthRegister(unittest.TestCase): """A pixel shader's depth output follows its colours at four registers per enabled render target, then the sample mask register. Ocarina of Time's agl_common depth pass diff --git a/dm/gfx/tools/dmsh_chain.lock b/dm/gfx/tools/dmsh_chain.lock index 8e40ca0adb..30d175d361 100644 --- a/dm/gfx/tools/dmsh_chain.lock +++ b/dm/gfx/tools/dmsh_chain.lock @@ -1,7 +1,7 @@ # Translation chain tool fingerprints (written by tool_lock.py --update-lock; regenerate after changing tools and commit it) -dmsh_xlate.dll f2d1a53df023344a -Ryujinx.Graphics.Shader.dll cbb2a0c24869eefa -Ryujinx.Common.dll ffe1bca8ca60ee61 +dmsh_xlate.dll 852359e6a7f8b536 +Ryujinx.Graphics.Shader.dll e0290d86b5577c98 +Ryujinx.Common.dll c7032c0b587a4493 xlate_source 8986080b40b79cab spirv-cross e37945d8e441d34d metal_macosx Apple metal version 32023.922 (metalfe-32023.922.4) diff --git a/dm/gfx/tools/dmsh_xlate_bin/Ryujinx.Common.dll b/dm/gfx/tools/dmsh_xlate_bin/Ryujinx.Common.dll index 656de0b001..64d9d38cb1 100644 Binary files a/dm/gfx/tools/dmsh_xlate_bin/Ryujinx.Common.dll and b/dm/gfx/tools/dmsh_xlate_bin/Ryujinx.Common.dll differ diff --git a/dm/gfx/tools/dmsh_xlate_bin/Ryujinx.Graphics.Shader.dll b/dm/gfx/tools/dmsh_xlate_bin/Ryujinx.Graphics.Shader.dll index 316cdb0629..3c51d746d7 100644 Binary files a/dm/gfx/tools/dmsh_xlate_bin/Ryujinx.Graphics.Shader.dll and b/dm/gfx/tools/dmsh_xlate_bin/Ryujinx.Graphics.Shader.dll differ diff --git a/dm/gfx/tools/dmsh_xlate_bin/Spv.Generator.dll b/dm/gfx/tools/dmsh_xlate_bin/Spv.Generator.dll index 763b5d97e5..94fca9f8c9 100644 Binary files a/dm/gfx/tools/dmsh_xlate_bin/Spv.Generator.dll and b/dm/gfx/tools/dmsh_xlate_bin/Spv.Generator.dll differ diff --git a/dm/gfx/tools/dmsh_xlate_bin/dmsh_xlate.dll b/dm/gfx/tools/dmsh_xlate_bin/dmsh_xlate.dll index 339d0b7f05..05dc27bc15 100644 Binary files a/dm/gfx/tools/dmsh_xlate_bin/dmsh_xlate.dll and b/dm/gfx/tools/dmsh_xlate_bin/dmsh_xlate.dll differ diff --git a/dm/gfx/tools/dmsh_xlate_bin/dmsh_xlate.pdb b/dm/gfx/tools/dmsh_xlate_bin/dmsh_xlate.pdb index fb37bb1b8b..5ca6a9643f 100644 Binary files a/dm/gfx/tools/dmsh_xlate_bin/dmsh_xlate.pdb and b/dm/gfx/tools/dmsh_xlate_bin/dmsh_xlate.pdb differ diff --git a/dm/third_party/ryujinx/README.md b/dm/third_party/ryujinx/README.md index 1771607980..5f1cf6de7e 100644 --- a/dm/third_party/ryujinx/README.md +++ b/dm/third_party/ryujinx/README.md @@ -17,5 +17,7 @@ Three projects from Ryujinx 1.3.2: `Ryujinx.Graphics.Shader`, `Ryujinx.Common` a - `Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs` (`TryGenerateBoundedUniformLoad`), `Translation/ResourceManager.cs`, `StructuredIr/ShaderProperties.cs`, `Constants.cs` and `Translation/TranslationFlags.cs`: the Metal path (`TranslationFlags.MetalUniformBounds`) gives 0 for a read of a game uniform slot (c[3] to c[16]) past the size the game bound, as Maxwell does, comparing the word's index with the slot's size from a table in bank 25 that the host fills. Upstream relied on Vulkan's robust buffer access, which Metal does not have. See [reads past a uniform binding's size](../../../docs/reference/shaders.md#reads-past-a-uniform-bindings-size), found on Paper Mario: The Thousand-Year Door (GitHub issue #1860). +- `Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs` and `Instructions/InstEmitMove.cs` (`EmitSReg`): system register 0x84 (`SReg.VariableRate`, the fragment's size in pixels, X in bits 8..15 and Y in bits 16..23) reads 1 x 1, as the host shades every pixel. It read 0 as an unknown register, and Nintendo Switch Sports Resort's sea divided by the area: NaN, a black sea + The shader translation tool in `dm/gfx/tools/dmsh_xlate` references them to translate the original Switch shaders to SPIR-V. License: MIT, see `LICENSE.txt`. Upstream: . diff --git a/dm/third_party/ryujinx/src/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs b/dm/third_party/ryujinx/src/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs index 8bf5671acf..eca36175a6 100644 --- a/dm/third_party/ryujinx/src/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs +++ b/dm/third_party/ryujinx/src/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs @@ -219,6 +219,7 @@ enum SReg CircularQueueEntryIndex = 97, CircularQueueEntryAddressLow = 98, CircularQueueEntryAddressHigh = 99, + VariableRate = 132, } enum RoundMode diff --git a/dm/third_party/ryujinx/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs b/dm/third_party/ryujinx/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs index 71e6292a96..592ac60d8d 100644 --- a/dm/third_party/ryujinx/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs +++ b/dm/third_party/ryujinx/src/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs @@ -109,6 +109,12 @@ private static Operand EmitSReg(EmitterContext context, SReg sreg) src = ConstF(1); // TODO: Use value from Y direction GPU register. break; + // The fragment's size in pixels: X in bits 8..15, Y in bits 16..23 (Mesa NAK's NAK_SV_VARIABLE_RATE). The host shades every pixel, so 1 x 1: the 0 an + // unknown register reads made the area 0 and what a shader divided by it infinite (Nintendo Switch Sports Resort's sea came out black) + case SReg.VariableRate: + src = Const(0x00010100); + break; + case SReg.ThreadKill: src = context.TranslatorContext.Definitions.Stage == ShaderStage.Fragment ? context.Load(StorageKind.Input, IoVariable.ThreadKill) : Const(0); break; diff --git a/docs/agents/symptoms.md b/docs/agents/symptoms.md index 87b796d560..e6d805cecc 100644 --- a/docs/agents/symptoms.md +++ b/docs/agents/symptoms.md @@ -961,6 +961,7 @@ One line per symptom in the [subsystem references](../reference/): what you see - the scene goes white with a yellow fringe: a few objects hold the largest value their target can, and bloom spreads it over the picture. Clamping a shader's terms one by one finds a value that is a register's stale contents. Or characters lit from an environment cube turn bright white in certain places only, and the cube's mean is far above its value elsewhere. → [A deferred write under a false guard](../reference/shaders.md#a-deferred-write-under-a-false-guard) - `Ampere instruction f63` (or `f6c`, `364`, `367`) `is not implemented`, or `SURED form 0310e000`, in the build. → [Ampere forms with a handle in a register, and SURED's other operations](../reference/shaders.md#ampere-forms-with-a-handle-in-a-register-and-sureds-other-operations) - `Ampere instruction 21b at … is not implemented` (`…0405721b`) or `Ampere instruction 925 at 0x0 is not implemented` (`040e0200000e4000 …7925`) in the build. → [Ampere BMSK and the read of a covered pixel](../reference/shaders.md#ampere-bmsk-and-the-read-of-a-covered-pixel) +- a Switch 2 game's water or another full-screen surface draws black (or as a flat dark colour under the fog) while its normals and depth are right; a texture dump of the target shows the colour channel at 0 where the surface is, and building the shader without fast maths changes nothing. → [The fragment's shading-rate size reads 1 x 1](../reference/shaders.md#the-fragments-shading-rate-size-reads-1-x-1) - no wrong picture seen so far. In the MSL, a shader with a 0x886 vote has `if (false)` and `false ? a : b` where a uniform predicate guards a value or a branch, and a lane guard on the same number reads `simd_all` or `simd_any` of its own predicate. → [VOTEU writes the uniform predicate](../reference/shaders.md#voteu-writes-the-uniform-predicate) - `[RED] : the translator deleted N bindless accesses` in the build (Losses a shader build reports; `Failed to find handle source for bindless access` in its reflection's notes), for a shader with TXD through a uniform register (0xf6c). The MSL has no gradient sample, and what it read comes back 0. → [TXD through a uniform register kept no handle](../reference/shaders.md#txd-through-a-uniform-register-kept-no-handle) - `Failed to find handle source for bindless access` in the build's translator notes, for a read whose handle is two words of the game's own uniform buffer, and what it read comes back 0. Or nothing in the log: the read binds `fp_t_cb3_` or `fp_t_cb2_`, a texture word with no sampler word, so its sampler is `samp_tex`'s guess, where the shader's other reads of that word bind both. → [Handles built under a guard the read's guard cannot be tied to](../reference/shaders.md#handles-built-under-a-guard-the-reads-guard-cannot-be-tied-to) diff --git a/docs/reference/shaders.md b/docs/reference/shaders.md index 1302b00c2a..780a0cd973 100644 --- a/docs/reference/shaders.md +++ b/docs/reference/shaders.md @@ -1308,6 +1308,14 @@ Finding, keying and translating shaders (`dm/gfx/tools/shader_split.py`, `dmsh_b - **Fix:** `ABmsk` follows PTX's `bmsk` definition, its `.W` at bit 75 as SGXT's (only the clamping form has been seen). `APixelCovered` loads a new `IoVariable.HelperInvocation`, Metal's `simd_is_helper_thread()`, as an integer; any other form of 0x925 stays refused (chain change). `test_dmsh_ampere_mem.py` covers both. - **Found on:** [Star Fox](../../dm/games/0400E88025B1A000/README.md): 1 compute and 2 pixel shaders, none asked for in play so far, so neither instruction has run. Neither opcode is in the shaders of Donkey Kong Bananza, Splatoon Raiders, Mario Tennis Fever or Pokopia. +### The fragment's shading-rate size reads 1 x 1 + +- **Symptom:** a Switch 2 game's water or another full-screen surface draws black (or as a flat dark colour under the fog) while its normals and depth are right; a texture dump of the target shows the colour channel at 0 where the surface is, and building the shader without fast maths changes nothing. +- **Cause:** S2UR of system register 0x84 (Mesa NAK's `NAK_SV_VARIABLE_RATE`): 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. Nintendo Switch Sports Resort's sea shaders divide a blend weight by the fragment's area: 0, an infinite weight, and the colour came out NaN, which `blend_zero` (`dm_az`) writes as 0. +- **Fix:** `SReg.VariableRate` (132, `Decoders/InstDecoders.cs`) reads `0x10100` in `EmitSReg` (`Instructions/InstEmitMove.cs`): the host shades every pixel, so 1 x 1 (chain change). +- **Found on:** Nintendo Switch Sports Resort (`04008E001F098000`, v65536; no profile yet): 612 of its 11,059 shaders, all pixel shaders, read the register; its title screen's sea was black and draws blue with the change, on the Mac. +- **Check:** `test_dmsh_ampere_mem.py` (`ShadingRate`, with `DMSH_TEST_XLATE`). + ### VOTEU writes the uniform predicate - **Symptom:** no wrong picture seen so far. In the MSL, a shader with a 0x886 vote has `if (false)` and `false ? a : b` where a uniform predicate guards a value or a branch, and a lane guard on the same number reads `simd_all` or `simd_any` of its own predicate.