Skip to content

Render targets dead by overwrite are reclaimed (DM_NVN_RT_RECLAIM, off by default): Ocarina of Time's Kokiri load holds 1.1-1.2 GB of render targets instead of 3.5 GB - #1588

Closed
nafields wants to merge 1 commit into
seedds:mainfrom
nafields:rt-reclaim

Conversation

@nafields

@nafields nafields commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

What and why

Refs #1587, which has the design and the data.

Symptom. Ocarina of Time's Kokiri Forest load adds 3.2 GB of render targets to the residency set in about 30 s, and on a 16 GB Mac swap follows wired memory.

Cause. The game keeps its render targets as texture objects over one 305 MB virtual pool and reuses the storage by overwrite: 781 live targets over 74 MB of storage. Each one keeps a private Metal texture, and treg_tex makes one for every registered ID, dead or not.

Change (DM_NVN_RT_RECLAIM=1, off by default; nvn_mtl.m, rc_*):

  • A shared texture is dead once other textures' whole-attachment passes have covered its storage range since its own last write. A partial pass covers nothing.
  • Its Metal texture goes to a free list keyed on the full descriptor. It is handed over after its last use's fence, zero-filled, and trimmed after 15 idle frames.
  • A reclaimed ID reads as an empty slot until a pass writes it again.
  • Never reclaimed: IMAGE textures, copy and clear destinations, readback sources, pool-copy overlaps, sparse pools, textures filled from pool bytes, and history targets read before their next write.

Reference entry: Render targets dead by overwrite. The game note has a line for it, and env_switches.tsv lists the switch.

What it builds on:

Checked

  • Game: Ocarina of Time (0400486024DEC000, v131072), Kokiri Forest route to f7,500, M2 Pro 16 GB, macOS 26. Two runs with the switch on:
    • render targets in the set: 1.1–1.2 GB instead of 3.5 GB;
    • peak wired memory: 7.6–8.8 GB;
    • swap: +0.05–2.4 GB.
    • Shots at f1,500 and f2,500 match a tree without the switch in scene and mean brightness (127.4 / 96.3 against 128.9 / 96.1).
  • Rebased on main, switch off (the default):
    • make -C dm/host GAME=0400486024DEC000;
    • make -C dm/host/tests GAME=0400486024DEC000 -j run;
    • check_t_alias_takeover (28, 0 failed) and check_t_image_alias (329, 0 failed);
    • test_docs.py, env_switches.py --check, docs_symptoms.py.
  • Not done:
    • No unit test drives the reclaim yet.
    • scripts/regress.py check wasn't run: only Ocarina of Time is available here. The switch is off by default, so other games run as before.
    • t_nvn and t_gfx_suspend don't link on main b2d46b8 (_dm_vi_frame_queued), independent of this change.

Docs

  • The finding is in docs/reference/rendering.md and the game note, and python3 dm/tools/docs_symptoms.py was run
  • No chain tool changed
  • No game files, keys or data extracted from a game in the change or the attachments

@github-actions github-actions Bot added nvn NVN on Metal and its tests (dm/gfx/nvn, dm/gfx/tests) docs Documentation game: 0400486024DEC000 A game labels Nov 6, 2026
@nafields

nafields commented Nov 7, 2026

Copy link
Copy Markdown
Contributor Author

Known issue, fix in progress: with DM_NVN_RT_RECLAIM=1, Ocarina of Time takes GPU page faults at the same frames in every run (f3,677 and f4,851, both compute command buffers). A run with the switch off clears both. The suspect is the 15-frame trim: it releases a reclaimed texture, and a cached view or resource ID of it (the texture-ID registry's cached views, or a patched texture-pool slot) still names it. Runs with the trim off, and with reuse off, are under way to separate the two. The fix will be pushed to this branch, with what it was. The switch is off by default, so main's behaviour is unchanged meanwhile.

@nafields

nafields commented Nov 7, 2026

Copy link
Copy Markdown
Contributor Author

Update on the page faults reported above: found and fixed in #1607, which this PR now depends on. The cause wasn't in the reclaim itself. In tex_by_id's slow path (main's code), a texture ID with no texture kept its cached view: treg_tex was nil and the base view's parentTexture was nil too, so nil == nil matched. The pool tables' fast path then wrote the resource ID of a texture the trim had released and taken out of the resident set. With #1607, runs with the reclaim on to f7,750 have neither the f3,677 nor the f4,851 fault, the pace is the same, and the reclaim counts are alike. A fault at f786 remains; it has another cause and happens with the reclaim off too. This branch needs no change of its own.

…carina of Time's Kokiri load no longer piles up 3.4 GB of render targets

Symptom: at Ocarina of Time's Kokiri Forest load on a 16 GB M2 Pro, the residency set grew from 1.6 to 6.6 GB in about 30 s, 3.2 GB of it render targets (72 → 1,068 textures, 31 → 313 descriptors); swap followed wired (wired 3.0 → 9.8 GB while swap rose 1.1 → 3.2 GB and available memory fell to 15%), and the runs reached the +4 GB swap limit.
Cause: the game keeps its render targets as texture objects over one 305 MB virtual pool, reused by overwrite: at f1,200, 781 live targets (2,788 MB of sizes) sat over 74 MB of storage, and 716 of the 828 live ones (2,697 MB) had their whole storage range written by other targets since their own last write. On the console those cost the pool's bytes; here each kept a private Metal texture, and the texture-ID registry (treg_tex) made one for every registered ID, dead or not.
Fix (behind DM_NVN_RT_RECLAIM=1): a shared texture is dead once other textures' render passes have covered its whole storage range in its pool since its own last write. Coverage counts only passes whose draws and clears (viewport ∩ scissor) covered their whole attachment; a partial pass covers nothing. A dead texture leaves the sharing table with its serials' mappings; its Metal texture goes to a free list keyed on the full Metal descriptor, is handed to the next texture of that descriptor after the fence of its last use and zero-filled as a new one is, and is released after 15 idle frames. A reclaimed serial's texture ID reads as an empty slot until a render pass writes it again, which notes the serial so its IDs are taken again. Never reclaimed: IMAGE textures, copy / clear destinations, readback sources, anything a pool copy overlaps, sparse pools, textures filled from pool bytes, and textures read before their next write after more than 4 deaths (luminance and bloom chains: 429 textures, 324 MB in a run to f4,978).
Checked: Ocarina of Time on an M2 Pro 16 GB, Kokiri Forest route to f7,500. Render targets in the set stayed at 1.1-1.2 GB instead of 3.5 GB through the load, peak wired 7.6-8.8 GB, and swap grew by 0.05-2.4 GB. A third of attachment passes were partial and cover nothing. Shots at f1,500 and f2,500 match a tree without it in scene and brightness (127.4 / 96.3 against 128.9 / 96.1). The free-list key and the aliasing rule rest on seedds#1583's heap_alias and rt_sizes probes: a texture placed over another's memory reads its bytes only with the same width, height, bytes per pixel and compression, and a single dynamic-resolution step scrambles it. Those game runs were on a tree that also carried PRs 1417 and 1484 and the bind-time pointers. Rebased on main, with the switch off by default: host build, host tests, t_alias_takeover (28), t_image_alias (329), test_docs. No unit test drives the reclaim itself yet, and regress.py check was not run (only Ocarina of Time is available here). Reference entry and game note line added; env_switches.tsv lists the switch.
seedds added a commit that referenced this pull request Nov 7, 2026
… released texture on the pool tables' fast path (GPU page faults) (PR #1607)

tex_by_id kept an ID's cached view when cur == v || v.parentTexture == cur. When the ID has no texture now, treg_tex returns nil, and a cached view that is the base texture has no parent either, so nil == nil kept it and stamped it valid; tex_fast_rid then gave the pool tables a released texture's resource ID. The view is kept only when cur is non-nil. Ocarina of Time with PR #1588's reclaim: the faults at f3,677 and f4,851 are gone. An A/B walk on main reached the path zero times, with the same red lines and pace.

Co-authored-by: nafields <2613057+nafields@users.noreply.github.com>
john62036203-ops added a commit to john62036203-ops/NXNativeWindows that referenced this pull request Nov 7, 2026
…imed, DM_NVN_RT_RECLAIM=1, off by default: Ocarina's Kokiri load no longer piles up 3.4 GB of render targets)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013SzykpcKm2eZPvPjGPKx1C
@PromethOps

Copy link
Copy Markdown

Tested this PR on main. In my scene, turning the reclaim on corrupts the picture. Details below, in case they help.

Setup

Results

Picture Residency set at f2400 Resident / GPU allocated at f1800
Switch unset clean 5,296 allocations, 4,422 MB 8,819 / 10,636 MB
DM_NVN_RT_RECLAIM=1 a large flat green area over the top-left half of the screen, with a stepped lower edge, at f1500 and f2400 5,213 allocations, 4,286 MB 8,745 / 10,906 MB

The green shows in every reclaim-on run (two). There are no GPU errors in any run. On this base the set is only 136 MB smaller, and GPU allocations are 270 MB larger: main has already cut most of this pool's targets since you measured.

What the reclaim's own log says. The "most read while dead" targets are the ones in the 305 MB virtual pool around the scene colour target:

[768x432 f5 off 0x1800c00: 5 deaths read before a write] [384x216 f10 off 0x18c0e00] [384x216 f52 off 0x1921000] [384x216 f63 off 0x1a41600] …

That matches what I found on #1453 (#1453 (comment)):

  • The engine packs live targets into the part of the 1536×864 R11G11B10F scene target at 0x1800c00 that a 768×432 dynamic-resolution render leaves untouched in NVN's block-linear layout.
  • One example: a 384×216 RGBA32F history at 0x1aa1800 starts exactly at x = 768 of block row 3.
  • So in this pool, live targets sit inside other targets' ranges by design.
  • I see that rc_cover already ignores partial passes. So the scene target's own 768×432 pass shouldn't kill anything, and I haven't traced which full pass declares these targets dead.
  • The read-while-dead targets are all in that packed region, though, and their IDs reading as empty slots would explain the flat colour.
  • A full pass of a smaller target over the scene target's tail can cover a live neighbour's bytes. This can happen when the engine moves the packing as the resolution changes. A pass's coverage could then be checked against the block-linear bytes it really writes, per target layout, rather than its pool range.

Two small things

  • rc_on takes getenv("DM_NVN_RT_RECLAIM") != NULL, so DM_NVN_RT_RECLAIM=0 turns the reclaim on. My first "off" run was really on. env_switches.tsv documents =1, so atoi, or sw_mode like the other switches, would match it.
  • The first "most read while dead:" line printed garbage bytes before the list (most read while dead:PЕZ{). The line buffer looks uninitialized when the list is empty.

I can rerun with extra logging on this save if useful, for example the coverage decision for the target at 0x1aa1800.

@dpadr

dpadr commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

Same green area here with DM_NVN_RT_RECLAIM=1 (with #1607) on an M2 MacBook Air (16 GB), OoT v131072 in Kokiri Forest, handheld; clean with the switch unset.

seedds added a commit that referenced this pull request Nov 8, 2026
…nd a freed target's heap waits resident for the next target it fits: Ocarina of Time's dynamic resolution, free to move since 04419bc, made about 3 new Metal textures a frame in its 305 MB target pool; on an M6 (16 GB), alternating the switch every 300 frames while the resolution moved, the target site added 455-521 MB to the residency set per 300 frames off and 27-37 MB on, frame p90 33-50 ms against 17-18 ms (issue #1831)

- nvn_mtl.m rh_texture: a COMPRESSIBLE texture in a CPU_NO_ACCESS pool (private storage; not display, video, linear or sparse; not a resolution family's heap) is made in a placement heap of its exact size. When the texture is freed (defer_drain, after its submissions completed) its heap goes to a free list, still resident; the next such target up to an eighth smaller than the heap is placed in it and filled as a new allocation is (tex_init_fill or zeros). No alias flush is added: the memory was the old texture's only up to its completed last use
- rh_trim, when a submission begins: heaps that waited 3 s, and the oldest past 256 MB waiting, leave the residency set
- The memory line counts the heaps made, reused, freed and waiting. DM_NVN_RT_RECYCLE=0 turns it off; ab alternates it with the DM_AB phases
- Why not share the pool's memory as on the console: a heap mirroring the pool (PR #1453) and a reclaim of targets dead by overwrite (PR #1588) corrupt the picture, as the game keeps live targets in the part of the full-size scene target its current resolution leaves unused, which only NVN's layout allows. The live targets' steady total (about 2.1 GB in the village) stays open
- t_rt_recycle (direct and deferred encoding, and with the switch off); t_image_alias's single-description check now asks that the texture is not in a family heap

Measured with the shaders of chain f9f01e4f9994 (on disk), on a host pinned to it. Village screenshots clean, no GPU errors or red lines. Unit tests: t_rt_recycle, t_image_alias, t_alias_takeover, t_upgrade_alias, t_cpu_tex and the gfx run pass (t_present_crop once rebased onto its fix, 3b6b980). Regression: not run (the user's call). The reporter's 16 GB MacBook Air M4 and Hyrule Field are not tested.
@seedds

seedds commented Nov 8, 2026

Copy link
Copy Markdown
Owner

Closing this one. Thank you, @nafields, and @PromethOps and @dpadr for testing it. #1607, which came out of chasing this PR's faults, is on main.

The green area that two testers saw with the switch on (M5 Pro and M2 Air) means the rule "dead once other whole-attachment passes covered its range" marks some targets dead while the game still reads them. PromethOps' finding on #1453 fits that. The game keeps live targets in the part of the full-size scene target that its current resolution leaves unused, so a full-size pass over a range doesn't mean what was there is gone. Without a liveness rule that holds for that, a reclaim of live objects can't land.

The churn part of the problem is handled on main in 7f50222 (#1831), without judging anything dead. Each render target in a GPU-only pool gets a placement heap of its own. When the host frees the texture (the sharing table's takeover, after its last use completed), the heap stays resident and the next target of about the same size goes into it. That uses your rt_sizes finding (#1583): neighbouring resolutions share Metal's allocation sizes. On an M6, while the resolution moved, the residency set grew by 27-37 MB per 300 frames with it, against 455-521 MB without it.

What this PR aimed at beyond that, the live total (about 2 GB in the village), stays open in the game note. A new PR is welcome if a sound rule for when a target is dead turns up. It would build on the recycled heaps rather than a free list of its own.

@seedds seedds closed this Nov 8, 2026
Know-It-All-Brother pushed a commit to Know-It-All-Brother/nxnative that referenced this pull request Nov 9, 2026
… released texture on the pool tables' fast path (GPU page faults) (PR seedds#1607)

tex_by_id kept an ID's cached view when cur == v || v.parentTexture == cur. When the ID has no texture now, treg_tex returns nil, and a cached view that is the base texture has no parent either, so nil == nil kept it and stamped it valid; tex_fast_rid then gave the pool tables a released texture's resource ID. The view is kept only when cur is non-nil. Ocarina of Time with PR seedds#1588's reclaim: the faults at f3,677 and f4,851 are gone. An A/B walk on main reached the path zero times, with the same red lines and pace.

Co-authored-by: nafields <2613057+nafields@users.noreply.github.com>
Know-It-All-Brother pushed a commit to Know-It-All-Brother/nxnative that referenced this pull request Nov 9, 2026
…nd a freed target's heap waits resident for the next target it fits: Ocarina of Time's dynamic resolution, free to move since 141c8b4, made about 3 new Metal textures a frame in its 305 MB target pool; on an M6 (16 GB), alternating the switch every 300 frames while the resolution moved, the target site added 455-521 MB to the residency set per 300 frames off and 27-37 MB on, frame p90 33-50 ms against 17-18 ms (issue seedds#1831)

- nvn_mtl.m rh_texture: a COMPRESSIBLE texture in a CPU_NO_ACCESS pool (private storage; not display, video, linear or sparse; not a resolution family's heap) is made in a placement heap of its exact size. When the texture is freed (defer_drain, after its submissions completed) its heap goes to a free list, still resident; the next such target up to an eighth smaller than the heap is placed in it and filled as a new allocation is (tex_init_fill or zeros). No alias flush is added: the memory was the old texture's only up to its completed last use
- rh_trim, when a submission begins: heaps that waited 3 s, and the oldest past 256 MB waiting, leave the residency set
- The memory line counts the heaps made, reused, freed and waiting. DM_NVN_RT_RECYCLE=0 turns it off; ab alternates it with the DM_AB phases
- Why not share the pool's memory as on the console: a heap mirroring the pool (PR seedds#1453) and a reclaim of targets dead by overwrite (PR seedds#1588) corrupt the picture, as the game keeps live targets in the part of the full-size scene target its current resolution leaves unused, which only NVN's layout allows. The live targets' steady total (about 2.1 GB in the village) stays open
- t_rt_recycle (direct and deferred encoding, and with the switch off); t_image_alias's single-description check now asks that the texture is not in a family heap

Measured with the shaders of chain f9f01e4f9994 (on disk), on a host pinned to it. Village screenshots clean, no GPU errors or red lines. Unit tests: t_rt_recycle, t_image_alias, t_alias_takeover, t_upgrade_alias, t_cpu_tex and the gfx run pass (t_present_crop once rebased onto its fix, fd601de). Regression: not run (the user's call). The reporter's 16 GB MacBook Air M4 and Hyrule Field are not tested.
stan69b pushed a commit to stan69b/nxnative that referenced this pull request Nov 10, 2026
…ee switches that are not on main are no longer quoted as switches

docs/env_switches.tsv: DM_NVN_SUB_DRAIN (A-2's switch to restore the old wait at each submission's end)
gets its class and meaning.

The docs quoted three names that no code reads. Each mention now drops its backticks and says the
switch is not on main:
- DM_NVN_VC_WAYS: another fork's switch, in memory.md.
- DM_NVN_CUT_FIRST: a probe switch since removed, in Breath of the Wild's note.
- DM_NVN_RT_RECLAIM: PR seedds#1588's switch, in rendering.md, and so in the symptom index too
  (docs_symptoms.py).
stan69b pushed a commit to stan69b/nxnative that referenced this pull request Nov 10, 2026
…ee switches that are not on main are no longer quoted as switches

docs/env_switches.tsv: DM_NVN_SUB_DRAIN (A-2's switch to restore the old wait at each submission's end)
gets its class and meaning.

The docs quoted three names that no code reads. Each mention now drops its backticks and says the
switch is not on main:
- DM_NVN_VC_WAYS: another fork's switch, in memory.md.
- DM_NVN_CUT_FIRST: a probe switch since removed, in Breath of the Wild's note.
- DM_NVN_RT_RECLAIM: PR seedds#1588's switch, in rendering.md, and so in the symptom index too
  (docs_symptoms.py).
stan69b pushed a commit to stan69b/nxnative that referenced this pull request Nov 10, 2026
…ee switches that are not on main are no longer quoted as switches

docs/env_switches.tsv: DM_NVN_SUB_DRAIN (A-2's switch to restore the old wait at each submission's end)
gets its class and meaning.

The docs quoted three names that no code reads. Each mention now drops its backticks and says the
switch is not on main:
- DM_NVN_VC_WAYS: another fork's switch, in memory.md.
- DM_NVN_CUT_FIRST: a probe switch since removed, in Breath of the Wild's note.
- DM_NVN_RT_RECLAIM: PR seedds#1588's switch, in rendering.md, and so in the symptom index too
  (docs_symptoms.py).
stan69b pushed a commit to stan69b/nxnative that referenced this pull request Nov 10, 2026
…ee switches that are not on main are no longer quoted as switches

docs/env_switches.tsv: DM_NVN_SUB_DRAIN (A-2's switch to restore the old wait at each submission's end)
gets its class and meaning.

The docs quoted three names that no code reads. Each mention now drops its backticks and says the
switch is not on main:
- DM_NVN_VC_WAYS: another fork's switch, in memory.md.
- DM_NVN_CUT_FIRST: a probe switch since removed, in Breath of the Wild's note.
- DM_NVN_RT_RECLAIM: PR seedds#1588's switch, in rendering.md, and so in the symptom index too
  (docs_symptoms.py).
seedds added a commit that referenced this pull request Nov 12, 2026
…ain, and DM_NVN_VCONV_KEEP's row says what =1 gives: env_switches.py lists DM_NVN_COPY_ABSORB, DM_NVN_CUT_FIRST, DM_NVN_RT_RECLAIM and DM_NVN_VC_WAYS as quoted in the docs but read nowhere, asking that each mention say so. COPY_ABSORB's (nvn-objects.md, gone with pkg_copy_absorb) and VC_WAYS's (memory.md, the fork's texture view cache, not on main) did; BotW's note named DM_NVN_CUT_FIRST=ab, a probe of its frame-step investigation, and rendering.md's texture-ID entry named DM_NVN_RT_RECLAIM=1 from PR #1588, neither saying the switch was never on main. They now do (the advisory line keeps listing the names: it reads no prose). vk_min reads DM_NVN_VCONV_KEEP=1 as the default 4096, which the row now says. The table's check failure at e2df31c (no DM_NVN_VCONV_KEEP row) was fixed by ccd834e. Symptom index regenerated.

Checked, as the end of a review of the Python suite: every test_*.py in dm/tools/tests, dm/ios/tests and dm/gfx/tests in its own process with CI's settings (DM_GAME=010015100B514000, DMSH_TEST_XLATE the in-repo translator, the pinned venv): 187 files pass, 2,081 tests, 11 skipped in 6 files. The skipped ones run here: test_pso_archive, test_transform_feedback, test_dmsh_border and test_dmsh_geometry with their native fixtures (make in dm/gfx/tests) and test_dmsh_membar_fence with DMSH_TEST_METAL=1 pass; test_dmsh_meshstage with DMSH_TEST_GPU=1 fails in its GPU case (dmsh_meshstage has no attributes(), since ccd834e; CI does not set the flag), a separate fix. test_docs and test_env_switches after the edits; lint_python.py: 321 findings, all in the baseline. No game (docs only)
stan69b pushed a commit to stan69b/nxnative that referenced this pull request Nov 12, 2026
…ee switches that are not on main are no longer quoted as switches

docs/env_switches.tsv: DM_NVN_SUB_DRAIN (A-2's switch to restore the old wait at each submission's end)
gets its class and meaning.

The docs quoted three names that no code reads. Each mention now drops its backticks and says the
switch is not on main:
- DM_NVN_VC_WAYS: another fork's switch, in memory.md.
- DM_NVN_CUT_FIRST: a probe switch since removed, in Breath of the Wild's note.
- DM_NVN_RT_RECLAIM: PR seedds#1588's switch, in rendering.md, and so in the symptom index too
  (docs_symptoms.py).
stan69b pushed a commit to stan69b/nxnative that referenced this pull request Nov 12, 2026
…ee switches that are not on main are no longer quoted as switches

docs/env_switches.tsv: DM_NVN_SUB_DRAIN (A-2's switch to restore the old wait at each submission's end)
gets its class and meaning.

The docs quoted three names that no code reads. Each mention now drops its backticks and says the
switch is not on main:
- DM_NVN_VC_WAYS: another fork's switch, in memory.md.
- DM_NVN_CUT_FIRST: a probe switch since removed, in Breath of the Wild's note.
- DM_NVN_RT_RECLAIM: PR seedds#1588's switch, in rendering.md, and so in the symptom index too
  (docs_symptoms.py).
stan69b pushed a commit to stan69b/nxnative that referenced this pull request Nov 12, 2026
…ee switches that are not on main are no longer quoted as switches

docs/env_switches.tsv: DM_NVN_SUB_DRAIN (A-2's switch to restore the old wait at each submission's end)
gets its class and meaning.

The docs quoted three names that no code reads. Each mention now drops its backticks and says the
switch is not on main:
- DM_NVN_VC_WAYS: another fork's switch, in memory.md.
- DM_NVN_CUT_FIRST: a probe switch since removed, in Breath of the Wild's note.
- DM_NVN_RT_RECLAIM: PR seedds#1588's switch, in rendering.md, and so in the symptom index too
  (docs_symptoms.py).
stan69b pushed a commit to stan69b/nxnative that referenced this pull request Nov 12, 2026
…ee switches that are not on main are no longer quoted as switches

docs/env_switches.tsv: DM_NVN_SUB_DRAIN (A-2's switch to restore the old wait at each submission's end)
gets its class and meaning.

The docs quoted three names that no code reads. Each mention now drops its backticks and says the
switch is not on main:
- DM_NVN_VC_WAYS: another fork's switch, in memory.md.
- DM_NVN_CUT_FIRST: a probe switch since removed, in Breath of the Wild's note.
- DM_NVN_RT_RECLAIM: PR seedds#1588's switch, in rendering.md, and so in the symptom index too
  (docs_symptoms.py).
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 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.

4 participants