# The Legend of Zelda: Ocarina of Time (`0400486024DEC000`) A Switch 2 game: update v131072, SDK 22.2.8 (`Ounce`). Nintendo EPD's engine (sead, agl, gsys, Havok, CRIWARE; project `Owl`, with Tears of the Kingdom's file layout) on NVN2. Modules `main`, `subsdk0` (NintendoSDK_movie) and `sdk`, no rewriter patches. Status: plays on the Mac through the title, the opening, a new file's first scene, Link's house and Kokiri Forest, with saves, at about 59 fps with the picture close to console video. Mac only so far. ## What it needs | Setting | Where | Why | |---|---|---| | `mem_reserve 0x320000000`, `mem_alias 0x100000000`, `mem_stack 0x10000000`, `app_pool 0x240000000` | [`game.tsv`](game.tsv) | the game asks `SetHeapSize` for 0x18ce00000; the rest as Donkey Kong Bananza, the Switch 2's 9 GB | | `mem_map_limit on` | [`game.tsv`](game.tsv) | a map past the application pool fails with ResultLimitReached, as on Horizon. The SDK's allocator keeps freed pages mapped until a map fails, then gives them back ([PR 1444](https://github.com/seedds/nxnative/pull/1444) reports a black screen leaving the Deku Tree without it; not reached here) | | `sys_resource 0x3000000` | `game.tsv` | 48 MB, as the NPDM declares | | Dynamic-resolution stub | [`patches.py`](patches.py) (frame step rule, `others`) | full resolution unless the launcher's Dynamic resolution is on ([Dynamic resolution](#dynamic-resolution)) | | `draw_barriers game` | `game.tsv` | about 180 full barriers a frame NVN does not ask for: GPU 25 → 18 ms at full resolution. From 7f50222 until the fix in [Draw passes that wait for each other again while images share memory](../../../docs/reference/frame-rate.md#draw-passes-that-wait-for-each-other-again-while-images-share-memory) it had no effect, since the dynamic resolution's family heaps turned it off; with the fix, M1 Max gameplay GPU 22.2 → 20.3 ms; no change on an M2 Air or an M4 Pro | | `tex_flush_upload on`, `tex_dirty_upload on` | `game.tsv` | as Tears of the Kingdom: the CPU writes the terrain tiles' height (R32F), blend and hole (RGBA8) maps into textures after creating them. Without the keys those textures stay zero: flat blue holes in the ground around the path with grass floating in them, and grey stone where the console shows dirt | | `shader_groups`, `shader_note` | `game.tsv` | shaders grouped as `/Shader/`, `/Effect/`, `/Lib/`, `/UI/` | | `pad_layout full` | `game.tsv` | a 3D field and both sticks | Render conditions, which the host evaluates for every game ([Conditional rendering evaluated at replay](../../../docs/reference/rendering.md#conditional-rendering-evaluated-at-replay); until 2026-11-12 the profile key `render_cond`): agl clears a copy of the prepass depth again under a render condition the console skips. Run unconditionally, the opaque pass's `EQUAL` test fails everywhere ([PR 1299](https://github.com/seedds/nxnative/pull/1299)'s finding). ## Files One [NUP container](../../../docs/development.md#the-packing-script-in-detail) of 25.6 GB: `scripts/pack_nup.py` unpacks it to `extract/v131072/`. The RomFS keeps most files as zstd with dictionaries from `Pack/ZsDic.pack.zs`. The movies are AV1. ## Shaders - About 42,850 in [`shader_map.tsv`](shader_map.tsv), including 500 mesh and 20 task shaders in the BNSH programs ([Task and mesh shaders in BNSH](../../../docs/reference/shaders.md#task-and-mesh-shaders-in-bnsh-and-indirect-mesh-draws)), and the movie library's in an NDVE pack ([`nn::ndve`](../../../docs/reference/shaders.md#nnndves-movie-enhancement-file)). - The runs here used a partial Mac library of the keys the playtests asked for, listed in [`shader_seen.tsv`](shader_seen.tsv) (7,276; building them took about 10 minutes when most came from the previous chain's libraries). - Current: [`RED.F32.FTZ.RN`](../../../docs/reference/shaders.md#float-atomics-in-storage-memory) translates. The build's `Attribute offset 0x5C is not valid` (about 2,300 shaders) is a vertex shader's write to an output the translator drops, probably a shading rate, as `nvnCommandBufferBindShadingRateImage` and its family are no-ops: everything shades at full rate, which is right to look at and costs GPU time. Not verified against the hardware. ## Frame rate Measured on an M6 Mac with 16 GB, headless, on one walk with camera turns (`walk.sh` in the work folder). - Current on an M5 Pro MacBook Pro (16-inch, 20-core GPU, 24 GB, macOS 27.2, 2026-11-09), `kokiri-deck` from a deck autosave with the full library of chain `dedc9391c30b`, at full resolution (Dynamic resolution off), two runs a variant: in a window a locked 60 fps (59.8–60.0, frame p99 16.8–19.4 ms, GPU p50 10.2 ms). In fullscreen on its ProMotion screen it held 40 fps (39.6–40.1) at the same GPU time, with every present a 120 Hz refresh late and every other link tick skipped: [The 60 Hz link at 40 fps in fullscreen on a ProMotion screen](../../../docs/reference/frame-rate.md#the-60-hz-link-at-40-fps-in-fullscreen-on-a-promotion-screen). With the 120 Hz link there, 58.8–59.6 fps, frame p99 25.3 ms; with the screen held at its fixed 60 Hz mode, 60.0 fps, frame p99 16.9 ms, which fullscreen now switches to. MetalFX present upscaling to the 3456-wide fullscreen cost no measurable GPU time (59.4–59.7 fps). Resident about 10 GB, GPU allocated 11.2–11.4 GB; the prepare step took 7.3 s on a first start. - Current: the title and menu, a movie behind them, are a locked 60 fps (p50 16.67 ms, GPU span 15.6 ms). - Resolved: the title movie and the cutscene movies stuttered in small regular jerks at that steady 60 fps on a Mac that decodes AV1 with dav1d (M1, M2). The frames were on time and the movie's pictures were not: the player's `advance` (`main+0x3b61ea0`) found no new picture in 546 and 569 of about 2,900 frames of the title on an M2 Pro, and showed the last one again. Two causes, both general: [packets decoded inside the submission](../../../docs/reference/movies.md#nncodec-packets-decoded-inside-the-submission), on the player's one reader thread, which also gives the shown pictures back, and [dav1d one frame at a time](../../../docs/reference/movies.md#dav1d-decoded-one-frame-at-a-time) (15.4 ms a packet in the movie's heaviest five seconds, 122 ms the longest). After: 0 or 1 in 2,567 to 3,036 frames a run (eight runs of 54 s, headless, `DM_PROBE=main:3b61ed8,main:3b61f80` counting the failed and the good calls), and `DecodeAsync` returns in 0.1 ms at the median and 1.1 ms at the 99th percentile, from 1.9 and 120 ms. Open: not run on an M1 or M2 with four performance cores, nor on an iPad; a Mac with a hardware AV1 decoder (M3 on) decodes with VideoToolbox, where only the first cause applied, not measured. - Game speed: the game steps its time by a frame ratio, its base rate (60, or 30) / its int frame rate, whatever the frame took (`main+0x6e6cc4` on v131072), so frames longer than a vsync played in slow motion (seen by the user on a slower Mac; [issue 1375](https://github.com/seedds/nxnative/issues/1375)'s M1 Max ran the title at about 28 fps). With `fps_target 60` the ratio is the host's frame step × base / 60 ([A fixed frame step plays in slow motion on a slow Mac](../../../docs/reference/frame-rate.md#a-fixed-frame-step-plays-in-slow-motion-on-a-slow-mac)), a stub placed by its function's fingerprint ([`patches.py`](patches.py)). Mac runs on the M6 (2026-11-08, a test host on the libraries of chain `3ee493b2c3a4`): the user's play sessions held 60 fps with 2- and 3-vsync holds while areas loaded, game time 1.000 of real time by the host's count. That count is the stub's own; no run on a Mac slow enough to show the slow motion before and after. - Sizes since the host answers graphics scale tier 0 docked ([The graphics scale does not follow the operation mode](../../../docs/reference/services.md#the-graphics-scale-does-not-follow-the-operation-mode)): the game's output is 2560x1440 and its top internal resolution 1920x1080. Every size and time in this note was taken with tier 1, at 0.8 of that (2048x1152 and 1536x864, and the dynamic resolution's levels with them), and has not been taken again. With tier 0 on `main` (two contributors' runs, only that answer changed): M4 Pro (20-core GPU), docked, fullscreen, GPU p50 15.0 ms against 12.9-14.0 ms, 59.0 fps against 55.8-59.8; M5 Pro (20-core GPU, 48 GB), Dynamic resolution off, 24 minutes of play, GPU p50 12.7 ms, 58.4 fps. Open: a slower Mac, and the routes of [`bench.tsv`](bench.tsv), with tier 0. - Current: walking the village at the game's top internal resolution, 1536x864: a locked 60 fps, mean 16.67 ms over each settled window. Per 2,700 frames: 0-8 frames over 25 ms and 0-2 over 33 ms. Frame p95 17.8-18.9 ms with no window, as the game's own loop moves around its fence wait; in a window p95 17.8 ms, 3 over 25 ms and 5-16 over 18 ms per 600. GPU span p50 16.5 ms, summed GPU 15.8 ms, CPU 6 ms, about 101 passes a frame. The heavy stretch where the village opens (about 1,560 draws) holds 16.65-16.8 ms by lowering its resolution, then returns to full. The main pass used to sit at 763x429 at the same GPU time: [marked shaders tested depth late](../../../docs/reference/frame-rate.md#a-hosts-own-store-in-a-fragment-shader-moves-its-depth-test). - Before [pool shaders stopped counting as depth readers](../../../docs/reference/rendering.md#pool-shaders-taken-as-depth-readers): passes 118, 104, 219, 224 in a four-frame cycle, frame times 15, 15, 18, 18.5 ms, mean 16.9 ms. Half the frames rendered the reflection probe's faces with one draw a pass. - The host's GPU figure adds up command buffers, which overlap, so it reads above the real span: 17.5 ms was never a frame's real time. The real span is `@gpu_end_ms − @gpu_beg_ms` in `DM_NVN_TSV`, or the `first start to last end` of `DM_NVN_GPUPROF`. - The roughly 1 ms the [wind fix](../../../docs/reference/shaders.md#dead-code-removal-dropped-a-coordinate-of-a-multi-output-instruction) seemed to add: 0.66 ms of it is the upscaler, 416 → 1076 µs, because its variants then read what they had lost. It is doing its real work. - Attachment traffic was not the cost it looked: 254 MB load and 277 MB store remain, and the cheapest passes move 20 MB in about 30 µs. The pass count was the cost. - Resolved: 25-40 ms frames while the GPU sat idle with the next command buffer committed, 51-75 per 2,700 in a bad run: [the GPU idles with the next command buffer already committed](../../../docs/reference/frame-rate.md#the-gpu-idles-with-the-next-command-buffer-already-committed). What holds Metal's submission queue in the kernel is still unknown. - Resolved: Kakariko Village at 30-70 ms a frame with 1,502 `background render pipeline creation failed … MDB_MAP_FULL` lines after a compacting prewarm of 40,000 rows put 8,693 bases into one archive period: [Metal's archiver store filled by one flush period](../../../docs/reference/rendering.md#metals-archiver-store-filled-by-one-flush-period). - The title movie's network took 14.8 ms of compute a frame here (median from frame 3,000), so the title held 60 fps with almost nothing to spare, and slower GPUs fell below it: [issue 1375](https://github.com/seedds/nxnative/issues/1375), 35 ms on an M1 Max. Since [HMMA chains keep their matrices in the SIMD-group](../../../docs/reference/shaders.md#hmma-chains-keep-their-matrices-in-the-simd-group), 11.4 ms, and the GPU span 11.9 ms instead of 15.6 (26.7 → 18.9 ms of compute on an M4 with 8 GPU cores). Not measured on an M1 Max since. - [Extended products behind spirv-cross's optnone helper](../../../docs/reference/shaders.md#extended-products-behind-spirv-crosss-optnone-helper): measured with `scripts/bench.py` on an M4 Pro with a 20-core GPU (2026-11-09), three runs a variant, medians, each variant with its own warmed pipeline archive and no red lines. Main `c78d7375` (chain `89f44f9b6bb0`) against [PR #1741](https://github.com/seedds/nxnative/pull/1741)'s literal-only rewrite (`e82b21a8fa3b`) and the overloads (`a5eef48f7122`). On `kokiri-deck` (default settings), GPU time was 15.55 / 15.54 / 14.76 ms, frame rate 54.0 / 55.6 / 59.1 fps, and frames over 1.5 vsyncs 210 / 149 / 29 in the 45 s window. On `title-movie` (empty save, `DM_OPMODE=1 DM_UPSCALER=game DM_PRESENT_UPSCALE=off`), GPU time was 12.96 / 12.48 / 12.44 ms, and all three held 60 fps. The shots match. On the PR author's M1 Pro, the title movie went from 26.57 to 28.40 fps with the literal-only rewrite. Not measured on the iPhone. - Current: the title and the opening movies' network runs as the host's kernels ([the NDVE movie network by the host](../../../docs/reference/shaders.md#the-ndve-movie-network-by-the-host), on by default): on an M4 Pro with 20 GPU cores, the `title-movie` route's GPU time 12.78 → 6.77 ms a frame (p50, three runs each, both at 60 fps); on the PR author's M1 Pro with 16, the network alone 36.3 → 10.8 ms. Not measured in the game on an M1-class Mac or the iPhone yet. - In a run that takes shots, each shot costs its frame 60–100 ms, and `DM_NVN_GPUPROF` and pass dumps slow the frames they cover. - Resolved: the castle town market at 9–12 fps on a first visit, with the GPU at 24–26 ms a frame and the main thread busy searching the pipeline archives: [archive searches repeated while the build table is full](../../../docs/reference/frame-rate.md#archive-searches-repeated-while-the-build-table-is-full). Game v131072. On an M5 Mac (macOS 27.0.1, a host near `bd979ced`), CPU translation 113.8 → 6.4 ms a frame and failed searches 9,208 (30.5 s) → 5 per 300 frames, frame p50 107 → 28.5 ms. On an M4 Pro MacBook Pro (24 GB, a host near `1794a35`, real play), failed searches about 49,000 → 5–57 per 300 frames, frame p50 84 → 16–20 ms, main thread 854 → 440–600 ms/s, game time 0.605 → 1.000 of real time. In both, the build table stayed full (384 in flight). Neither is a controlled A/B: the second visits started with the first visit's pipelines prepared (489 more on the M4 Pro), which explains part of the lower GPU time. The drop in failed searches is the change's direct effect. - Resolved: frames of 50–170 ms on entering the castle town, in a second run as in the first, with `new pipelines 1` or `0` and the time under `predictions`: [pulling stand-ins made for predicted keys that are never drawn](../../../docs/reference/frame-rate.md#pulling-stand-ins-made-for-predicted-keys-that-are-never-drawn). Game v131072, M4 Pro, the town entry route (headless, `DM_FPS_HOLD=off`): the three predictions of the route 30.8, 104.4 and 54.5 ms → 0.5, 2.0 and 1.4 ms, pulling pipelines made 244 → 1; frames 910, 1458 and 1787 (48, 132 and 71 ms) are no longer long. The long frames left on the route are texture uploads. - Resolved: frames of 30–64 ms while walking into the castle town, with `new pipelines 0` and `6 re-uploads, 227.4 MB / 32.0 ms`: the loader fills a 1800x1380x8 R16 array (38.5 MB) and three block-compressed textures over several frames while they are drawn, and each use read the whole texture again, up to 8 times in one submission: [a texture written while it is drawn read whole at every use](../../../docs/reference/frame-rate.md#a-texture-written-while-it-is-drawn-read-whole-at-every-use). Now one look a submission and only the written layers: 25 ms at most there. The single-image textures (3072x2096 BC5, 3056x1152) are still read whole, once a submission (open). - Open: frames of 100–650 ms when an area first loads (640 first uploads, 321 MB, in one frame of 294 ms; another 640 ms of submit with 68 MB). - `DM_NVN_TBDR=1` was tried and does not help: p95 19 ms on the same walk, against 17.8. - The first long run on a new chain is slower (p50 60–114 ms), while `MTLCompilerService` compiles pipelines; the next run is not. - [`pso_manifest.tsv`](pso_manifest.tsv) seeds the [prepare step](../../../docs/development.md#the-prepare-step) of a first start: 9,625 rows (8,376 shaders, 1,112 render and 137 compute pipelines) recorded on an M3 Pro Mac (macOS 27.2) with no lists present, over boot, the title and its movie, a new game and the opening, Link's house, Kokiri Forest and a save load ([PR 1493](https://github.com/seedds/nxnative/pull/1493)), and five compute pipelines Saria's greeting builds (Deku Scrub, Business Scrub, Deku Baba), found in [PR 1488](https://github.com/seedds/nxnative/pull/1488). With the list empty, a first start with a cold Metal shader cache showed a black picture with the title music for minutes: one title frame built 11 pipelines and loaded 12 shader libraries in 252.6 s on that Mac ([issue 1477](https://github.com/seedds/nxnative/issues/1477)). Before compute pipelines were in the offline archive, a true first start with the list prepared for 134.7 s on an M4 MacBook Air and then had no frame over 0.25 s to a save load. Open: the first start has not been measured with the archive's compute pipelines, nor on iOS, where this game has no bundled archive (no `pso_mtl4`) and the list is all a first start has; the render bases the `R` rows need are not in the offline archive. A longer list from hours of iPad play (PR 1488, 9,789 rows) prepared 16 s longer on that MacBook and avoided only 7 render builds that made no frame over 100 ms. The list's last 4,959 rows (3,193 shaders, 1,579 render and 187 compute pipelines) are the rows of an iPad Pro (M5)'s own lists that it lacked, and rows recorded on a Mac on first looks at places a new player reaches next: the forest bridge (Saria's farewell), Hyrule Field, the Lost Woods and Kokiri Forest. There, with only the iPad's lists, the Mac built 344 render pipelines in play in Hyrule Field and 362 on the bridge (frames up to 749 and 1,273 ms), against 0 and 5 with them (up to 236 and 385 ms, the load frame and the bridge's cutscene); on the iPad, where each is built at its first draw (0.1-0.9 s), the stutter at a first look went away except in the forest's heaviest area. Cost: the prepare step of a cold first start builds those pipelines too. - With a window, the first start on a new chain runs the pipeline prebuild first: 33,190 shader pairs, 691 s on this Mac. A run with no window skips it. - The opening movie runs at the movie's own 24 fps pace during the story section and cannot be skipped: about 8 minutes on a new file. - Resolved: at a 30 fps cap (`DM_FPS_TARGET=30`) the title and opening movies played at half speed: the player takes one picture per game frame, and the movies are 60 pictures a second (their times whole milliseconds, also through the opening's story section). The frame step now follows the movie's picture rate: [The frame step follows a movie's picture rate](../../../docs/reference/frame-rate.md#the-frame-step-follows-a-movies-picture-rate) ([PR #2622](https://github.com/seedds/nxnative/pull/2622)). On an M6 at the 30 cap: 10 s of movie in 20.0-24.2 s before, 10.0-10.4 s after, the step 60 while a movie plays and 30 again when its decoder is finalized. Open: where the frames cannot hold 60 (an M1 with the movie network on), the step falls to 30 for late frames and the movies still play slowly; only a change in the player itself (`advance`, `main+0x3b61ea0`, stepping pictures by the frame step) would cover that. ## Dynamic resolution - The controller (v131072; found by fingerprint, so the addresses are only for reading): the parameters are `game__gfx__DynamicResolutionParam` (`LoadThreshold`, `ReduceCounterMax`, `IncreaseCounterMax`, and a list of levels, each a `ResolutionReduceScale` and a `Priority`). Once a frame the update (`main+0x805aa0`) reads the frame's GPU time, subtracts what its cost model predicts at the current size and keeps the trend of that residual. The level function (`main+0x8065e0`) adds the residual to the model's cost at each level and returns the best level under the threshold: 0, the full 1536x864, when the current size fits. The update moves down after `ReduceCounterMax` frames that ask for it and back up after `IncreaseCounterMax`. Scripted `GPULoad` hints from event data (`main+0x31c3de0`) and `OneshotSetDynamicResolutionLoadThreshold` adjust it in cutscenes. - Resolved on the host: after a loading screen it sat at its floor, 683x384, for minutes while the frame held 60 fps with GPU time to spare. A stale frame-start timestamp and event waits that idled the GPU made its timer read more than the frame took: [Dynamic resolution at its floor while the GPU has time to spare](../../../docs/reference/frame-rate.md#dynamic-resolution-at-its-floor-while-the-gpu-has-time-to-spare). - Resolved in the game's model: with the host's timer right it still sat at 683x384 for good. The update takes the frame's load (about 0.2 ms a unit) less its small fixed-cost model, divides the rest by the square of the current scale, and the level function multiplies that back by each level's scale squared. The whole remaining cost is taken to grow with the pixel count, as on the Switch 2 its constants were tuned for. Here it hardly does: 683x384 cost 14.3 ms and 1536x864 14.7 ms on an M4 Pro (mesh, compute and the upscaler's fixed 2048x1152 dominate). A probe of the controller in play (levels 0..9 at 0.75..0.333 of 2048x1152, threshold 83, about 16.4 ms) read a load of 70-80 at 683x384, under the threshold. The extrapolation still put full size at 300-400, so it never stepped up. - Current: the launcher's **Dynamic resolution** (`DM_DYNAMIC_RESOLUTION`, off by default). Off holds full resolution. On climbs one level at a time on the load measured at each level, with no model of how the cost grows, within the game's own level list, threshold and counters: - down when the load has been over the threshold for 8 frames in a row, and that level is marked as not fitting; - up when the load fits and the level above was never measured, fitted when last measured, or was measured more than about 10 s ago. Nothing is decided until a level has held 8 frames, since the load is read a few frames late. The 8 frames in a row keep a single slow frame, or the first frames at a new size, from rejecting a level that fits. An earlier version stepped down on 2 frames over and stopped at level 2 after one burst to 90 at level 1. A version that stepped up only when the load, scaled by the next level's pixel ratio, stayed under the threshold sat at the floor too: 70 × 1.3 is over 83. Result (M4 Pro, 2026-11-09, `kokiri-still`, the full library on chain a02f1e0dfdde): from the floor to full size, then mostly at 1536x864. The load there is about 83, the game's own threshold, so a sustained frame over moves it to level 1 (0.70) for about 10 s. 59.9 fps, all 1,800 frames held at one vsync, frame p99 16.89 ms, GPU p50 14.5 ms. Loads by level: 72 at the floor, 75 at level 6, 80 at level 2, 82 at level 1, 83 at full size. - Open: in heavier scenes it goes down to the floor for nothing. In the player's session (M4 Pro, 2026-11-09, Metal HUD) it held full size at loads of 80-84 until a scene took it to 94-102. It then stepped down to 683x384, where the load was still 83-86, about the same as at full size. It stayed near the floor at about 80 until a lighter scene (55 at the floor) let it climb again. The frame rate held about 60 throughout. The cost that decides the load here does not follow the resolution. The fix is to step down only to a level measured cheaper than the current one and otherwise climb back. Until then off, full size, is the better default on this Mac. Slower Macs, where resolution may matter, are not measured. It is a stub of the frame step rule in [`patches.py`](patches.py) that replaces the level function. It is placed by that function's fingerprint and the update's (it reads the update's fields `+0x8` and `+0x174`). The rewriter declares the `DYNAMIC_RESOLUTION` capability when it places the stub. - Where the full-resolution GPU frame goes (M4 Pro, Metal System Trace, 2026-11-08): vertex/mesh 4.9, fragment 5.2, compute 4.3 ms per 1/60 s, and they do not overlap (their union is 14.4 ms, their sum 14.4). The heaviest passes: the main mesh pass (task `a5288d0a6c4971fd`, mesh `3439aaa071d868ae`, fragment `a1a56cf6d62ae798`), two foliage passes (task `47f9b38490dcf9a2`), and the upscaler's compute (`fa3033d05c0a41d6`, 177 HMMA calls, already on `simdgroup` matrices). Ruled out: the stage serialization is mostly the game's own barriers (about 70 a frame, 39 of them `ORDER_PRIMITIVES | INVALIDATE_TEXTURE`). The relaxed draw-pass barriers of `draw_barriers game` were then off whenever an image family heap existed (this game's dynamic-resolution depth buffer makes one). Keeping them on with the alias flush moved to a start barrier skipped 22% of the full barriers, but the real GPU span on this M4 Pro did not change (14.7 ms p50 either way, two runs each). That change is on main now, with fragment-only barriers no longer forcing the full one, for its gain on the M1 Max ([Draw passes that wait for each other again while images share memory](../../../docs/reference/frame-rate.md#draw-passes-that-wait-for-each-other-again-while-images-share-memory)). Also ruled out: mesh shaders writing only the vertices their primitives use (the highest index + 1, instead of all `max_v`), as an overlay of the 31 play mesh shaders against the same shaders recompiled: span 14.7 / 15.5 ms against 16.0 / 15.6, so the game's meshlets are full. A shader timeline needs the trace attached before the pipelines are made, and a recording from 3 s to the play window (9 GB, or 45 s with the shader profiler) never finished saving. - Measured on the M4 Pro (20-core GPU, 2026-11-08, `kokiri-deck`, one run each): at full resolution (the main passes 1536x864 in pass dumps at 75, 100 and 125 s) 48.1 fps, frame p50 16.74 / p95 33.41 ms, GPU p50 15.0 / p95 16.9 ms: the GPU needs about one vsync a frame, so the frame step holds 2 vsyncs much of the time. With the game's own dynamic resolution and the host fixes above: 60 fps, GPU p50 12.6 ms, at a lower resolution. A second dynamic-resolution run wandered off the route into Saria's dialogue and is not counted. ## Memory - Current: on the walk, run from an external drive with a 3.5 GB pipeline archive: resident 10.9-11.4 GB, wired about 9.4 GB, on a 16 GB Mac. `vmmap` and `footprint`: 6.4 GB of guest memory and 3.8 GB of Metal textures and targets, 1 GB of IOAccelerator memory. [Reading the archive into memory](../../../docs/reference/memory.md#a-pipeline-archive-on-an-external-drive-is-read-into-memory) used to add 3.5 GB (footprint 15.0, peak 16.7). `mem_map_limit on`: a map past the application pool fails as on Horizon, and the SDK's allocator then gives back the pages it keeps. Other apps' memory adds to the swap: other games running beside it can push the Mac over. - This game took the Mac down before the fixes (kernel panic in `IOGPUGroupMemory` at 14.7 GB wired): [raw pool table](../../../docs/reference/memory.md#memory-pools-nothing-gave-an-address-to-stay-out-of-the-raw-pool-table), [textures made only when read](../../../docs/reference/memory.md#shaders-report-which-textures-they-read-through-a-pool), [out-of-memory exit](../../../docs/reference/memory.md#repeated-gpu-out-of-memory-failures-end-the-game). - Why so much: the game's own memory is about the console's 9 GB application pool, and on the Mac its textures also exist as Metal textures, as Metal cannot sample the guest's block-linear storage. - Resolved in part ([#1831](https://github.com/seedds/nxnative/issues/1831)): render targets that move in the game's 305 MB pool and change size with its dynamic resolution still get a new Metal texture each time. Their memory is now recycled ([Render targets that move with the dynamic resolution](../../../docs/reference/rendering.md#render-targets-that-move-with-the-dynamic-resolution-get-new-metal-memory-each-time)). Since 04419bc2 the resolution moves, and on a 16 GB MacBook Air M4 the game stuttered (p95 99 ms) with GPU allocation near 15 GB. On an M6, while the resolution moved in the village, the target site added 455-521 MB to the residency set per 300 frames without recycling and 27-37 MB with it once heaps were waiting, and frame p90 went from 33-50 ms to 17-18 ms. The live targets' steady total, about 2.1 GB in the village, cannot share the pool's memory as on the console: the game keeps live targets in the part of the full-size scene target its resolution leaves unused, which only NVN's layout allows (PR #1453). [A report of a GPU driver panic](https://github.com/seedds/nxnative/pull/1299) lines up with this churn. The reporter's Mac has not been tested. - Current: the iPhone's first run (12 GB iPhone, guest files on, the phone's IPA at 34ada028) was ended for memory about 9.5 minutes in, at about 28 fps, at an area load: the log stops mid-frame with 5.96 GB of room reported at start, the jetsam limit's figure. On the Mac the same commit reached 6.36 GB of footprint stepping into the village, 4.4 GB of it Metal memory, 2.9 GB of that the 304 MB render-target pool's targets, nearly all of them unused for 20 s or more and drawn over by newer targets while their texture objects lived. They are now released ([Render targets drawn over stayed while their texture objects lived](../../../docs/reference/rendering.md#render-targets-drawn-over-stayed-while-their-texture-objects-lived)): on an M6, the village save and 2.5 minutes around Link's house, footprint 5.35–5.47 GB before, 3.70–3.80 GB after (one run each), 60 fps both, screenshots alike. Open: the iPhone run with it. ## Saves The game autosaves into `slot_00` and `slot_01`, with `option.sav` and `account.sav`. The Load screen lists them and reloading works. A save's thumbnail is a 543x306 JPEG in `caption.sav`, made from a copy of a texture to a buffer. A manual save (Menu, System, Save) on the current build has the picture (slot 02, mean colour 138 / 123 / 86). The autosaves in the work folder's saves, made on older chains, hold a black one. Open: an autosave made on the current build has not been looked at. ## Driving it on the Mac Run headless, `DM_CTL` at a control file. The work folder has `boot.sh ` (with a guard that kills the host past 12.5 GB wired or 16 GB of swap; do not run without it), `ctl.sh`, `seq.sh`, `snap.sh`, `keys.sh ` (adds and builds the keys the logs ask for), `white.py` (share of near-white pixels in a shot) and `run/save_ref` (autosaves in Kokiri Forest). From an empty save: at about 45 s `A` (title), `A` (New Game), then the movie. From `save_ref`: at about 58 s `A~0.2`, 5 s later `A~0.2` (Load), 30 s later three `A~0.1` 3 s apart, then 12 s to the bridge outside Link's house. There `LUp~3.5`, `A~0.2` (Climb Down) and `LDown~4` three or four times reach the village floor. ## Open - Root cause confirmed on M4 Pro (2026-11-08): [ignored event waits](../../../docs/reference/nvn-objects.md#gpu-event-waits-order-work-across-queues) let lighting consumers run before their producers. The game issues about 25 waits and 19 signals per frame. All 297 complete traced frames submit queue 5's ambient-cache consumer `9e6b00b050e30801` before queue 1's SH producer `21d9f83e6027c565`, despite the explicit dependency. Queue 1 also waits for the CPU frame-ready event after the CPU clears the lighting buffer. At frame 3872 the consumer ran at 0.686 ms, the clear at 0.966–0.968 ms, and the producer at 1.282 ms; its release followed at 4.467 ms. Exposure writer `1bba09ec4bc8ff67` has another event dependency. The native SDK's flush/invalidate functions are no-ops for these coherent pools. - The pale outline around Link is produced before fog, bloom and upscaling, by task `efaa4fa6a80ba719`, mesh `066f5daf12e43c71`, fragment `5ad65c0eefd09c7f`. An offline replay matches both 768x432 RGBA16F targets byte for byte. Removing shared-buffer bytes 48–111 makes direct SH lighting agree with the cached ambient lookup (affected-pixel mean RGB error 0.100/0.136/0.218 falls to 0.0034/0.0020/0.0008): the cache lacked the SH contribution available to the later direct draw. Honoring the event dependency removes the outline, confirmed by the player and later captures. - A repeating patchy pale layer remained after event ordering was fixed, including with optional attachment discard disabled. [Conditional rendering state lost across queue continuations](../../../docs/reference/rendering.md#conditional-rendering-survives-queue-continuations) caused repeated clears: disabled draws and pending timestamp counter values must survive physical Metal submission boundaries. Fixing both removes the layer in 2,000 asynchronous frames covering a stationary view, loading, camera rotation and movement to the village floor. The loading peak is the normal black-to-scene fade; the camera peak is a foreground branch crossing the view. These bounded samples do not establish every later area is unaffected. Synchronous screenshots wait for the GPU and can hide the original race, so validation uses asynchronous present copies. The rebased delivery subset passes the focused queue, descriptor, continuation, completion, residency and mesh-first-use checks. The current-main shader rebuild and five-game regression are still running at the visual-fix push. - Diagnostic caveat: earlier shader probes used fragment buffer 30, which conflicts with alpha-test state, then buffer 28 with added atomics that can move depth testing late. Visual conclusions from those instrumented runs are inconclusive. The event trace, offline producer comparison and combined validation above use the original uninstrumented fragment libraries. The scene flash predates those probes. - Regression reach is uncertain because every NVN submission now passes through logical queue scheduling. The five selected checks are Ocarina (the original dependency), Three Houses (multiple queues), Octopath Traveler 0 (command-buffer fences and Unreal RHI), Octopath Traveler II (an available boot route), and Okami HD (an older NVN path). The run is in progress; its unfinished rebuild is not a passing result. The focused tests pass with Metal validation: event/receipt 80, descriptor lifetime 38, continuation 38, native mesh first use 42 and storage-writer state 34 checks per encoding mode; capture 91, suspend 22, NVN 245, alias residency 28, completion ordering 36 in each of three configurations, and counter-overlap 42 per mode. The older CPU-post report/GPU-byte ordering limitation remains open as described in the rendering reference. - Current on M4 Pro (20-core GPU, 2026-11-08, the `kokiri-deck` route in [`bench.tsv`](bench.tsv), the partial shader set): GPU p50 12.6 / p95 13.6 ms, 51.8 fps, frame p50 16.69 ms (60 fps most of the time; two runs). Before, the main pass sat at the game's floor, 683x384, and the frame step held 30 fps at 16.3 / 18.6 ms (a hold of one vsync needs a cost under 1.1 vsyncs, a probe back to it 0.85). The time was geometry: per frame about 9 ms in the vertex and mesh stages, 5 ms compute, 4 ms fragment (Metal System Trace); the foliage task/mesh pass 4.6 ms of vertex-stage time against 0.2 ms of fragment. Two causes, both general: the mesh shaders read their 128-bit raw loads a word at a time ([128-bit raw global loads read one word at a time](../../../docs/reference/shaders.md#128-bit-raw-global-loads-read-one-word-at-a-time), about 3.1 ms), and occlusion conditions were never read ([Occlusion conditions read once landed](../../../docs/reference/rendering.md#occlusion-conditions-read-once-landed), 0.6 ms). Ruled out by A/B: the mesh shaders' threadgroup memory (folding it 50-77% smaller did not help), its zero fill, the pool table reads, their primitive output (0.3 ms) and missing `c1_full.tsv` / `cb_extent.tsv`. Open: compute is still about 5 ms of the frame (the NDSR upscaler `d99fbeeb53d38847` writing 2048x1152, a fixed cost at any internal resolution, and a culling group), not looked into. - Resolved: the build left 9 shaders out. Six foliage compute shaders used `ATOMG.CAS` ([Global memory: descriptors, ATOMG.CAS, INC / DEC](../../../docs/reference/shaders.md#global-memory-descriptors-atomgcas-inc--dec)); `1f78730053feb036` and `e172bd7ec218ab8e` nest 259 brackets ([Bracket nesting past the front end's 256](../../../docs/reference/shaders.md#bracket-nesting-past-the-front-ends-256)); `68bbe98b3bfd9876` (`Ampere instruction 481 at 0x0`) and five more map rows were no shaders at all ([A control section's words read as a BAHS entry](../../../docs/reference/shaders.md#a-control-sections-words-read-as-a-bahs-entry)), now gone from the map. None of the eight real ones was used on the walk to Kokiri Forest. - Open: [the movie network](https://github.com/seedds/nxnative/issues/1375) took 35 ms a frame on an M1 Max, so movies and the title ran at about 28 fps there; on this M6 it takes 11.4 ms since [HMMA chains keep their matrices in the SIMD-group](../../../docs/reference/shaders.md#hmma-chains-keep-their-matrices-in-the-simd-group) (from 14.8), not measured on an M1 Max since. - Current: against a frame of console video of Navi's close-up the picture matches: hair and sign within 1–2 % in red and green. The causes, each general: [float atomics](../../../docs/reference/shaders.md#float-atomics-in-storage-memory) (too bright, yellow skin, a pale veil: the exposure read back from buffers that stayed 0), the terrain's CPU-written maps (holes, wrong materials; the profile keys above), [the gather offsets](../../../docs/reference/shaders.md#texturegatheroffsets-and-a-loop-over-its-offsets) (the dotted rows in blurred areas, born in the NDSR upscaler, three pictures from one input), and [image atomics on RGBA8](../../../docs/reference/shaders.md#image-atomics-on-a-32-bit-colour-texture) (the anti-aliasing's blend weights). - Current: the grass no longer flickers. Its blades shifted at 10 Hz because the wind's pressure solve read one neighbour at an undefined row ([dead-code removal](../../../docs/reference/shaders.md#dead-code-removal-dropped-a-coordinate-of-a-multi-output-instruction)), so the velocity field (512x512 RGBA16F, a two-buffer ring) held strong stripes up to ±11 with a 6-frame cycle. In the moss on the bridge, the mean difference between adjacent frames fell from 5.97 to 0.70 of 255, and it now grows smoothly with the lag. - Open: [PR #2226](https://github.com/seedds/nxnative/pull/2226) reports intermittent holes in Hyrule Castle's courtyard bushes on an M1 Max. On `5a4e23a2`, chain `d254f45e8002`, M6 / macOS 27.2 (2026-11-14), a fixed Kokiri Forest view exercised the same five foliage cull kernels: in 33,841 nonzero indirect dispatches, with and without the proposed encoder boundary, the sampled native grid and, where read, the shader's driver counts matched the GPU's argument record. All five received an intra-encoder barrier without the split. This checks count reads, not every foliage-list entry; the probes change GPU work and cannot rule out a timing-sensitive race. With probes disabled, the medians across the alternating 300-frame phases were 13.14 ms GPU span without the boundary and 13.32 ms with it (one session). No courtyard save, courtyard capture or M1 Max run was available. Host ordering is unchanged; the failing read remains unidentified. - Resolved in the log, to be eye-checked at the spots it showed: flat blue holes in the ground with grass and rocks floating over them (Kokiri Forest, the village floor, the Deku Tree) after an hour of play: [textures made for GPU use kept from their CPU rewrites](../../../docs/reference/nvn-objects.md#textures-made-for-gpu-use-kept-from-their-cpu-rewrites). The terrain tiles' height, blend and hole maps are rewritten from the CPU as tile slots are reused, and the dirty-page poll dropped every uncompressed one; with the fix, about 250 of them upload in the first 160 s of the Kokiri Forest autosave and a walk onto the village floor (M4 Pro), and the normal tiles the GPU draws stay, or become again, the GPU's. - Open, small differences from that same console frame: Navi's core has a harder edge and a narrower glow here (the bloom texture peaks at 0.44; the ball's HDR is about 0.9 with a tiny spot at 45); shadows are less blue, and the far grass about 1.4 times brighter. One frame of compressed video is weak evidence for either. The falling leaves look dark near the camera, not compared. - A red `texture type mismatch` on `9b310b3349825e9a` (a UI merge shader): a 3D texture left bound in a slot its active branch does not sample. Harmless so far. - `nn::codec AV1: busy (result 3603), a packet refused with 0 pictures waiting for a released buffer and 12 packets to decode` (4 lines a run, at the title movie's start, where the reader sends ahead of the decoder): the decoder's queue is full and the movie library retries, as it is written to ([a refusal is no fault](../../../docs/reference/movies.md#the-submission-ready-event-counted-as-packets)); movies play. It was a red line until then. - The `pipeline archive` red line is [issue 342](https://github.com/seedds/nxnative/issues/342), a limit of this system build. `c1_full.tsv is stale` holds on partial libraries, which no full build has stamped. - The "Navi" name label in the Great Deku Tree scene was missing on an earlier chain and has not been checked since. Not played yet: the Deku Tree dungeon, combat, later areas. Sound opens with no red line; movies and speech were not listened to. Physics issues reported in PR 1299 were not seen on this route (walking, the ladder, the camera). - Resolved: dotted horizontal rows across the picture in play, in fog and light (GitHub issue 1537): the agl_ndsr_vr networks' HMMA calls outside a chain overwrote an accumulator a chain held ([An HMMA outside a chain overwrote the chain's accumulator](../../../docs/reference/shaders.md#an-hmma-outside-a-chain-overwrote-the-chains-accumulator)), since chain 23a1ea1db092; found by bisecting chains on the Kokiri Forest autosave (0f510fd5's chain clean, bd9f422d's not). Under the game's upscaler they had also grown into white blotches: [LDSM reads rows other lanes stored](../../../docs/reference/shaders.md#ldsm-reads-rows-other-lanes-stored). - Open: GPU hangs in play ([#1326](https://github.com/seedds/nxnative/issues/1326), [#1605](https://github.com/seedds/nxnative/issues/1605)), repeatable in a Kokiri Forest conversation and the Impa escort on an M2 Pro and gone with the pipelines of task `a5288d0a6c4971fd` dropped ([#2011](https://github.com/seedds/nxnative/issues/2011)). They are not [threads returning before a barrier](../../../docs/reference/shaders.md#compute-threads-that-return-before-a-barrier): that task, the culling tasks `47f9b38490dcf9a2` and `99280b7b8aae78df` and mesh `3439aaa071d868ae` have no barrier in their bodies (none of the game's 520 task and mesh shaders has one). The lead in #2011 is pixel shader `bd09ab5fc4fd7680`'s light loop, which runs `min(count, 1000) + 1` times per pixel with per-tile counts the GPU reads. - No iOS shader build or iPhone run has been done. Its memory use makes an iPhone unlikely as it stands. ## Unused border corrections Specialize unused sampler border corrections away ([mechanism](../../../docs/reference/rendering.md#unused-border-corrections-retain-sample-registers)). No sampler of this game needs a twin (no "border colour … Metal lacks" line, the mask 0 in every draw of a profiled frame), so every draw takes the specialization and the picture does not change. Measured on a MacBook Air M4, docked at render scale 0.75 (1920×1080), whole GPU, a window, in the fixed view of Hyrule Field with the most grass (`still.sh`), with the 264 render-stage shaders of that scene that sample through the corrections rebuilt in the new form beside the chain `971e0669b443` image and a host of that chain: - In-process A/B (`DM_AB DM_NVN_BORDER_KEEP=ab`, the same process, scene and temperature): GPU per frame 25.20 ms specialized against 26.69 and 26.79 ms with the corrections, about 1.5 ms, in the render stages; with the kernels too, a mean of 24.73 ms over five phases against 26.54 ms over four, 1.8 ms. - Separate runs, interleaved: 26.48 and 27.10 ms with the corrections, 26.00 and 25.46 ms specialized (thermal drift between runs is about 0.6 ms). - The earlier test-only experiment, every twin sample removed from all 301 shaders including 37 kernels, gave 24.97 ms against a four-run mean of 26.36 ms. The prewarm built the 551 specialized bases of that scene in 11 s at the start, on background threads, before the measured frames. The offline archive of a full conversion holds them (`pso_offline.py`): a test archive of 24 scene pairs, 20 of them with a specialized pixel function, served all 24 to the host's descriptors. Its 37 kernels that correct samples, NDSR's network among them, gave about 0.25 ms more (GPU per frame 25.09 and 25.24 ms with their corrections, 24.97 and 24.86 ms without, interleaved runs; their compute encoders 0.3 ms faster in the profiled frames). Chain `da29694f3f98` contains the change for every stage; a full shader conversion with it is pending, and so is the target of at least 55 fps at these settings. ## Found on this game - [Metal's build folders left by pipeline archive flushes](../../../docs/reference/shaders.md#metals-build-folders-left-by-pipeline-archive-flushes): fixed, the built Mac app's `org.nxnative.0400486024dec000/com.apple.gpuarchiver` held 4,016 folders, about 285 GB, 6 more (560 MB) each start of the Castle Town route ([issue #2680](https://github.com/seedds/nxnative/issues/2680)); an app now removes its earlier runs' folders at start. - [A run that ends in its prewarm loses what it compiled](../../../docs/reference/rendering.md#a-run-that-ends-in-its-prewarm-loses-what-it-compiled): fixed for runs without a window ([PR #2625](https://github.com/seedds/nxnative/pull/2625)): each warm-up run ended with 400–500 bases not written, and the next one compiled 57–167 again at area loads; not run in game with this host. - [The frame step follows a movie's picture rate](../../../docs/reference/frame-rate.md#the-frame-step-follows-a-movies-picture-rate): fixed, its 60-picture `nn::codec` movies at half speed at a 30 fps cap. - [Render targets drawn over stayed while their texture objects lived](../../../docs/reference/rendering.md#render-targets-drawn-over-stayed-while-their-texture-objects-lived): fixed on the Mac (footprint 5.4 → 3.7 GB on the village route); the iPhone retest is open. - [A long render pass split while the GPU has nothing](../../../docs/reference/frame-rate.md#a-long-render-pass-split-while-the-gpu-has-nothing): the GPU idled about 0.3 ms a frame inside the scene passes (500–800 draws) on an M1 ([PR #2560](https://github.com/seedds/nxnative/pull/2560), measured in another tree); not measured with this host. - [A bundled pipeline archive read a page at a time when it opens](../../../docs/reference/shaders.md#a-bundled-pipeline-archive-read-a-page-at-a-time-when-it-opens): resolved. On an M4 Max the app's `applegpu_g16s` archive (4.4 GB) opened in 8 s, a gray screen before the pipeline progress screen, while an M1 Pro, which has no bundled file for its GPU, started fast. Now it opens in about 1.6 s with the file not in memory. - [A frame standing in the present queue after a late frame](../../../docs/reference/frame-rate.md#a-frame-standing-in-the-present-queue-after-a-late-frame): fixed. With the display link on an M2 Pro's ProMotion screen, the title screen at the 30 fps cap in a window showed frames 115.4 ms after the game queued them, 65.4 ms with the drain and the 60 Hz link; in game at the 60 setting, 82-102 ms became 65-70 ms. - [Frames pile up for the screen at a cap the game holds, without the display link](../../../docs/reference/frame-rate.md#frames-pile-up-for-the-screen-at-a-cap-the-game-holds-without-the-display-link): fixed. With `DM_PRESENT_LINK=0` on the M2 Air, fullscreen, at the 30 fps cap frames reached the screen 109-155 ms after the game queued them; with the screen wait, 57-65 ms, with frame times unchanged (33.3 ms). - [A game's own upscaler drawn by MetalFX](../../../docs/reference/rendering.md#a-games-own-upscaler-drawn-by-metalfx): resolved. `DM_UPSCALER=metalfx` did nothing here, because its networks are in `agl_ndsr_vr.pck` and `agl_ndsr_vr_light.pck`, which the profile's upscaler keys left out ([issue #1911](https://github.com/seedds/nxnative/issues/1911)). They are named now, as every NDSR pack's compute shaders are: 61 keys, 22 of them `agl_ndsr.pck`'s, which the game ships and does not run, and 2 of the pack inside the movie file, which their code keeps. The network the game runs reads square roots in RGB9E5 and writes R11G11B10F (1440x810 → 1920x1080 handheld), so both copies around MetalFX stay. On an M2 Air the upscale took 1.31-1.38 ms instead of 2.50, and the frame p50 25.5-26.7 ms instead of 27.5-28.2 ([PR #1459](https://github.com/seedds/nxnative/pull/1459), a host before PR #2559). On the M4 Pro (`kokiri-deck`, one run a side, a host on chain `0c434f1b68df` with this change, 2026-11-14) the GPU p50 went from 17.15 to 15.79 ms (mean 16.47 to 15.43), both held at 30 fps by the frame step; the picture's means at the same spot, on frames a little apart, were 38.5 / 33.8 / 16.8 of 255 with the network and 41.1 / 37.4 / 19.4 with MetalFX (darkest 5 %: 4.3 against 7.0), not washed out. The edges are softer than the network's, as the spatial scaler draws them; the game's network stays the default. - [An upscaler's colour read from its code](../../../docs/reference/rendering.md#an-upscalers-colour-read-from-its-code): resolved, the square-rooted shape. Of the 37 compute shaders of the `_vr` packs, 18 networks (`fa3033d05c0a41d6`, `d99fbeeb53d38847`, `04135bb812a0d66e`, `aad7ea7e5c02f41d`, `b106afba1a18faa9` among them) are drawn with their input squared; 12 networks that encode after the square and 7 pre-passes keep their shader. Only the `_vr_light` networks have been seen in play; the colour of the 10 in `_vr` is read from their code only. - [Pipeline archive files left behind by a major system update](../../../docs/reference/shaders.md#pipeline-archive-files-left-behind-by-a-major-system-update): resolved ([pull request #2539](https://github.com/seedds/nxnative/pull/2539)). After macOS 26.6.2 to 27.2.1 on an M1 Pro, 57 archive files (1.68 GB) were refused at every start and the prebuild was taken as done, so its bases were built at the draws (53 stalls, 39 s, in 24 minutes); the refused files and the stamp now go, and the next start runs the prebuild again (13.4 minutes for the 34,649 pairs on an empty archive, once). - [A submission's commit read the whole storage-write page set](../../../docs/reference/frame-rate.md#a-submissions-commit-read-the-whole-storage-write-page-set): resolved. About 30 commits a frame each read the 16,384-slot set of pages its storage buffers wrote (256 KB); standing on the Kokiri Forest deck the submit thread went from 42.8–43.9k to 38.7–39.3k instructions per draw (M4 Pro, 640 draws a frame), with no change in frame time, which waits for the GPU there. - [Sparse mappings under cached texture bindings](../../../docs/reference/nvn-objects.md#sparse-mappings-under-cached-texture-bindings): found in an audit of the host's caches (PR #1721); a mapping under a texture with no serial change after it left the texture's old pages in its cached bindings. - [Captures still queued on the harvest queue left out of a waiting flush](../../../docs/reference/rendering.md#captures-still-queued-on-the-harvest-queue-left-out-of-a-waiting-flush): resolved, the prebuild tool trapped at exit in one Mac app build of two and the app was packed without its prewarm files ([GitHub issue #2498](https://github.com/seedds/nxnative/issues/2498)). - [A fence after every shared memory store crashes Apple's compiler](../../../docs/reference/shaders.md#a-fence-after-every-shared-memory-store-crashes-apples-compiler): fixed on the Mac side, retest on an M2 open ([GitHub issue #2481](https://github.com/seedds/nxnative/issues/2481)). The foliage pipeline (task `a7f22b4a795ff4d1`, mesh `f85bb2e8d78b864c`) crashed the M2's compiler in 39 of 39 runs with the fence after every store. The translator now fences only loads that follow a store with no barrier between: the fenced shaders went from 25,108 fences to 1,566, and the mesh shaders have none. Whether the GPU hangs seen with the old fences (7 of 9 long runs) are gone is also open. - [Compute dispatches past the pipeline's thread limit](../../../docs/reference/shaders.md#compute-dispatches-past-the-pipelines-thread-limit): resolved, the haze, mist and light shafts of Kokiri Forest, the Lost Woods and Hyrule Field were gone on M1 and M2 Macs since 918e9f34 (fine on the M3 and M4): eight 8x8x8 froxel kernels of `AglShader.sharcb` (`9ed48d1011f1b48b` and seven more) took 124 to 128 registers there, which allows 384 threads a threadgroup, and their 512-thread dispatches ran nothing ([GitHub issue #2425](https://github.com/seedds/nxnative/issues/2425), [PR #2487](https://github.com/seedds/nxnative/pull/2487)). Confirmed fixed on an M2 Pro (full rebuild) and an M1 Pro (the eight kernels overlaid); with the effects drawing, footprint in Kokiri Forest was about 2 GB higher on the M1 Pro (11 to 13 GB, handheld). - [Frame threads with no deadline of their own run on efficiency cores at 30 fps](../../../docs/reference/frame-rate.md#frame-threads-with-no-deadline-of-their-own-run-on-efficiency-cores-at-30-fps): resolved for the normal GPU state, Kakariko on an iPhone 17 at the 30 fps cap 26.4 → 29.1 fps with the replay and encode threads declaring the frame's deadline; open, a state of the phone where the GPU takes 2-3 times as long (9-13 fps with or without the change). - [A time-constraint computation sampled per acquire or across acquire threads](../../../docs/reference/frame-rate.md#a-time-constraint-computation-sampled-per-acquire-or-across-acquire-threads): resolved in the host, found by synthetic tests; not shown on this game ([GitHub issue #1985](https://github.com/seedds/nxnative/issues/1985) open). - [Texture views made again for render targets initialized again every frame](../../../docs/reference/frame-rate.md#texture-views-made-again-for-render-targets-initialized-again-every-frame): resolved. The game initializes about 40 render targets again every frame over the same storage, and their views (depth read as (R, 0, 0, 1), RGBX with alpha 1, sRGB and image views) were made again each frame on the submit thread: about 40 views a frame on the Kokiri Forest deck with the finalized textures' sets kept, about 1.5 once views are kept by the Metal texture they view (M4 Pro, `kokiri-deck`, frame time unchanged). - [A commit switched and back, or another worktree, rebuilds every shader](../../../docs/reference/packing.md#a-commit-switched-and-back-or-another-worktree-rebuilds-every-shader): resolved, its Mac shaders were translated again (46 minutes, all from the compile cache) after the checkout went to a newer `main` and back with the same translator bytes ([PR #2496](https://github.com/seedds/nxnative/pull/2496)). - [A clear pass on a large depth texture fails with `Insufficient Memory`](../../../docs/reference/memory.md#a-clear-pass-on-a-large-depth-texture-fails-with-insufficient-memory): resolved, its save load makes a 12288x12288 Depth16 (296 MB) and a 4096x4096 Depth32F, and on an M3 Pro the clear pass that zeroed them failed with `kIOGPUCommandBufferCallbackErrorOutOfMemory` and took the queue with it: about half the loads ended at the 32-failures stop (PR #2306). Depth textures are now zeroed by copies; the author's 203 runs on its build with copies (and a retry throttle not landed here) had no failure. Not seen on an M4 Pro. - [More finalized textures in use than their table held](../../../docs/reference/nvn-objects.md#more-finalized-textures-in-use-than-their-table-held): resolved; the reporters' long-session retest is open ([GitHub issue #2213](https://github.com/seedds/nxnative/issues/2213), [PR #2389](https://github.com/seedds/nxnative/pull/2389)). Frames of 100 to 490 ms for about ten seconds at a streamed area transition, 50 to 55 fps for five seconds after the pause menu, and in long sessions slow motion for a minute or two (10 to 25 fps, riding into Hyrule Field, Kakariko): the table of finalized textures thrashed at 512 entries on the submit thread, with the GPU at its usual time. - [Regression shader compilations grow the normal build cache](../../../docs/reference/packing.md#regression-shader-compilations-grow-the-normal-build-cache): resolved in cache and harness tests. Its normal build's `cas.db` held 40.8 GB; future regression writes use a separate bounded cache, while the existing mixed cache is preserved. - [A reuse build spends a minute and a half on the c1 and uniform extent tables](../../../docs/reference/packing.md#a-reuse-build-spends-a-minute-and-a-half-on-the-c1-and-uniform-extent-tables): resolved, 95 s of every build that restored its 42,848 shaders from the compile cache, 6.5 s from the memo. - [Draw passes that wait for each other again while images share memory](../../../docs/reference/frame-rate.md#draw-passes-that-wait-for-each-other-again-while-images-share-memory): resolved, `draw_barriers game` was off whenever family heaps existed, and fragment-only barriers forced full ones; M1 Max gameplay frame 23.4 → 21.7 ms. - [Single frames over the fit keep a longer hold](../../../docs/reference/frame-rate.md#single-frames-over-the-fit-keep-a-longer-hold): 20 fps for 24.5 s after an area load in windowed play, then 60 again; fixed in the frame step. Open: the castle town market, at the edge of the fit, can still stay at 30 fps after its load. - [A new compile cache on an exFAT drive is "a readonly database"](../../../docs/reference/packing.md#a-new-compile-cache-on-an-exfat-drive-is-a-readonly-database): resolved, its first Mac app build from the NUP onto an external exFAT drive (macOS 26.6.2) stopped at the shader step with `attempt to write a readonly database` ([GitHub issue #2434](https://github.com/seedds/nxnative/issues/2434)); open, why SQLite's own guard for such drives missed there. - [A shader build's Python finishes one shader at a time](../../../docs/reference/shaders.md#a-shader-builds-python-finishes-one-shader-at-a-time): resolved, the whole Mac map's cold build took 58 minutes on an M3 Max with 4 to 5 of its 16 cores busy; 30 minutes with worker processes. - [Reads past a uniform binding's size](../../../docs/reference/shaders.md#reads-past-a-uniform-bindings-size): open ([GitHub issue #2218](https://github.com/seedds/nxnative/issues/2218)), long white streaks from the horses at Lon Lon Ranch and from Link's slingshot, and flickering parts of the slingshot and of the ReDeads' faces, reported with the uniform read bound on. The bound now takes the size the driver gives the GPU, rounded up to 256 bytes; the game binds sizes that are not multiples of 256. Not reproduced here before or after (the slingshot at Link's house, the ReDeads of adult Castle Town), so the reporters' retest decides. - [The blend helper's output reference on iOS](../../../docs/reference/shaders.md#the-blend-helpers-output-reference-on-ios): resolved, 22,296 of 42,848 Mac shaders failed to compile on macOS 26 ([GitHub issue #2272](https://github.com/seedds/nxnative/issues/2272)); the Mac compile now names the host's minimum macOS. - [A NaN colour under a zero blend weight](../../../docs/reference/shaders.md#a-nan-colour-under-a-zero-blend-weight): resolved, white clouds over the ground mist between Kokiri Forest and Hyrule Field and over the ferns at the camera in Kokiri Forest ([issue #1839](https://github.com/seedds/nxnative/issues/1839)). The mist shader `5ad65c0eefd09c7f` writes NaN at alpha 0 where its layer has density 0; NVIDIA's blender, with the option NVN's driver sets, adds nothing there, Apple's stored the NaN. - [A first start builds every base of the prewarm list before the first frame](../../../docs/reference/rendering.md#a-first-start-builds-every-base-of-the-prewarm-list-before-the-first-frame): the 450-475 s first-start prepare on an M5 Max; the Mac app now bundles the list's pipelines (`--prebake`, on by default) and it took 4.3 s. - [The 60 Hz link at 40 fps in fullscreen on a ProMotion screen](../../../docs/reference/frame-rate.md#the-60-hz-link-at-40-fps-in-fullscreen-on-a-promotion-screen): 40 fps in fullscreen on an M5 Pro MacBook Pro with the GPU at 10 ms of the 16.7, every present one 120 Hz refresh late and every other display-link tick skipped; the screen's fixed 60 Hz mode held 60.0 fps there and fullscreen now switches to it, with the 120 Hz link (59 fps) for screens with no such mode. - [The fallback pacer slept a lead the display link built up](../../../docs/reference/frame-rate.md#the-fallback-pacer-slept-a-lead-the-display-link-built-up): resolved in the host's replay, not rerun on the TV: 29 freezes of 0.5 to 27.6 s in 55 minutes in fullscreen on an LG TV's 120 Hz variable-refresh link ([PR #2590](https://github.com/seedds/nxnative/pull/2590)). - [The shader package and the shader map come from different splits](../../../docs/reference/packing.md#the-shader-package-and-the-shader-map-come-from-different-splits): resolved, a black screen with sound in a build through NX2Apple ([issue #1841](https://github.com/seedds/nxnative/issues/1841)); the present pass was translated from a terrain shader's record. Records now carry their keys, and the shader build finds them by key. - [Native mesh pipelines compiled at their first draw](../../../docs/reference/shaders.md#native-mesh-pipelines-compiled-at-their-first-draw): resolved. The foliage, shadows and enemies drawn with task and mesh shaders stalled at their first draw on a cold Metal cache (59.5 s of builds at the Kokiri Forest entry on an M5 Max, one frame of 42.9 s), as nothing recorded or kept their pipelines; `pso_manifest.tsv` now carries 164 `M` rows (Kokiri Forest, Hyrule Field, Castle Town, Hyrule Castle, and 41 from later play, Death Mountain Trail among it), and with the prepare step they are built before the first frame. Pipelines outside the list still stall once, then go into `pso_user.tsv`. With the first 123 alone, a cold first start (empty Metal cache and pipeline archive, M4 Pro) on a Death Mountain Trail save built 9 and 10 at their first draw in two runs, 2.7 and 4.2 s, frames of 1,775 and 1,657 ms; with all 164, none, worst frame 25.6 and 18.5 ms, the prepare step 29.6 and 29.3 s → 32.1 and 31.0 s. - [Early fragment tests from the control section](../../../docs/reference/shaders.md#early-fragment-tests-from-the-control-section): 746 of its 22,352 pixel shaders set the NVN2 flag (control +0x5b), none of them writes depth; before, no NVN2 shader got the attribute. - [Visibility buffers made and dropped for each counting submission](../../../docs/reference/frame-rate.md#visibility-buffers-made-and-dropped-for-each-counting-submission): resolved, 8-12 fps in Kakariko Village and Hyrule Field on an iPhone 17 at the 30 fps cap (25-30 fps with the fix), the main thread waiting in `nvnSyncWait` while each counting submission made and dropped a visibility buffer. - [Texture views re-made for finalized textures every submission](../../../docs/reference/rendering.md#texture-views-re-made-for-finalized-textures-every-submission): resolved. About 310 of the 330–347 views made a frame in Kokiri Forest were made again for finalized textures; now 38 a frame, −1.72 M submit cycles a frame. - [Archive searches repeated while the build table is full](../../../docs/reference/frame-rate.md#archive-searches-repeated-while-the-build-table-is-full): the castle town market at about 12 fps while the GPU had time to spare. - [Pulling stand-ins made for predicted keys that are never drawn](../../../docs/reference/frame-rate.md#pulling-stand-ins-made-for-predicted-keys-that-are-never-drawn): resolved, frames of 50–170 ms on entering the castle town in every run, with one new pipeline or none. - [Predictions replayed every submission of an area's stand-in warm-up](../../../docs/reference/frame-rate.md#predictions-replayed-every-submission-of-an-areas-stand-in-warm-up): resolved, about 2 ms a frame of submit CPU in the village arrival's stand-in warm-up (issue #2438). - [A texture written while it is drawn read whole at every use](../../../docs/reference/frame-rate.md#a-texture-written-while-it-is-drawn-read-whole-at-every-use): resolved, frames of 31–59 ms on entering Hyrule Castle Town, a 1800x1380x8 R16 array (38.5 MB) and three block-compressed textures re-uploaded 31–52 times while the loader wrote them, the array 16–17 times whole; 16–17 re-uploads after, the array's 7–9 at 4.7–14.2 MB, one frame of 25 ms at most. - [One Pro Controller read as two players](../../../docs/reference/services.md#one-pro-controller-read-as-two-players): resolved, no music in Kokiri Forest gameplay and its cutscenes; the controller applet opened over and over and the game kept its music suspended while it had focus. - [A keyboard receiver that declares a gamepad takes player 1](../../../docs/reference/services.md#a-keyboard-receiver-that-declares-a-gamepad-takes-player-1): current until the reporter retests ([issue #1657](https://github.com/seedds/nxnative/issues/1657)). A Keychron Link receiver was player 1 and the Pro Controller player 2, so the game took no input; a raw HID pad is now a player from its first button press. - [The controller applet leaves out the players past the maximum](../../../docs/reference/services.md#the-controller-applet-leaves-out-the-players-past-the-maximum): resolved in the host's tests, not run on the game. One Bluetooth Pro Controller came back as players 1 and 2 after a long disconnect, and the game opened the controller applet 1,671 times with no input ([issue #2237](https://github.com/seedds/nxnative/issues/2237)); the second player's controller now feeds player 1. - [A HID entry that sends nothing counted as a second pad](../../../docs/reference/services.md#a-hid-entry-that-sends-nothing-counted-as-a-second-pad): resolved in the host's tests, not run on the game: the second entry of that Pro Controller (USB location 0, `0x10000003` to its calibration request) no longer counts as a pad, so the pad stays one player read by GameController ([issue #2237](https://github.com/seedds/nxnative/issues/2237)). - [Compute bindings prepare texture copies before the dispatch encoder](../../../docs/reference/rendering.md#compute-bindings-prepare-texture-copies-before-the-dispatch-encoder): resolved, `DM_NVN_DISPATCH_OWN=1` crashed in the Metal driver (`AGX::ComputeContext::prepareForEnqueue`) during the Kokiri Forest load ([issue #1743](https://github.com/seedds/nxnative/issues/1743)); on an M2 Pro 5 of 5 runs crashed before the fix and 3 of 3 loaded the forest after it. - [A fixed frame step plays in slow motion on a slow Mac](../../../docs/reference/frame-rate.md#a-fixed-frame-step-plays-in-slow-motion-on-a-slow-mac): the frame-ratio division. - [Frame-step stubs placed by their function](../../../docs/reference/rewriter.md#frame-step-stubs-placed-by-their-function) - [Repository patches missing under bracketed paths](../../../docs/reference/rewriter.md#repository-patches-missing-under-bracketed-paths): resolved, a build in a folder named with brackets lost this game's frame-step stubs and cuts without an error. - [Retired pool buffers pile up while a game submits nothing](../../../docs/reference/memory.md#retired-pool-buffers-pile-up-while-a-game-submits-nothing): out of focus GPU allocated went from 11 to 20 GB in 12 s with swap following (PR #1392); the likely cause of a session that stayed frozen after a window switch. Not reproduced on the village-deck route (2026-11-09, M6): 40 s out of focus, during the load or standing on the deck, made no pool deletions at all, so the scene that remakes a virtual pool's runs out of focus is still to be found. - [The pool walk's statistics made chunks of a large pool](../../../docs/reference/memory.md#the-pool-walks-statistics-made-chunks-of-a-large-pool): resolved, GPU allocated 3.3 to 15.8 GB in the area load on a 16 GB Mac (issue #2068); 77 overlapping 80 MB chunks of the 2,432 MB pool, made by two log statistics. - [Models black for one frame when a streaming step replaces their textures](../../../docs/reference/rendering.md#models-black-for-one-frame-when-a-streaming-step-replaces-their-textures): resolved, the castle town wall, Hyrule Castle, rocks and trees near the town and Lon Lon Ranch's barn black for one frame, twice, 4-5 frames apart, at the streaming steps that replace their material sets (PR #2022; seen on an M4 Pro and an M5 Max). - [A streamed texture's first read drew its surface dark for one frame](../../../docs/reference/rendering.md#a-streamed-textures-first-read-drew-its-surface-dark-for-one-frame): resolved, the cliff beside the castle town wall in Hyrule Field darker for one frame as the game streamed it a sharper texture set ([issue #1969](https://github.com/seedds/nxnative/issues/1969), PR #2075); the first read now samples the texture's own colours, coarse, for that frame. Seen also in Kakariko Village and its graveyard on a MacBook Air M5 (three flashes in 8 minutes before, none in 4 after, by eye). - [Vertex shaders with layer entry points left out of the prebuild](../../../docs/reference/shaders.md#vertex-shaders-with-layer-entry-points-left-out-of-the-prebuild): resolved, a 230 ms frame at a camera turn in Hyrule Field. The effect vertex shaders (no stage inputs, layer entry points of their own) had no base in the bundled archive and were compiled at their first draw; with the fix (measured with PR #2065's form of it, the same bases as the reflection's `layer_out` gives) the archive holds 33,365 bases instead of 30,690, and the worst frame on the `field-run` route is 39 ms (M6). - [Unused border corrections retain sample registers](../../../docs/reference/rendering.md#unused-border-corrections-retain-sample-registers): about 1.5 ms of GPU a frame at 1080p ([measurements](#unused-border-corrections)). - [Position invariance across shaders](../../../docs/reference/shaders.md#position-invariance-across-shaders): white shapes on plants and bushes for single frames in Kokiri Forest, from the foliage's mesh pre-pass and mesh colour pass rounding the position apart. - [gl_FragCoord.z against the depth stored for its own surface](../../../docs/reference/shaders.md#gl_fragcoordz-against-the-depth-stored-for-its-own-surface): the dark specks flickering in the ground mist (long-standing). The mist's own depth test (`5ad65c0eefd09c7f`, against its depth-only pass `4757b5b373a5406e`) rejected its own surface where Apple's GPU stored the depth one ulp under the fragment's z. Bisected by dropping fragment shaders: after mesh draws took the drop too (`skip_fs_draw`), the specks went with the mist or its depth-only pass, and when that pass wrote `[[position]].z` as depth. Resolved by the rewrite: no specks in play with its 39 rewritten shaders as an overlay (the player's check, 2026-11-09); a full library build on the new chain has not been run. - [Signed integer overflow in translated shaders](../../../docs/reference/shaders.md#signed-integer-overflow-in-translated-shaders): resolved, a bright band on one section of a dark wall in Gohma's room in the Deku Tree, shimmering as the camera moved. The mesh shader `35785cddc971290c` sign-extends a packed 16-bit lightmap / probe coordinate through a multiply by 65536, which the compiler folded as if it could not overflow (found by nawangwe, PR #1844). Not rechecked: the pale patch on the ground at the Kokiri Forest exit, which the PR puts down to the same term on another prop. - [GPU event waits order work across queues](../../../docs/reference/nvn-objects.md#gpu-event-waits-order-work-across-queues): the lighting producer-to-consumer dependency is proven in native SDK code and 297 live traced frames; the halo disappears when it is honored. - [128-bit raw global loads read one word at a time](../../../docs/reference/shaders.md#128-bit-raw-global-loads-read-one-word-at-a-time): the foliage mesh shaders read 464 of their 506 raw words as pieces of 116 `LDG.128`s, one lookup and one load each; GPU p50 16.3 → 12.6 ms with the occlusion fix below. - [Occlusion conditions read once landed](../../../docs/reference/rendering.md#occlusion-conditions-read-once-landed): 213 conditions on its occlusion queries in a dumped frame, all landed at replay and 42% false over the route, were drawn anyway; GPU p50 17.06 → 16.44 ms on an M4 Pro. - [Conditional rendering survives queue continuations](../../../docs/reference/rendering.md#conditional-rendering-survives-queue-continuations): event/fence splits lost disabled draws and pending counter values, causing the remaining patchy scene flashes. - [Native mesh draws omitted while their pipeline compiles](../../../docs/reference/shaders.md#native-mesh-draws-omitted-while-their-pipeline-compiles): compile the first demanded native mesh pipeline before its draw; the prior background path omitted geometry and depth while loading. - [Depth sampled in the pass that writes it](../../../docs/reference/rendering.md#depth-sampled-in-the-pass-that-writes-it): light-blue dashes and blocks over the dark rock at Death Mountain Trail's cave entrance, painted after the walls by a full-screen pass (25a8adc977ff813d) and the decals after it, each sampling the scene depth while writing it. The snow's holes and black blocks in adult Zora's Domain and on frozen walls (issue #1932, PR #1995) are the same kind: its shaders sample the depth they write through texture slot 6, which the same copy covers (not yet run on main). - [An old NUP cache stamp makes the build unpack again](../../../docs/reference/packing.md#an-old-nup-cache-stamp-makes-the-build-unpack-again): after a packer update the v131072 image is checked against the NUP in 21.6 s and kept, instead of unpacked again. - [GPU writes dropped by a pool buffer made over write-protected pages](../../../docs/reference/nvn-objects.md#gpu-writes-dropped-by-a-pool-buffer-made-over-write-protected-pages): resolved. The first fix counted the pages it opened as written: 34 textures read again on the village walk (main at 9983d659, two runs) though nothing wrote them, none with the pages compared instead; its load opens 12,032 pages once, none changed. Lunks measured 4,038 re-uploads and a GPU median of 22.4 ms instead of 13.3 in Hyrule Field on a downstream build that makes chunk and whole-pool buffers here ([issue #1591](https://github.com/seedds/nxnative/issues/1591)). - [GPU textures in storage the CPU can read](../../../docs/reference/nvn-objects.md#gpu-textures-in-storage-the-cpu-can-read): the crossing-stripe ("plaid") ground of Dodongo's Cavern and other areas after several dungeon entries and exits (GitHub issue #1865). The terrain defragmentation moves the normal tiles the GPU drew (260x260 RG8, 9 levels) with `memcpy`; their storage held zeros, as the drawing lived in the host texture alone. Resolved: what the GPU draws into them is written into their storage on the GPU, in the same submission. - [Uncompressed storage beside storage the GPU writes](../../../docs/reference/nvn-objects.md#uncompressed-storage-beside-storage-the-gpu-writes): resolved, the ground opening in holes after walking out of a cave and back (the terrain's height and blend maps came out as zeros once the first normal map had been drawn into their pool); a reload had hidden it. - [A span's sliver inside a texture](../../../docs/reference/nvn-objects.md#a-spans-sliver-inside-a-texture): resolved, whole terrain tiles missing or showing another tile's surface after some walking (a grass square across the path from Castle Town, a grass patch on Death Mountain Trail; [issue #1566](https://github.com/seedds/nxnative/issues/1566)'s reports of this kind); the height and hole maps came out as zeros when less than a page of another span lay in their storage. - [A damaged file in a NUP or a dump has no hash to fail](../../../docs/reference/packing.md#a-damaged-file-in-a-nup-or-a-dump-has-no-hash-to-fail): a contributor's NUP held three `.zs` textures beginning with zeros, and the background loader reloaded the fairy fountain model for ever (412% CPU); this repository's copy has none. - [A control section's words read as a BAHS entry](../../../docs/reference/shaders.md#a-control-sections-words-read-as-a-bahs-entry): six map rows that were no shaders, one of them failing the build; `tera_common` gave its variation pairs once its row was gone. - [Freeing a memory pool during a texture ID walk](../../../docs/reference/nvn-objects.md#freeing-a-memory-pool-during-a-texture-id-walk): the `-[NvPkg buffer]: unrecognized selector` crash after a save or a scene change. - [Extended products behind spirv-cross's optnone helper](../../../docs/reference/shaders.md#extended-products-behind-spirv-crosss-optnone-helper): 348,587 constant divisions left as unoptimized calls; the Kokiri Forest deck 54.0 → 59.1 fps on an M4 Pro. - [HMMA chains keep their matrices in the SIMD-group](../../../docs/reference/shaders.md#hmma-chains-keep-their-matrices-in-the-simd-group): the NDVE movie networks behind the title and the opening, 14.8 → 11.4 ms of compute a frame on this M6. - [Raw global memory lookups that start from their last entry](../../../docs/reference/shaders.md#raw-global-memory-lookups-that-start-from-their-last-entry): the foliage, vertex and compute shaders read structures word by word through loaded addresses; about 2 ms less GPU a frame at the spawn point. - [The GPU idles with the next command buffer already committed](../../../docs/reference/frame-rate.md#the-gpu-idles-with-the-next-command-buffer-already-committed): the 25-40 ms frames. - [The uniform copy thread's hand-off convoys on its mutex](../../../docs/reference/frame-rate.md#the-uniform-copy-threads-hand-off-convoys-on-its-mutex): resolved. On one host build (M4 Pro) the replay thread spent half its busy samples in `ubc_wait`, 221-333k instructions a draw and 23-57 ms of submit CPU a frame against 38.9k and 7.1 ms with `DM_NVN_UB_COPY_THREAD=0`; about 35 shadows a frame that `um_overlay` writes into are now copied on the submit thread (PR #2078). - [The 30 Hz display link in fullscreen on a fixed-rate screen](../../../docs/reference/frame-rate.md#the-30-hz-display-link-in-fullscreen-on-a-fixed-rate-screen): 66.7 ms frames at a 30 fps cap in fullscreen on a MacBook Air M4, with the frame-delta change that applies the cap. - [Pool tables and readback buffers made anew each frame](../../../docs/reference/memory.md#pool-tables-and-readback-buffers-made-anew-each-frame): about 1,500 residency changes per 300 frames. - [Pool tables patched instead of built whole](../../../docs/reference/nvn-objects.md#pool-tables-patched-instead-of-built-whole): the game registers something between most of its draws, its streamed textures map and unmap virtual pool runs dozens of times a second, finalized descriptors stay registered and textures take over each other's storage; each of these walked the 33,025-entry texture table again, about 53 s of a seven-minute walk. - [Pool shaders taken as depth readers](../../../docs/reference/rendering.md#pool-shaders-taken-as-depth-readers): about 80 extra passes a frame, and half the frames over budget. - [A pipeline archive on an external drive is read into memory](../../../docs/reference/memory.md#a-pipeline-archive-on-an-external-drive-is-read-into-memory): 3.5 GB of swapped memory, now mapped. - [A texture in a sparse pool came out CPU-written at its first use](../../../docs/reference/memory.md#a-texture-in-a-sparse-pool-came-out-cpu-written-at-its-first-use): found by the unit test, on this profile's `tex_dirty_upload`. - [Textures made for GPU use kept from their CPU rewrites](../../../docs/reference/nvn-objects.md#textures-made-for-gpu-use-kept-from-their-cpu-rewrites): the terrain tiles' height, blend and hole maps rewritten as tile slots are reused; flat blue holes in the ground after an hour of play. - [`EventFd` needs no network](../../../docs/reference/services.md#eventfd-needs-no-network) - [No virtual audio input devices (SDK 22)](../../../docs/reference/audio.md#no-virtual-audio-input-devices-sdk-22) - [`nn::ndve`'s movie enhancement file](../../../docs/reference/shaders.md#nnndves-movie-enhancement-file) - [Task and mesh shaders in BNSH, and indirect mesh draws](../../../docs/reference/shaders.md#task-and-mesh-shaders-in-bnsh-and-indirect-mesh-draws) - [Indirect task / mesh draws on the encode thread](../../../docs/reference/rendering.md#indirect-task--mesh-draws-on-the-encode-thread): the iOS app aborted when a save loaded. - [Mesh draws with no task shader above 1024 workgroups](../../../docs/reference/rendering.md#mesh-draws-with-no-task-shader-above-1024-workgroups): GPU page faults 30-40 s after a save loaded (issue #1326), from foliage mesh draws of up to 1,771 workgroups. - [F2IP](../../../docs/reference/shaders.md#f2ip) - [A pixel shader's depth after a partly written target](../../../docs/reference/shaders.md#a-pixel-shaders-depth-after-a-partly-written-target): 46 depth-writing pixel shaders read their depth from the wrong register, agl_common's depth pass among them. - [An HMMA outside a chain overwrote the chain's accumulator](../../../docs/reference/shaders.md#an-hmma-outside-a-chain-overwrote-the-chains-accumulator): dotted rows across fog and light in play. - [HMMA rounds its f16 accumulator once per 8 products](../../../docs/reference/shaders.md#hmma-rounds-its-f16-accumulator-once-per-8-products): resolved, its NDSR upscaler and NDVE movie network were a few f16 steps off the console in about half of each HMMA's outputs (nothing visible). - [The NDVE movie network by the host](../../../docs/reference/shaders.md#the-ndve-movie-network-by-the-host): the title and movies' network as the host's kernels, bound to nn::ndve's model (on by default; `DM_NDVE_ENGINE=check` compares it with the game's in the run). - [LDSM reads rows other lanes stored](../../../docs/reference/shaders.md#ldsm-reads-rows-other-lanes-stored): white blotches over Kokiri Forest through agl's upscaler on an M4 Pro. - [A shader that writes a whole frame through raw global memory](../../../docs/reference/frame-rate.md#a-shader-that-writes-a-whole-frame-through-raw-global-memory) - [Stage memory byte stores through a compare-and-swap loop](../../../docs/reference/shaders.md#stage-memory-byte-stores-through-a-compare-and-swap-loop): 515 shaders, the foliage mesh shaders among them; pull request #1503 measured the foliage pass at 8.3-8.6 ms on an M3 Pro with the loop, while on an M6 the loop, the store and AND / OR measure the same. - [Memory pools nothing gave an address to stay out of the raw pool table](../../../docs/reference/memory.md#memory-pools-nothing-gave-an-address-to-stay-out-of-the-raw-pool-table) - [Shaders report which textures they read through a pool](../../../docs/reference/memory.md#shaders-report-which-textures-they-read-through-a-pool) - [Repeated GPU out-of-memory failures end the game](../../../docs/reference/memory.md#repeated-gpu-out-of-memory-failures-end-the-game) - [Pools that hold shader code made resident to key the shaders](../../../docs/reference/memory.md#pools-that-hold-shader-code-made-resident-to-key-the-shaders): first reported here (222 pools, 334 MB on a build with other changes); on main the raw pool table makes those pools resident anyway (4,276 resident pools before the fix, 4,277 after, on the village walk). - [Guest files keep the blocks of memory the game gave back](../../../docs/reference/memory.md#guest-files-keep-the-blocks-of-memory-the-game-gave-back): the iPad stopped at 5.0 GB free with 6.6 GB of guest files. - [A GPU whose access macOS revoked stops the game](../../../docs/reference/rendering.md#a-gpu-whose-access-macos-revoked-stops-the-game): after a GPU hang or page faults (issue #1326), nothing was drawn for about 70 s before the game aborted, and in full screen WindowServer's watchdog killed it. - [A texture ID with no texture kept its cached view](../../../docs/reference/rendering.md#a-texture-id-with-no-texture-kept-its-cached-view): the page faults at f3,677 and f4,851 with the render-target reclaim on. - [Compute pipelines compiled at first use](../../../docs/reference/shaders.md#compute-pipelines-compiled-at-first-use): the offline archive holds a compute pipeline for each of the 4,125 compute shaders (480 MB, 204 s on an `applegpu_g18g` Mac). From `save_ref` to the village with an empty prewarm list, all 103 compute pipelines came from it and none were built. PR #1522 measured what that saves with the Metal cache cold: 195 s of pipelines on the submit thread before, 155 s of it in the title movie's twelve kernels. - [Compute pipelines outside the bundled archive built at every start](../../../docs/reference/shaders.md#compute-pipelines-outside-the-bundled-archive-built-at-every-start): without the bundle, the first start's flushes wrote 88 compute pipelines into the device's archive, and the second start took 88 from it. - [Every start after play captures the whole pipeline archive again](../../../docs/reference/rendering.md#every-start-after-play-captures-the-whole-pipeline-archive-again): 9 files of play beside a compaction's 5 made the next start capture 8,543 bases again, 713 s before the first frame on an M4 with 16 GB; by tier, 23.6 s (the 262 bases new to the list) and 2.4 s after. - [Metal's archiver store filled by one flush period](../../../docs/reference/rendering.md#metals-archiver-store-filled-by-one-flush-period): a compacting prewarm of 40,000 rows filled the archiver's store, Kakariko drawn without 1,502 pipelines (PR #1735). - [A cold start leaves a file per timer flush, and the next start compacts them all](../../../docs/reference/rendering.md#a-cold-start-leaves-a-file-per-timer-flush-and-the-next-start-compacts-them-all): a cold start wrote 80 run files and the next start captured 8,859 pipelines again, 762 s before the first frame on an M4 with 16 GB; with the change 5 files, and 3.3 s (issue #1747). - [Archive lookups asked every file, so a few files made a start compile every base again](../../../docs/reference/rendering.md#archive-lookups-asked-every-file-so-a-few-files-made-a-start-compile-every-base-again): 9 files of play beside a compaction's 5 made the next start capture 8,543 bases again, 713 s before the first frame (PR #1880); an index beside each file makes a lookup ask one file whatever the number of files. - [Texture IDs of one storage share one packaged texture](../../../docs/reference/nvn-objects.md#texture-ids-of-one-storage-share-one-packaged-texture) - [A write missed while opening shared pool pages](../../../docs/reference/nvn-objects.md#a-write-missed-while-opening-shared-pool-pages): a tracker race reproduced with synthetic memory while investigating issue #2086. The race is fixed; it reaches only host pages two pools share, so it does not by itself explain that issue's stale terrain map (about 33 pages), whose cause is open. - [A texture shared by its packaged texture's serials left the residency set with one of them](../../../docs/reference/nvn-objects.md#a-texture-shared-by-its-packaged-textures-serials-left-the-residency-set-with-one-of-them): GPU page faults 15-45 minutes into play (issue #1326), on 260x260 terrain tiles made GPU textures at the GPU's first write. - [Finalizing a copy of a texture object](../../../docs/reference/nvn-objects.md#finalizing-a-copy-of-a-texture-object) - [Dynamic resolution that never moves, and draw passes that wait for each other](../../../docs/reference/frame-rate.md#dynamic-resolution-that-never-moves-and-draw-passes-that-wait-for-each-other) - [Dynamic resolution at its floor while the GPU has time to spare](../../../docs/reference/frame-rate.md#dynamic-resolution-at-its-floor-while-the-gpu-has-time-to-spare): a stale frame-start timestamp, and event waits that idled the GPU - [A full table of running GPU writes ends passes for nothing](../../../docs/reference/frame-rate.md#a-full-table-of-running-gpu-writes-ends-passes-for-nothing) - [Float atomics in storage memory](../../../docs/reference/shaders.md#float-atomics-in-storage-memory) - [textureGatherOffsets and a loop over its offsets](../../../docs/reference/shaders.md#texturegatheroffsets-and-a-loop-over-its-offsets) - [Image atomics on a 32-bit colour texture](../../../docs/reference/shaders.md#image-atomics-on-a-32-bit-colour-texture) - [A host's own store in a fragment shader moves its depth test](../../../docs/reference/frame-rate.md#a-hosts-own-store-in-a-fragment-shader-moves-its-depth-test) - [Dead-code removal dropped a coordinate of a multi-output instruction](../../../docs/reference/shaders.md#dead-code-removal-dropped-a-coordinate-of-a-multi-output-instruction) - [Mesh stage memory reserves the primitive index bytes for every primitive block](../../../docs/reference/shaders.md#mesh-stage-memory-reserves-the-primitive-index-bytes-for-every-primitive-block): the white blotches over Kokiri Forest's foliage and ground (light tiles drawn with corrupt triangles). - [Mesh attributes: every vertex block before the primitive blocks](../../../docs/reference/shaders.md#mesh-attributes-every-vertex-block-before-the-primitive-blocks): the Poe Sisters and Phantom Ganon paintings' per-primitive colours (pull request #1590), and the point size of the 5 point mesh shaders. - [One command allocator for each of a frame's submissions](../../../docs/reference/frame-rate.md#one-command-allocator-for-each-of-a-frames-submissions): reported from two iPhones, where the submit thread waited for the GPU up to 17 times a frame with the pool of 8 in flight (PR #2090); a frame's submissions now share an allocator. Open: no run of the game with it yet. - [The alias flush on every encoder's end barrier](../../../docs/reference/frame-rate.md#the-alias-flush-on-every-encoders-end-barrier): the main submission's 8 command allocators at 36-80 MB each and 6-15 ms a frame in scenes of about 150 passes before main's Ocarina fixes; on main at 2eb71820, CPU-visible Metal memory 392 → 298 MB and no measurable frame-time change (M3 Pro, 18 GB). - [Live resolution variants share physical image storage](../../../docs/reference/rendering.md#live-resolution-variants-share-physical-image-storage): the dynamic-resolution depth buffer (`COMPRESSIBLE` alone) kept 15-16 live sizes at one offset, 14.5 MB each, as separate Metal textures (GPU allocation 7,710 → 7,520 MB in Kokiri Forest before main's Ocarina fixes; the 17 sizes still form on main at 2eb71820, where all Depth32Float textures take 225 MB with them shared). - [Render targets that move with the dynamic resolution get new Metal memory each time](../../../docs/reference/rendering.md#render-targets-that-move-with-the-dynamic-resolution-get-new-metal-memory-each-time): the targets in the 305 MB pool, recycled since #1831. - [Pixel shader inputs the mesh shader does not write](../../../docs/reference/shaders.md#pixel-shader-inputs-the-mesh-shader-does-not-write): the foliage point meshes' pipelines (pixel shaders reading generic attribute 5 that no mesh output writes). - [Mesh outputs as wide as their pixel shaders read](../../../docs/reference/shaders.md#mesh-outputs-as-wide-as-their-pixel-shaders-read): the foliage mesh `626fbf48041cb82b`, padded to 35,840 bytes of mesh output, past Metal's 32 KB. - [AV1 movies stop an M1 iPad with SIGILL in dav1d](../../../docs/reference/movies.md#av1-movies-stop-an-m1-ipad-with-sigill-in-dav1d): the title movie on an M1 iPad Pro. - [`nn::codec` packets decoded inside the submission](../../../docs/reference/movies.md#nncodec-packets-decoded-inside-the-submission): resolved, movies stuttering at a steady 60 fps; its player feeds the decoder and gives the shown pictures back on one thread. - [dav1d decoded one frame at a time](../../../docs/reference/movies.md#dav1d-decoded-one-frame-at-a-time): resolved, the title movie's heavy packets on an M2 Pro; and the drain that must go to its end. - [`devicectl` copies a large RomFS image through the Mac's memory](../../../docs/reference/packing.md#devicectl-copies-a-large-romfs-image-through-the-macs-memory): the 26.2 GB image's pushes to an iPad filled a 16 GB Mac's swap and failed; now sent over AFC. - [`devicectl`'s `--remove-existing-content` empties the app's whole container](../../../docs/reference/packing.md#devicectls---remove-existing-content-empties-the-apps-whole-container): the first complete copy of the image, deleted by the DLC step after it.