Graphics: libnapple_gfx (NVN on Metal) first working slice¶
Objective¶
Give Napple its own graphics driver for NVN games: the guest calls NVN, the host implements it on Metal. Plug-in interface for the runtime, shader translation without .NET, headless tests that need no game.
Files (all under gfx/ unless noted)¶
include/napple_gfx.h: public C API (init/config, hooks, settings, NVN loader, fault/write-prepare, vsync, screenshot, diagnostics).src/core.c, timeline.c, hostobj.h, obj.h, internal.h: lifecycle, settings, handle table, NVN name table + unimplemented stubs (tools/gen_nvn_tables.shgenerates the per-name AArch64 stubs), submission timeline and deferred destruction.src/api_*.c, tex_xfer.c: NVN entry points (510 of 523 names): device, queue, sync, events, pools, buffers, textures, views, samplers, descriptor pools, programs, state objects, command buffers (recording), windows/present. Prototypes are compile-checked againstnvnapi/nvn_protos.h.src/dirty.c: write-protection dirty tracking of texture storage.src/mtl_core.m, mtl_tex.m, mtl_shader.m, mtl_replay.m: Metal device, pool registry, scratch ring, replay thread, presentation and screenshots, texture cache/upload/views/samplers, shader and pipeline compilation (async), the op interpreter.src/shader/maxwell_translate.c, maxwell.h, blob.c: native Maxwell -> MSL translator (imported from LASRecomp, MIT) with the Vulkan GLSL path, per-game tint, debug hooks and mirror mode removed; depth-texture sampling and a clean binding contract added.src/tex/: Tegra block-linear layout/deswizzle, format table, BC decode (from D2R/MacNativeGames, MIT; deko3d layout code, zlib).src/nvnapi/: nvn.h (h1k421, MIT), prototypes and name list.adapter/gfx_napple.c: runtime adapter.Makefile,tests/.runtime/Makefile:GFX=nappleoption (integration hook).docs/gfx-design.md,docs/gfx-interface.md.
Logic (flow)¶
guest NVN call -> record op into cmdbuf host buffer ----EndRecording----> immutable cmdrec (handle)
queue submit -> job queue -> replay thread: op interpreter -> Metal render/blit/compute encoders (one MTLCommandBuffer per submission)
|-- textures: snapshot -> MTLTexture cache; dirty? -> deswizzle + upload (protect, then read)
|-- program hashes -> translate (Maxwell->MSL) -> MTLFunction -> pipeline cache (async)
'-- UBOs snapshotted per draw into a scratch ring
GPU completion -> timeline advance -> syncs, events, counters, retirement of freed resources
present job -> scale pass to CAMetalLayer (presentAfterMinimumDuration) or offscreen texture; optional PNG screenshot
Decisions¶
See docs/gfx-design.md (native translator, descriptor-snapshot textures, per-draw UBO snapshot, virtual vsync). The reference renderer (14k lines, TotK-specific) was studied but not copied: its hacks (frame generation, UI ghosting, shader seed lists, per-game patches) are out; only data/algorithms that are generic were reused.
Tested (make -C gfx test, MTL_DEBUG_LAYER=1, validation clean)¶
t_triangle: NVN API with hand-assembled Maxwell shaders, offscreen render, copy to memory, pixel checks, window present, screenshot (PNG verified visually), sync/acquire.t_sample: CPU texel write -> deswizzle upload -> sampling through texture/sampler pools; CPU rewrite picked up (dirty tracking).t_state: depth test across draws (D32F), scissored clear via draw fallback, clear folding.t_tex: block-linear addressing/round trips (2D/array/3D/cube/BC/ASTC), known layout sizes, format table.t_xlate: translator goldens;NAPPLE_SHADER_CORPUS=<dir>runs extracted shaders: SMO sample 128/128 translate and compile, Link's Awakening 671/679 (earlier run: 295/295 sampled compile).- Translator regression after cleanup: MSL of 799 real shaders identical to baseline except the removed tint code.
Risks / not done (ordered blockers)¶
- Runtime: nvdrv service (see gfx-interface.md section 9). No real game frame has been rendered yet; everything above is synthetic.
- Not implemented / skipped (diagnosed by name or log once): virtual/physical memory pools (
nvnMemoryPoolMapVirtual), buffer textures (texel buffers), separate texture/sampler binding (c2 +0x168/+0x568), image load/store binding (BindImage), transform feedback, geometry/tessellation stages, MSAA depth resolve details, scaled/format-converting texture copies, conditional rendering, primitive-restart with custom index, dual-source blend validation, logic ops, line width, UI2F/RGB10A2 vertex formats. - Translator: bindless TEX.B and a few unknown opcodes (8 of 679 Link's Awakening shaders); internal-resolution-scale plumbing (the
rscaletable exists, bound as identity). - Per-pass load/store optimisation (all attachments Load/Store); GPU deswizzle (CPU today); texture eviction heuristics; shader prewarm list from a previous run is implemented but untested.
- Render targets are not written back to guest memory (CPU readback of GPU-rendered textures only through CopyTextureToBuffer).
- Per-game: NVN API version (Link's Awakening 55.x assumed; SMO older, to be read from the binary), heap window for global memory.