Skip to content

gfx-stages-real (2026-10-27, batch b7, overnight): stage parameters from the control section, the rest of the geometry / tessellation / transform feedback replay, sample shading, MSAA views and resolves, sparse fallback

Branch claude/gfx-stages-real-2026-10-27-b7 (Mac, Metal). Contract: gfx.md section 13 ("Parameters the control section carries", "Covered since b7"). Tests: gfx/tests/t_control.c (new), t_stages.c (five new cases), t_msaa.c (new), t_xlate_stages.inc (TLD.MS, sample shading, capture void).

What changed

  1. GLSLC control section reader (gfx/src/shader/control.c, control.h): header fields (length, code / constant bounds, stage, compute group), a transform feedback reader (gfx_control_xfb, hardware varying-table form -> gfx_xfb_layout) and a tess eval parameter reader (gfx_control_tess, Maxwell TESSELLATION_MODE form). gfx_shader_register (blob.c) calls them for every stage and registers what it finds in the stages.h registries; NAPPLE_GFX_CTL_DUMP=<dir> dumps every control section + code area; the log says once when a title creates a geometry / tess control / tess eval program. The XFB / tess offsets are a hypothesis (see "Findings"): the readers take them only when the bytes are non-zero and self-consistent, else nothing is registered and explicit gfx_program_set_* registrations (now keyed by the stage that feeds the rasterizer: geometry, else tess eval, else vertex) work as before.
  2. Replay (gfx/src/mtl_stages.inc, mtl_replay.m, translator): GS instancing (sp[3] invocations, SR_INVOCATION_ID, per-thread output slots), triangle strips with adjacency (GL 4.6 table 10.1), indirect draws with stages / during capture (arguments read at encode time), transform feedback of geometry shader output (GPU-counted sessions: xfbgpu serial decomposition kernel, counters blitted to / from the control memory, DrawTransformFeedback indirect through xfbargs), vertex capture after a pass-through / unusable geometry program.
  3. Sample shading (api_cmd.c, obj.h, mtl_replay.m, mtl_priv.h, mtl_shader.m, translator): p_bind_program.sample_shading from nvnProgramSetSampleShading, pipe_key.sample_shading when the pass is multisampled, ShaderKey.sample_shading -> the fragment function declares [[sample_id]] (Metal then runs it per sample). MSAA texture views: TLD.MS translates to texture2d_ms / texture2d_ms_array reads by sample index (the binding path of multisampled textures already existed). Resolves: a generic pass (resolve_generic) for what the fixed-function resolve refuses (another destination format, another size, integer array slices); float formats average the samples, integer ones take sample 0. Sparse (api_texture.c): nvnTextureBuilder/TextureGetSparseTileLayout fill a tile layout (64 KiB page shape per texel size, tiled level count); sparse textures are fully resident (logged once).
  4. Capture-only vertex void (ShaderKey.capture_void): the capture pipeline's vertex function returns nothing and rasterizationEnabled = NO.
  5. BC block-grid copy-back: already done by mtl_bcrt_flush (b6 port of nxnative's bcrt), t_bcrt passes; the stale docs/todo.md row is removed. The copy-in (loading the old blocks into the shadow before a partial compression pass) is still not done.

TRANSLATOR_VERSION 12 -> 13 (generated MSL changed: GS prologue, fragment prologue, capture void).

Findings

  • No title on hand uses these stages. RomFS shader archives of Link's Awakening (1178 vertex / 2443 fragment controls), Mario Kart 8 Deluxe (4184 / 4244, one geometry program in Common/Gfx/gsys_resource.Nin_NX_NVN.release.sarc, GLSLC version 8, 0x7d0 bytes, no data outside the known fields) and Animal Crossing (only 73 unique controls found: its .zs files likely need a dictionary; not pursued) plus the GLSLC outputs in the four titles' main modules were scanned (scanner in the session scratchpad, not committed). No tess control / tess eval program anywhere, and no vertex control has data in +0x030..+0x6ef (the only region large enough for varying tables), so the XFB layout / tess parameter offsets could not be reverse-engineered from data. The NVN driver itself is not in the game modules (only the nvnProgramSetShaders name string the game passes to GetProcAddress), so its reader could not be disassembled either. Control-section facts that are new: +0x034..+0x05b (u32 0x340 then 01 ff ff ... ff, 33 bytes) in some vertex and fragment outputs of LAS and MK8D; MK8D's version-8 outputs also carry +0x0b8..+0x0d8 and +0x6ec words in a few programs and +0x778..+0x790 in most; t_control reproduces a real LAS vertex section.
  • Super Mario Odyssey creates 6 geometry programs (3 distinct) and Animal Crossing 4 (2 distinct) during their first 45 s (title creates geometry programs in the boot logs; NAPPLE_GFX_CTL_DUMP caught their control sections and code: SMO GLSLC version 9, 0x880 bytes, line-strip output with 64 / 64 / 2 maximum vertices; ACNH version 0xf, 0x850 bytes, triangle-strip output with 6 vertices; all one invocation, 3 / 5 / 8 OUT instructions, no transform feedback block). None was bound by a draw within 45 s (no stage-path log line), so the replay has not run them in a game yet. Offline check: all five translate through the geometry kernel path and compile in Metal for input primitives of 1, 2 and 3 vertices (maxwell_translate + newLibraryWithSource in a scratch harness; stage slot masks 0x6 for SMO's, 0x3ca / 0x3ce for ACNH's). Their code is game data and is not committed. MK8D's one geometry program (RomFS) was not created during its boot.
  • The GS control sections carry nothing in +0x030..+0x6ef either and nothing stage-specific after +0x714 beyond the common words, so a geometry program's parameters all come from its SPH (as the replay already reads them).
  • Sample shading is observable only through the key / MSL (a constant-colour fragment program renders the same per sample); t_msaa checks the stored state, that the sample-shaded draw renders and the [[sample_id]] declaration (t_xlate).
  • GS + tessellation together and transform feedback of tessellation output stay out: Metal exposes neither the tessellated primitives nor their order; a software tessellator would be needed.

Verified vs not

Item Status
make -C gfx lib adapter test with MTL_DEBUG_LAYER=1 (incl. t_control, t_stages 8 cases, t_msaa, t_xlate) tested: ALL TESTS PASSED
Control section reader on a real vertex section + synthetic XFB / tess sections tested (t_control)
GS instancing, strip adjacency, indirect GS draw, XFB of GS output + indirect DrawTransformFeedback tested (t_stages)
TLD.MS read of a 4x texture, sample-shaded draw, RGBA8 -> BGRA8 resolve, sparse layout + init tested (t_msaa)
Headless 45 s boots LAS / SMO / ACNH / MK8D (audio off, game lock, MTL_DEBUG_LAYER=1) verified: LAS / SMO / MK8D exit 0, gfx warning sets identical to the b6 base logs (LAS 0, SMO 0, MK8D 2x nvnDeviceWaitForError), 0 skipped draws, 0 validation lines; ACNH's first run hit the known 1-in-4 start crash (exit 133, guest frame in sdk+0xd4314, retried: the second run crashed inside Metal's binary-archive parser (MTLLibraryDataWithArchive parseArchive) loading the pipelines-v13.metalar the crashed first run had left; with that file moved aside the third run exits 0 with 3286 frames, the base's one warning plus one command buffer call on a null or finalized command buffer ignored (recording lifecycle, not in the changed code; ACNH's start is the known flaky one)); LAS's first run drew 929 frames vs 2646 on the base log (CPU contention from other sessions' builds suspected, retried: 924 frames again, and main's own runtime run right after under the same conditions also draws 925 (boot-LAS-main-now.log, same 20348 draws): the machine, not this branch). New info lines: title creates geometry programs on SMO and ACNH. Logs: side-session-notes boot-*-b7.log, boot-*-b7-retry.log
Any title using the stages / sample shading / sparse textures not verified: none of the four does in the first 45 s

Files

gfx/src/shader/control.c + control.h (new), blob.c, maxwell.h (append-only: TEXTYPE_2D_MS_ARRAY, ShaderKey.sample_shading / capture_void), maxwell_translate.c, api_cmd.c, api_state.c, api_texture.c, obj.h, mtl_priv.h, mtl_replay.m, mtl_shader.m, mtl_stages.inc; tests t_control.c (new), t_msaa.c (new), t_stages.c, t_xlate.m, t_xlate_stages.inc; docs/architecture/gfx.md section 13, docs/todo.md.