Skip to content

DM_CTL poolstat and texstat, and what fills Ocarina of Time's GPU working set (iPad out of memory at 8,259 of 8,192 MB) - #1436

Open
aksmith-git wants to merge 2 commits into
seedds:mainfrom
aksmith-git:pr/gpu-memory-diag
Open

aksmith-git wants to merge 2 commits into
seedds:mainfrom
aksmith-git:pr/gpu-memory-diag

Conversation

@aksmith-git

@aksmith-git aksmith-git commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

What and why

Ocarina of Time (Switch 2, 0400486024DEC000, profile in #1299) runs on an iPad (iPad17,3, M5, 12 GB) at about 22 ms a frame until the first area load after a save, then runs out of GPU memory: GPU out of memory: the residency set holds 9124 objects, 8259 MB against Metal's 8,192 MB working set, with kIOGPUCommandBufferCallbackErrorOutOfMemory, pink or broken frames and 120-300 ms of GPU a frame. Handheld mode fails later, 27 MB over. #1369 hit the same wall on an iPhone. This PR does not fix it: it adds two diagnostics and records what fills the working set, so the next attempt starts from numbers.

  • DM_CTL poolstat: the resident memory pools, the texture storage inside them, the largest pools.
  • DM_CTL texstat: host textures by kind; those made from guest memory by frames since their last use (NvPkg gets a last-use frame); render targets by shape with distinct guest storage addresses, their span, and live serials. Each host texture is counted once.
  • docs/reference/memory.md: the measurements and three experiments that did not help (symptom index regenerated), with a line in Ocarina of Time's note; docs/env_switches.tsv lists the two lines under DM_CTL.

Measurements (M4 Pro, handheld, 7 minutes from a save; guest memory in files)

These were taken before main's raw pool table and pool read bitmap changes, which address the pool row below (the 2,432 MB pool now stays out of the table). The doc entry says so and links them; the render-target row is still open. The iPad has not been measured on a host with both changes.

working set 12.1-12.5 GB (iPad: 8 GB)
memory pools (raw table keeps all resident) 5.9 GB; 2,625 of 5,370 MB is texture storage, so resident twice
textures from guest memory 2.36 GB, 5,527, every one resolved in the last second
render targets 2.4 GB, 860; transient ones duplicated at shifting offsets (103 x 720x405 RGBA16F over a 9.4 MB span, 36 + 36 x 1440x810 over 46.6 MB)

The raw table is needed: 783 of the 8,319 shaders a play used do raw loads.

Not kept (details in the doc entry): leaving texture-only pool memory out of the raw table (one GPU hang before the first frame), a large pool through its chunks only (no saving), reclaiming taken-over shared textures by alias-lookup idleness (active targets looked idle).

Checked

@github-actions github-actions Bot added host The Horizon host: SVCs, services, threads, memory (dm/host) nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests) docs Documentation labels Nov 6, 2026
fpedrolucas95 added a commit to fpedrolucas95/nxnative that referenced this pull request Nov 6, 2026
…lls Ocarina of Time's GPU working set (iPad out of memory at 8,259 of 8,192 MB)

# Conflicts:
#	dm/gfx/nvn/nvn_mtl.m
#	dm/host/hle_hid.c
gnosticgeek pushed a commit to gnosticgeek/nxforge that referenced this pull request Nov 6, 2026
…fills Ocarina of Time's GPU working set (iPad out of memory at 8,259 of 8,192 MB)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qteK29T18U8p1c9LsVY8g
gnosticgeek pushed a commit to gnosticgeek/nxforge that referenced this pull request Nov 6, 2026
) replaces the pull request heads

Upstream landed Ocarina of Time in main (chain adec7d81d970: "holds 60 fps in
Kokiri Forest, 4 GB less") and closed the pull requests it folded in, with
reworded and partly different code. Mixing a pull request head with its landed
version hunk by hunk silently broke things (two GlobalToStorage tables, a
duplicate AF2ip, two mtk_pso definitions), so the rule here is one version per
feature:

- Pull requests upstream closed (seedds#1299, seedds#1300, seedds#1312, seedds#1343, seedds#1345, seedds#1353,
  seedds#1354, seedds#1359, seedds#1360, seedds#1373, seedds#1428, seedds#1430, seedds#1434): their hunks are taken out
  of our side before the three-way merge, so upstream's landed version stands
  alone. Gone with them: bindless pool table patching (reg_bump), the ghost
  table's per-submission hold, the growing raw pool table and its write-log
  sizing, seedds#1428's pool_buf spares (upstream's pool_table_take is the same
  idea), the ixc-based indirect mesh draw (upstream uses mdx), and their tests
  (t_pool_table, t_mesh_indirect, t_gmem_raw, finalized_bindless_pool,
  test_bnsh_nvn2, test_ndve).
- Open pull requests that upstream's landing supersedes: seedds#1394 (upstream's
  mtl_pool_addressed) and seedds#1393 pool_skip_retired (upstream's budgeted pool
  walk; the profile does not use it) are dropped with their tests and docs.
  seedds#1368's mesh pipeline prewarm is dropped too: upstream kept the background
  build in nvn_geometry.inc, and the prewarm's mtk_build / mtk_claim /
  g_warm_mps went with it.
- Kept, as upstream does not have them: NXForge's own features (GPU watchdog,
  present filters and anti-aliasing, aniso floor, quick settings, pipeline
  archive compaction, upload breakdown, memory report lines) and the still
  open pull requests seedds#1304, seedds#1305, seedds#1308, seedds#1320, seedds#1331, seedds#1332, seedds#1355, seedds#1356,
  seedds#1369, seedds#1376, seedds#1377, seedds#1379, seedds#1382, seedds#1390, seedds#1391, seedds#1392, seedds#1414, seedds#1420,
  seedds#1436, seedds#1439, seedds#1446, seedds#1453.
- Combined where both sides changed the same lines: GW_N 1 << 16 with seedds#1320's
  wide-write intervals (GW_PAGES = SBW_WPAGES, 128 slots), the CPU upload log
  with upstream's linear upload route, arch_open_end before the compaction
  limit, cbn_prog in geo_gs_one (seedds#1382).
- Translator: upstream's sources and binaries, plus seedds#1414's
  InstEmitAmpereMem diff and the LDSM SubgroupBarrier in
  InstEmitAmpereTensor.cs. The binaries and dmsh_chain.lock are upstream's
  until the Mac rebuilds them (dotnet publish, tool_lock --update-lock,
  dmsh_chain --header); dmsh_chain.h is regenerated for now.
- RED_ONCE keys: upstream took 153 and 154, so the present anti-aliasing lines
  move to 156 and 157 and seedds#1420's packaged-entry line to 158 (array 159).
- Host: hle_net.c and t_net_eventfd.c are upstream's (its eventfd poll waits
  out the whole timeout, which our 5d7920c fixed another way).
- Kept ours: dm/tools/romfs_image.py and its test, so the 26 GB RomFS image is
  not packed again.

dm/tools tests on Linux: same failures as on 875449e (Linux-only quirks),
plus test_nlg4 and test_dmsh_pool_arrays, which fail on upstream main too.
Not yet built on the Mac.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qteK29T18U8p1c9LsVY8g
fpedrolucas95 added a commit to fpedrolucas95/nxnative that referenced this pull request Nov 6, 2026
…n Kokiri Forest, about 4 GB less memory) beside this tree's own work

Kept from this tree: the growing raw pool table with two-word hints (plus upstream's read-before-OR on its write
log), seedds#1439's held count, seedds#1436's diagnostics, seedds#1447's texture release, seedds#1435, the idle evictions, the GPU watchdog,
range-tracked GPU writes, the packaged-texture cache (g_pkg_alias), the synchronous task / mesh pipeline build with
prewarm, this tree's F2IP.

Taken from upstream: the pool tables (only what shaders read is made, within a budget; pool draws count as depth
readers only when one was seen reading, replacing seedds#1430's rule; table reuse), sub_cut's busy rule, the indirect
task / mesh draw through mdx, the empty mesh fragment, the larger GPU-write table (65,536 slots), linear textures by
rows, the atomic alias on any 32-bit format, its eventfds (with this tree's select and fcntl on them), its
prepo Send and audio update lock, its shader split and Ocarina profile and tables.

Duplicates both sides added were removed (SDK 22 answers, F2IP, test stubs); RED_ONCE 153 split; t_pool_table
(the replaced pool table code) removed; tests adapted to the taken rules. Translator rebuilt; chain 695a9393d125.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@williamstonef

Copy link
Copy Markdown

Same device class here: iPad Pro 11-inch (M5), 12 GB, iPadOS 27.2, same game (v131072), NX2Apple 1.2.3 engine pin 88c7e03f51e3, shader chain adec7d81d970. Handheld + MetalFX upscaler, Encode thread off, Frame buffering on.

It runs at about 54 fps (frame p50 18.5 ms, p95 29 ms over the last windows), but memory climbs the whole time (host memory up to frame lines):

frame resident GPU allocated
300 2.2 GB 2.4 GB
10,500 6.8 GB 9.5 GB
21,900 9.4 GB 15.3 GB
41,700 10.6 GB 16.4 GB

At 20:00 of play (frame 41,806) the host log just stops, with no CRASH line and no GPU out of memory line. iOS wrote a JetsamEvent for the same minute: the system hit vm-pageshortage and ended 118 processes (background daemons and suspended apps), with DmShell frontmost at 9.83 GB resident, lifetime max 11.06 GB. So on this iPad the jetsam kill came before Metal's own out-of-memory failure.

If it helps, I can run any of the DM_CTL poolstat/texstat captures on this iPad.

Drafted with Claude; reviewed by me.

@aksmith-git

Copy link
Copy Markdown
Contributor Author

Thanks, that matches what pushed us to the guest-file path. On an iPad Pro 13-inch (M5) with the same game (v131072) and guest memory in files (DM_GUEST_FILE=1, plus the hole punching from #1492 and the idle upload staging release from #1491), a 15-minute handheld run stayed at 3.5 GB resident (GPU allocated 10.7 GB) from start to end, no jetsam: 49,800 frames, no out-of-memory line. With guest memory in RAM the climb you see is mostly the guest's own pages, which iOS counts against the footprint; in files they're clean file-backed pages the system can evict. If you can, a run with DM_GUEST_FILE=1 in env.txt and the memory up to frame / [mem] guest file lines would show whether it holds there too.

Adrian Smith added 2 commits November 8, 2026 15:21
…sured on Ocarina of Time (iPad GPU out of memory at 8,259 of 8,192 MB)

poolstat logs the resident memory pools and the texture storage inside them,
with the largest pools. texstat logs the host textures by kind: those made
from guest memory by frames since their last use (NvPkg gets a last-use
frame), and render targets by shape, with how many distinct guest storage
addresses, over what span, and how many live serials name them. Each host
texture is counted once.

docs/reference/memory.md records the Ocarina of Time measurements they gave
(12.1-12.5 GB on an M4 Pro Mac in handheld mode against the iPad's 8 GB:
5.9 GB of pools the raw table keeps resident, 2.6 GB of it texture storage
also held as Metal textures; 2.36 GB of guest-memory textures all used every
second; 2.4 GB of render targets with transient ones duplicated at shifting
offsets) and three experiments that did not help.
…lls them and the host tests link without nvn_mtl.m
@aksmith-git

Copy link
Copy Markdown
Contributor Author

Rebased on main eed7c42 (the conflicts were the note's entry list and the DM_CTL row of env_switches.tsv). Builds with the Ocarina of Time profile; t_hid 169 checks, 0 failed; docs_symptoms --check current; test_docs OK. test_env_switches fails on main itself (DM_NVN_CUT_FIRST, DM_NVN_RT_RECLAIM, DM_NVN_VC_WAYS, DM_NVN_BC_ALIAS), nothing from this change.

Fasen24-AI added a commit to Fasen24-AI/swordboiMacNative that referenced this pull request Nov 10, 2026
…set)

Upstream pull requests applied in this commit (their authors' work, brought onto this tree with a three-way merge; the description is in each PR):
- seedds#1436 by aksmith-git (open): DM_CTL poolstat and texstat, and what fills Ocarina of Time's GPU working set (iPad out of memory at 8,259 of 8,192 MB)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation game: 0400486024DEC000 A game host The Horizon host: SVCs, services, threads, memory (dm/host) nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants