Repository navigation
A staged upload from a large pool makes its second source buffer only when taking its staging began a new batch, so a scene load no longer puts its textures' storage into the residency set twice - #2378
Closed
bymaxmarc wants to merge 1 commit into
Conversation
… when taking its staging began a new batch, so a scene load no longer puts its textures' storage into the residency set twice Symptom: the first frame of a scene that draws hundreds of new packaged textures took a quarter to half a second, nearly all of it in submit+present (`[longframe] … texture uploads 855 first …, 908.5 MB`), and the `residency set up to frame` line of that window counted about twice as many shared buffers from up_big_src as packaged textures (2,866 and 1,433). Found on Nintendo Switch Sports Resort (04008E001F098000, Switch 2, v65536), the first frame of the court behind the sport menu. Cause: an upload from a pool no address was given out in, or from a large pool (BIG_MIN), reads the texture's storage through a zero-copy buffer of the upload batch (up_big_src), which the batch makes resident and retires. pkg_upload_ made that buffer, then took the texture's staging (up_alloc), which may flush the batch, and made a second buffer in case it had: for every texture, flush or not. Each staged upload put its storage into the residency set twice, and the second buffer was made, wired and committed for nothing. Fix: pkg_upload_ makes the second buffer only when up_alloc began a new batch (g_up_seq moved). Reference: memory.md, a new entry (Two source buffers for each staged upload from a large pool); symptom index regenerated. Checked: t_upload_src under Metal validation (eight textures in one batch make eight source buffers, sixteen before; three 64 MB ones whose staging splits the batches make one more for the texture that flushed its batch, six before; every texel against the CPU reference), the rest of the graphics tests (Apple M2 Max, macOS 27.2; t_feedback's 'with nothing running: a commit' check failed once in the full run and 1 of 3 runs alone, as it does on main, 2 of 4), test_docs. In the game, two 75 s runs each from the same save and pipeline state, before and after on the same work branch (M2 Max): residency commits to frame 2,400 678-692 → 520-523 ms, shared buffers added in that window 2,852-2,858 (3.0 GB) → 1,429-1,437 (1.6 GB), the court's first frame 263-270 → 243-251 ms, the next load's (340 textures, 188 MB) 155-157 → 134-138 ms. Not run: regress.py (every game's staged uploads from large pools take this path, and the only change is one buffer fewer when the batch did not change), lint_c.py tidy (no clang-tidy here), iOS.
bymaxmarc
force-pushed
the
pr/staged-upload-src
branch
from
November 12, 2026 11:08
19b8ab3 to
5b07ee6
Compare
seedds
added a commit
that referenced
this pull request
Nov 12, 2026
… buffer only when its batch changed (PR #2378) pkg_upload_ reads a texture in a large pool (BIG_MIN), or in a pool no address was given out in, through a zero-copy buffer of the upload batch (up_big_src). After taking the texture's staging (up_alloc), which may flush the batch, it made a second buffer for every texture, flush or not, so each staged upload put its storage into the residency set twice. It now records the batch (g_up_seq) when it makes the first buffer and makes a second only when the batch changed. - Switch Sports Resort's first court frame (855 textures, 908 MB, M2 Max, PR #2378): up_big_src buffers 2,852-2,858 -> 1,429-1,437, residency commits 678-692 -> 520-523 ms, the frame 263-270 -> 243-251 ms. - Mario Kart World, 200 s regress route (M4 Pro): up_big_src buffers 24,696 (5,429 MB) -> 12,082 (2,424 MB); 60.0 fps and frame p99 unchanged. - t_upload_src: one buffer per texture in one batch, one more for a texture whose staging flushed its batch, every texel checked under Metal validation; main's code fails 2 of its 41 checks. - regress.py check: Mario Kart World and OCTOPATH TRAVELER II pass (Octopath does not reach up_big_src). Same cause found earlier in PR #2342. Co-authored-by: bymaxmarc <354601562+bymaxmarc@users.noreply.github.com>
Owner
|
Landed on main as 0c23e0b, with one change: Checked on an M4 Pro: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
On Nintendo Switch Sports Resort (04008E001F098000, a Switch 2 game; no profile on main yet), the first frame of the court behind the sport menu draws 855 new packaged textures (908 MB). That frame took a quarter to half a second, nearly all of it in submit+present. The
residency set up to frameline for that window counted about twice as many shared buffers fromup_big_srcas packaged textures (2,866 and 1,433).An upload from a large pool (
BIG_MIN), or from a pool no address was given out in, reads the texture's storage through a zero-copy buffer of the upload batch (up_big_src). The batch makes that buffer resident and retires it.pkg_upload_made the buffer, then took the texture's staging (up_alloc), which may flush the batch. In case it had, it made a second buffer, and it did so for every texture, flush or not. So each staged upload put its storage into the residency set twice, and the second buffer was made, wired and committed for nothing.pkg_upload_now makes the second buffer only whenup_allocbegan a new batch (g_up_seqmoved).Reference: Two source buffers for each staged upload from a large pool (new); symptom index regenerated.
Checked
Tests, on this branch (main ccd834e + 1 commit), Apple M2 Max, macOS 27.2:
make -k -C dm/gfx/tests GAME=010015100B514000 -j8 run: every test passed exceptt_feedback, with one check (with nothing running: a commit) failing. That check is flaky on main too: run alone, it failed 1 of 3 times on this branch and 2 of 4 on main.t_upload_src: 41 checks, 0 failed.t_upload_srcruns under Metal validation:python -m unittest dm.tools.tests.test_docs dm.tools.tests.test_env_switches dm.tools.tests.test_pr_areas dm.tools.tests.test_lint_c: 30 tests OK.Game: Nintendo Switch Sports Resort 04008E001F098000 v65536 on the user's Mac (Apple M2 Max, macOS 27.2), on our work branch. I ran 75 s twice before and twice after this commit, from the same save and pipeline state:
Most of what is left is the residency set taking the new textures and their staging.
Not checked:
scripts/regress.py: not run. Every game's staged uploads from large pools take this path; the only difference is one buffer fewer when the batch did not change.lint_c.py tidy(no clang-tidy on this Mac) and iOS.Docs
docs/reference/<subsystem>.mdentry, andpython3 dm/tools/docs_symptoms.pywas run (the symptom index)