Graphics round 2: LAS in-game white-out root cause (partial round, stopped at a clean point)¶
Objective¶
Fix general graphics correctness shared by Link's Awakening (LAS) and Super Mario Odyssey (SMO); first target: LAS in-game scenes rendering flat white.
Root cause (LAS white room) - FIXED¶
nvnVertexAttribState format 0x69 (NVN_FORMAT_RGB10A2SN, packed signed normal/tangent) had no Metal vertex format in mtl_vertex_format. The attribute was dropped,
the vertex shader read zeros for the tangent frame, the PBR fragment shaders normalised a zero vector (NaN), and the final fmin(x, 4.0) (Metal fmin returns the
non-NaN operand) turned every lit pixel into the HDR clamp value 4.0, which tone mapping and bloom showed as white. Fix: 0x69 -> MTLVertexFormatInt1010102Normalized,
plus the whole *_UI2F/*_I2F family (0x51..0x68, plain integer formats feeding float attributes) and BGRA8 (0x9A).
Evidence: before work/shots/user-first-room.png, after work/shots/las-room-after-fix.png (same scene, Marin's house, scripted input).
Second bug found on the way (compute) - FIXED¶
Compute shaders read uniform buffers through global memory; bind_stage stored host addresses in the driver-constant UBO table (c0+0x210) instead of Metal GPU
addresses (SSBOs already used gpu_va). The light-culling compute (30x17 tiles) therefore saw no lights. Now all three lights per tile are listed.
Files¶
gfx/src/mtl_tex.m(vertex formats),gfx/src/mtl_replay.m(UBO gpu_va; diagnostics),gfx/src/mtl_shader.m,gfx/src/shader/maxwell_translate.c|maxwell.h(probe),gfx/src/mtl_core.m(event screenshots),gfx/src/shader/blob.c(compute control dump at log level 3),gfx/tests/t_vfmt.m(new regression test),gfx/tests/t_xlate.m(corpus lists failing shaders).
Diagnostics added (all env switches, no per-game logic)¶
NAPPLE_GFX_EVENT_FS=<fs hash>starts a relative frame clock at the first draw with that fragment program (machine-load independent captures);NAPPLE_GFX_EVENT_SHOTS=n,n,..+NAPPLE_GFX_EVENT_PREFIXtake screenshots at relative frames;NAPPLE_DRAWLOGframes become relative.NAPPLE_DRAWLOG_QUIET,_GRID(sample grid of pixel changes per draw, with_EACH),_UBO=<words>, per-pass RT dumps from every pass end, vertex attribute/stream and compute (UBO/SSBO after) logging.NAPPLE_SHADER_PROBE=<fs>:<pc hex>:<rA>:<rB>:<rC>[:scale][;...](+_PERIOD): a fragment program writes intermediate registers as its colour (debugging; compiled synchronously). UseNAPPLE_SAVE_MSL=1with an empty shader cache to get// pcannotations.- Scripted LAS path to the first room:
NAPPLE_MOVIE_PACE=0 NAPPLE_TURBO=1000, L+R at ~60 s then A every 4 s (reaches Marin's room in ~6 minutes under load).
Findings not yet acted on¶
- Translator: 6 LAS shaders fail (3x
bezel_pbr*ps: unknown opcode0x7e06...probably HSETP2 immediate/cbuf form;decal_uberps: AL2P0xefa0+ indexed attribute;water_uberps: bindless TEX.B handle not from a cbuf). SMO corpus 128/128. mtl_pipe_getskips draws while an async pipeline compiles (visible as pops and wasted frames); a fallback or prewarm is needed.OP_DRAW_TEXTURE, transform feedback, multi-draw-indirect-count, separate texture/sampler binding remain unimplemented;nvnMemoryPoolMapVirtualis implemented (t_vpool).- SMO text: a run by the other engineer (work/smo/j3-3500.png) already shows glyphs ("Look Around"); not re-verified by me (lock taken before my SMO run).
- Render scale (rscale plumbing), settings, 60 fps work, shader disk-cache prewarm: not started.
Tests¶
make -C gfx test passes (new t_vfmt), Metal validation layer on. Game runs: LAS to Marin's room headless; intro movie and title confirmed earlier in the session (work/g2/a1-, a3-).
Risks¶
Debug switches add a few getenv calls on hot paths (cached statics); remove if profiling shows cost.