Files
nxnative-easy/docs
seeddsandv0lumehi 8a4b1dc820 Faster NVN → Metal translation from GitHub PR #62: Metal encoding on its own thread (on by default on iOS), uniform shadows copied by a worker, uniform updates kept across render passes
Taken from v0lumehi's PR #62, the parts that hold for every game (none is gated by a title, build or profile; all but the Mac front end reach only the NVN games):
- nvn_rec.h: the replay's Metal calls (encoders, draws, state, argument table writes, commits, event signals) go to a worker thread as records, in the same order. The submit thread waits for it at the end of each submission and before anything that waits for the GPU or uses another queue. On by default on iOS, DM_NVN_ENC_THREAD=1 on the Mac.
- Uniform shadows of 512 bytes or more (ubp_get) are copied by a worker; a commit waits for the copies queued before it, a CPU read of a shadow for its own (DM_NVN_UB_COPY_THREAD=0 copies on the submit thread as before).
- Deferred inline uniform updates are kept across render pass ends (DM_NVN_UB_LAZY, at most 160) and land before the next copy, a draw that reads their memory other than through uniform bindings the shadows cover, or the end of the submission, instead of in a copy encoder after every pass.
- A two-level address → pool cache (by 4 KB page, then the last 8 pools found), and the pass dump check answered first when it is off.
- DM_NVN_GPUPROF=f or f1-f2: every encoder's GPU start and duration, from Metal 4 counter heap timestamps.
- Mac: the drawable follows the window in pixels (in fullscreen it had stayed at 1280x720), the process declares latency-critical activity, and DM_METAL_HUD=1 shows Apple's performance HUD (on the iPhone too, from Documents/env.txt).

On top of the PR:
- A dispatch lands the kept updates first when it reads or writes their memory through a storage buffer or its indirect arguments, or reads a part of a uniform binding the shadows cannot give (ubd_conflict_cs, with the per-stage check it shares with draws, ubd_conflict_sh). Dispatches open the copy encoder through blit_open, which never landed them, so they ran before the updates. Transform feedback draws land them first, as every pass end did before.
- A deferred update that a newer one covers whole is dropped from the landing list. Breath of the Wild rewrites the same 64 bytes before every draw group, so the list held up to 160 repeats that every binding lookup scanned: the port cost 16,800 instructions a draw against main's 13,200, and costs 13,500 with the drop.
- The encoding thread records encoder labels (DM_NVN_ENC_LABEL) and counter heap timestamps (DM_NVN_GPUPROF): without them either diagnostic stopped the host on iOS.
- DM_NVN_UBP_CHECK waits for the copy worker before it reads a shadow. It compared unfilled shadows (293,447 "different"); now 232,000 equal and 0 different with the encoding thread on, 234,000 and 0 with it off.

Left out: Breath of the Wild above 60 fps and above 1080p (rewriter cuts for its build and a profile key), the Mac GPU filler and screen mode switch, and the iOS launcher.

Breath of the Wild on the Mac (the Great Plateau save, 60 fps cap, beside main under the same load): submit thread 4.30 against 4.48 ms a frame, GPU p95 6.2 against 7.0-7.3 ms, 1.000 of real time, no red lines; with the encoding thread the submit thread takes 3.16 ms (9,700 instructions a draw). t_nvn 100/100 with the encoding thread off and on; the hosts for TOTK and Hades build.

Co-authored-by: v0lumehi <34629096+v0lumehi@users.noreply.github.com>
2026-10-23 09:29:42 +08:00
..