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¶
- 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, MaxwellTESSELLATION_MODEform).gfx_shader_register(blob.c) calls them for every stage and registers what it finds in thestages.hregistries;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 explicitgfx_program_set_*registrations (now keyed by the stage that feeds the rasterizer: geometry, else tess eval, else vertex) work as before. - 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:xfbgpuserial decomposition kernel, counters blitted to / from the control memory,DrawTransformFeedbackindirect throughxfbargs), vertex capture after a pass-through / unusable geometry program. - Sample shading (
api_cmd.c,obj.h,mtl_replay.m,mtl_priv.h,mtl_shader.m, translator):p_bind_program.sample_shadingfromnvnProgramSetSampleShading,pipe_key.sample_shadingwhen the pass is multisampled,ShaderKey.sample_shading-> the fragment function declares[[sample_id]](Metal then runs it per sample). MSAA texture views:TLD.MStranslates totexture2d_ms/texture2d_ms_arrayreads 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/TextureGetSparseTileLayoutfill a tile layout (64 KiB page shape per texel size, tiled level count); sparse textures are fully resident (logged once). - Capture-only
vertex void(ShaderKey.capture_void): the capture pipeline's vertex function returns nothing andrasterizationEnabled = NO. - BC block-grid copy-back: already done by
mtl_bcrt_flush(b6 port of nxnative's bcrt),t_bcrtpasses; the staledocs/todo.mdrow 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.zsfiles likely need a dictionary; not pursued) plus the GLSLC outputs in the four titles'mainmodules 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 thenvnProgramSetShadersname string the game passes toGetProcAddress), so its reader could not be disassembled either. Control-section facts that are new: +0x034..+0x05b (u32 0x340 then01 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_controlreproduces 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 programsin the boot logs;NAPPLE_GFX_CTL_DUMPcaught 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 / 8OUTinstructions, 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+newLibraryWithSourcein 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_msaachecks 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.