Files
nxnative-easy/dm/third_party/ryujinx/README.md
T
seedds 87e647f5bb Age of Calamity's last red lines, fixed for every game: buffer textures read by 1D integer fetches are bound as 2D rows, and shadow vertex inputs replace the CPU vertex conversion
- Row wrap: Maxwell reads a texture buffer and a 1D texture with the same instructions (TLD with integer coordinates only, TLDS Texture1DLodZero) and the bound descriptor decides. Without a type answer the translator read such a slot as 2D at (x, 0) and the host bound a dummy for a buffer texture, so it read 0. The vendored translator (InstEmitTexture.cs, marked nxnative:) now addresses those fetches as (x & 0x3FFF, x >> 14) and reports the slot (fetch1d); the host binds a buffer texture there as a linear 2D texture of 16,384-texel rows over the same memory (tbuf_as_2d, nvn_tbuf_rows), cached by texture serial. KTGL's light and decal lists: Age of Calamity 16,438 shaders, Three Houses 321 (torches now light its map, blood decals are lit); FFT 5, Monster Hunter Rise 3
- Shadow vertex inputs: for each vertex shader with integer inputs the build writes <key>_bv (dmsh_vin.py), which declares every integer input again as float4 and as the other-sign integer at attribute indices 16-30 and ORs them into the input; the host sends each attribute to the index of its format's class and the zero buffer to the others (vin_ct, vin_on). Age of Calamity's Mac battle: 0 vertices converted on the CPU instead of 545,000 per submission, submit + present 10.0 -> 5.7 ms a frame, CPU 3.97 -> 2.44 W, the same picture. DM_NVN_VCONV_CPU=1 gives the CPU conversion back
- A texture type mismatch no view bridges is logged once per shader and slot with its texture ID, NVN target and formats (tex_mis_note); it showed Age of Calamity's remaining one: 3D slots 8 and 9 of three vertex shaders bound with 2D textures (bound as is, as before)
- Chain procedure for every profile: the 18 with game files on this drive rebuilt for Mac and iOS (c1_full.tsv / cb_extent.tsv: header lines only; new cb_extent.tsv for Hades II, Cuphead and Monster Hunter Rise), the others header-only with their lists carried over. Mac smoke runs of 13 games: no new red lines
- Tests: t_tbuf2d (row aliases against texture_buffer views), t_vin (shadow inputs against the CPU conversion, every format class), test_dmsh_vin.py, nvn_tbuf_rows in t_nvn
2026-10-22 00:28:29 +08:00

2.5 KiB

Ryujinx shader translator (vendored subset)

Three projects from Ryujinx 1.3.2: Ryujinx.Graphics.Shader, Ryujinx.Common and Spv.Generator, with these local changes:

  • Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs, GenerateTextureSample:

    • Texel offsets that are not constants (Maxwell AOFFI / TLD4.PTP offsets read from a register) are built with OpCompositeConstruct and passed as Offset. A four-offset gather becomes four one-offset gathers, each giving component w. Upstream wrote them as an OpConstantComposite with ConstOffset(s), which is invalid SPIR-V; SPIRV-Cross threw nullptr and the shader never built (2026-10-18: 8 shaders in Wonder, Link's Awakening, Tears of the Kingdom and Age of Calamity). Marked nxnative: in the code.
    • Texture coordinates that are NaN are replaced by 0 (the nfZero select). This came with the first import.
  • Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs, TryGetOperation / IsSameOperand: a bindless handle that reaches the texture instruction through a phi is followed through the phis that feed it (a handle kept in a loop variable comes back through the loop's own phi), and two constant buffer | constant handles count as the same value. Upstream compared only the phi's direct inputs, so such handles fell to the texture-pool array of 32 (*_t2d_cb0_40000000), which cannot be bound; 218 pixel shaders in Monster Hunter Rise (2026-10-20). Marked nxnative: in the code.

  • Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs, EmitTex (TLD 1D with integer coordinates only) and EmitTexs (TLDS Texture1DLodZero), with IGpuAccessor.RecordTexelFetch1D: Maxwell reads a 1D texture and a texture buffer with the same instruction, and the bound descriptor decides. When the texture type query does not answer "buffer", the read is translated as 2D at (x & 0x3FFF, x >> 14) instead of (x, 0), and the handle is reported. A 1D texture (one row at most 16384 wide on the host) reads the same; the host binds a texture buffer in such a slot as a linear 2D texture of 16384-texel rows over its memory (nvn_mtl.m tbuf_as_2d). Upstream read (x, 0) and the host had only a dummy texture to give: the light and decal lists of Age of Calamity (at least 15,000 shaders) and Three Houses read zeros (2026-10-21). Marked nxnative: in the code.

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: https://github.com/Ryubing.