Skip to content

2026-11-01 claude/gfx-nvn-api-2026-11-01-r2: the 14 missing NVN entry points, refused MSAA branches, ACNH program hash

Batch 2026-11-01-r2, orchestration gfx-nvn-api. Mac (Apple M5), local only.

NVN entry points (gfx/src/nvnapi/, api_cmd.c, api_device.c, api_program.c, api_texture.c)

names.txt gains the 14 names titles look up (Animal Crossing 13, Mario Kart 8 Deluxe nvnDeviceWaitForError), in the same commit as their prototypes (nvn_protos_ext.h) and implementations: NVN 537 of 537 entry points implemented.

entry point behaviour
nvnCommandBufferBind{UniformBuffers,StorageBuffers,Textures,Images,SeparateTextures,SeparateSamplers}Deferred bind like the plain variants (arrays copied into the recording)
nvnCommandBufferOverrideSubtileSize, nvnCommandBufferSetZCullZF32CompressionEnable hints, no-ops (Metal's tile renderer chooses tile size and depth compression)
nvnProgramGetSubtileSize 0 x 0 (the driver's choice)
nvnCommandBufferSetControlMemoryCallbackEnabled no-op (control memory is host-allocated)
nvnCommandBufferSetProvokingVertexMode mode kept, a non-zero mode reported once; flat outputs keep Metal's provoking vertex
nvnDeviceSetAutomaticCheckpointsMask no-op like the other checkpoint setters
nvnDeviceWaitForError returns "no error" at once; calls counted, first one logged
nvnTextureBuilderGetViewOffset same computation as nvnTextureGetViewOffset, from the builder's description

The parameter lists are inferred from the neighbouring entry points of each family (no public header has them; nxnative lists them as names only). An xref scan of Animal Crossing's main module (string -> loader -> GOT slot -> function-pointer global -> readers) shows the loader stores the 13 pointers and nothing reads them back, so no call site depends on the exact lists.

tools/gen_a32_nvn.py now reads nvn_protos_ext.h as well: runtime/src/a32/a32_nvn_sigs.inc has a row for all 537 entry points (was 477), so 32-bit titles get marshalled stubs for every newer entry point (MK8D's "no host implementation or signature for nvnDeviceWaitForError" is gone).

Refused copy / Downsample branches (mtl_replay.m)

xcopy_draw, a generic copy pass, replaces the three refusals: copy_t2t_draw (depth/multisampled/3D copy with another size or format), resolve_int (integer Downsample across formats / of an array layer) and resolve_generic (depth <-> float, integer <-> float Downsample). It reads the source with read() (2D, array, 3D, multisampled, multisampled array or their depth counterparts; cube faces as layers) at the scaled pixel and writes colour or [[depth(any)]]: multisampled destinations per sample, a float colour Downsample averages samples, anything else takes sample 0 (as the 2D engine); integer <-> float by value, depth <-> integer by bits. Draw copies cover every layer / depth slice of the region (one draw each), and copies between different sample counts no longer reach Metal's blit (which aborts).

Animal Crossing "program hash 0x3 is not a shader hash"

A host layout bug, not the guest double-job race: diagnosed on the title, the value 0x3 sat in prog_t.hash[4] (object offset 0x88) where SetShaders had stored 0. The title's NVNprogram is followed by another object at +0x88 at the latest; prog_t assumed 152 bytes, so Initialize / Finalize zeroed and SetShaders wrote the neighbour's memory, and the neighbour's writes became stage hashes. Stage hashes now live in a host table keyed by program address (gfx_program_hashes), used by BindProgram and the transform feedback / tessellation setters; the object mirrors stages 0..3 only (below +0x88, static assert) for programs copied by value; a scribbled object is reported once. Possible earlier symptom: with a compute program (stage 5, +0x90) SetShaders would have overwritten that neighbour.

Tests

t_nvn_ext.c (every name resolves, BindTexturesDeferred draws, view offsets, getters), t_nvn_ext_prog.c (no write past +0x88, scribbled and by-value programs bind), t_msaa_copy.c (7 readback cases).

Status

See integrators/claude-gfx-nvn-api-2026-11-01-r2.md.