Repository navigation
Compute pipelines in the pipeline archives: compiled offline for every compute shader of the map, and captured into the device's archive when the bundle lacks one - #1307
Closed
lunks wants to merge 6 commits into
Conversation
…te shader of the shader map, which the host takes at the first dispatch: a provisional profile, whose prewarm list names none, no longer builds them all on the device A compute pipeline depends on its function alone: the host builds it from the function (newComputePipelineStateWithFunction, no threadgroup size or other option; the dispatch gives the threadgroup size), so it can be compiled at build time without a recorded play session. pso_offline.py now gives the archive a compute pipeline descriptor naming the function of each compute shader's <key>_b: the prewarm list's C rows first, then every row of stage compute in shader_map.tsv (--shader-map, --manifest, --no-compute), leaving out functions with function constants as the host specializes those. The compute pipelines compile in groups of their own (batches.<arch>/c<index>, 16 each, --jobs at a time, half the logical CPUs by default), each cached by its inputs and merged with the bases by metal-pack, so new pairs leave them compiled and the reverse. The host's cps_new looks <key>_b up in the bundled archive first (cps_bundle), at the first dispatch and at the prewarm's C lines, and builds it on a miss; the prewarm's last lines and the 'pipelines up to frame' line count both. Star Fox (0400E88025B1A000, provisional profile, chain ae59da13e481, M5 Pro, measured on a fork whose host also carries other changes): 233 compute pipelines beside 3 bases, a 35 MB archive, 268 s in 16 groups against 453 s as one metal-tt run (842 / 829 s of CPU, background priority, load averages 19-26 from other jobs); unchanged inputs reuse it in 0.3 s. Two 90 s headless boots to the title with it took all 7 of their compute pipelines from the archive and built none. Checked: test_pso_offline.py (new Compute class and group tests), test_docs.py, docs_symptoms.py --check; t_pso_bundle (compute pipelines from the archive; a shader with constants or outside the map built).
… the device's pipeline archive: Star Fox's second headless start took its 8 from it and built none A compute pipeline that missed the archive bundled with the app (no bundle, a shader outside the shader map, an OS update the bundle's binaries do not survive) was built through the MTLDevice, which no pipeline serializer sees, so pso_archive never held one and every start built it again, in full whenever the system's shader cache had dropped it. cps_new now looks such a pipeline up in the bundle, then in the device's own archive files; on a miss it builds it through the MTLDevice for the dispatch, as before, and compiles it again through the Metal 4 compiler into the current flush period on a serial utility queue (cps_harvest, arch_capture_cs), so the flush writes it. A period with compute pipelines is a plain file, never a p<seq> one, so the lookups skip the prebuild's files, and compaction captures a compute pipeline the prewarm takes from an old file again, as it does bases. Functions without a Metal 4 descriptor (constants, the embedded kernels) are neither looked up nor captured. The capture stays off the dispatch: a first version that built the dispatch's pipeline through it took 95 ms for Star Fox's first 5 compute pipelines, against 1.2 ms through the MTLDevice, which the system's shader cache serves. Headless boots of 90 s to the title without the bundle (M5 Pro, macOS 27.0, on a fork with other host changes), each series with its own archive and prewarm list: before, 5 built at the first start (1.2 ms) and 9 at the second (2.2 ms), none kept; after, 5 built at the first start (0.6 ms) and written by the flushes, then 8 from the device's archive at the second (1.4 ms of lookups) and none built; the same frame and the same [RED] lines. The log counts them (compute pipelines so far: N from the archive bundled with the app, M from this device's archive, K built, with the lookup and build times), also in the pipelines up to frame line. t_pso_archive checks the capture, the plain file, the second lookup, compaction and that a function without a descriptor is never looked up; t_pso_bundle that a shader outside the map is captured and one with constants is not.
… not "?": test_dmsh_abi counts "?" as the binding kind DMSH_KIND_UNKNOWN and failed on its third use in nvn_mtl.m
pso_offline.py: main's real_libraries (libraries of one file get a temporary folder for metal-tt) now also copies the compute pipelines' libraries, and the compute pipelines go through it in compile_batch and in main().
1 task done
nvn_mtl.m: arch_capture_cs (compute pipelines into the archive) and main's mapped read of .mtl4a files both stay. Checked: check_t_pso_archive (111 checks, 0 failed), check_t_pso_bundle (69), test_pso_offline.py, with GAME=01007EF00011E000.
nvn_mtl.m, t_pso_archive.m: main's specialization-descriptor ownership note (88c7e03) and this branch's compute-pipeline archive line and check both stay. Checked: check_t_pso_archive (123 checks, 0 failed), check_t_pso_bundle (69), with GAME=01007EF00011E000.
This was referenced Nov 6, 2026
seedds
added a commit
that referenced
this pull request
Nov 7, 2026
…y compute shader of the map, and captured into the device's archive when the bundle lacks one (PR #1307) pso_offline.py gives the bundled archive a compute pipeline for each compute shader (the prewarm list's C rows, then the shader map's compute rows; functions with constants left out). The host's cps_new looks a compute pipeline up in the bundle, then in the device's archive files. On a miss it builds through the MTLDevice for the dispatch, as before, and compiles it again into the flush period on a background queue, so the next start finds it. plan_compute now reads the libraries through romfs.Path, as plan does. With pathlib.Path, a one-file library (b-<chain>.img, what dmsh_build.py writes) left out every compute shader as having no reflection, and the archive held none (test_pipelines_from_a_library_image). Ocarina of Time (chain f9f01e4f9994, M6): 4,125 compute pipelines in a 480 MB archive. With it, all 103 of a route's compute pipelines came from the bundle and none were built. Without it, the first start wrote 88 into the device's archive and the second took 88 from it. PR #1522 measured the cold-cache gain: 195 s of submit-thread pipelines to 0.9 s. Return of the Obra Dinn quick regress: PASS (its route dispatches no compute). Co-authored-by: Pedro Nascimento <91118+lunks@users.noreply.github.com>
Owner
|
Landed as 30fb213, with you as co-author. One fix on top: Measured on Ocarina of Time (v131072, M6, warm Metal cache):
The cold-cache gain is #1522's measurement (195 s of submit-thread pipelines down to 0.9 s), cited in the docs. Return of the Obra Dinn quick regress: PASS. Its route dispatches no compute, so it only shows nothing else changed. |
gnosticgeek
pushed a commit
to gnosticgeek/nxforge
that referenced
this pull request
Nov 7, 2026
…merges clang-tidy found one more core.NullDereference in nvn_mtl.m (tex_cpu_content's first-use log line read t->pool->flags without a pool, seedds#1604), and one more nullability.NullablePassedToNonnull each in t_copy_alias.m (a kernel function looked up by name, seedds#1556) and t_pso_archive.m (the archive path from a boxed C string, seedds#1307). The log line prints 0 for a texture without a pool; the tests stop when the lookup fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016qteK6QbhFrzr2Vop9t34k
ksolenk38ze
added a commit
to ksolenk38ze/nxnative
that referenced
this pull request
Nov 7, 2026
…loading Merge adjacent GPU memory pools with contiguous guest virtual addresses and matching GPU address deltas into single gmem_map run entries (up to GMEM_RUN_MAX = 1,024 runs) backed by a reusable MTLBuffer ring (gmem_table_take / gmem_table_give), and reuse g_gmem_buf across encode_args_ calls within a submission since gmem_commit_patch updates the table at commit time. Also zero-fill unmapped virtual pool spans with PROT_READ | PROT_WRITE in vpool_map so late CPU reads do not fault. For MTL4Archive pipeline caches (related to PRs seedds#1307, seedds#1488, seedds#1493, seedds#1546), separate vertex-descriptor (vd) and pulling (non-vd) archive epochs so specialized vertex-descriptor pipelines are not written into the pulling archive set; guard against macOS 27's crash in MTL4ArchiveImpl::newArchiveReplyForKey when deserializing an archive entry with a zero-length function reflection string; preserve the active OP_CALL return stack when starting shader_prefetch_rest and pso_predict_rest_; and support falling back to the bundled shader chain directory when DMSH_CHAIN advances. Verified in The Legend of Zelda: Ocarina of Time (0400486024DEC000) on Apple M-series (macOS).
Know-It-All-Brother
pushed a commit
to Know-It-All-Brother/nxnative
that referenced
this pull request
Nov 9, 2026
…y compute shader of the map, and captured into the device's archive when the bundle lacks one (PR seedds#1307) pso_offline.py gives the bundled archive a compute pipeline for each compute shader (the prewarm list's C rows, then the shader map's compute rows; functions with constants left out). The host's cps_new looks a compute pipeline up in the bundle, then in the device's archive files. On a miss it builds through the MTLDevice for the dispatch, as before, and compiles it again into the flush period on a background queue, so the next start finds it. plan_compute now reads the libraries through romfs.Path, as plan does. With pathlib.Path, a one-file library (b-<chain>.img, what dmsh_build.py writes) left out every compute shader as having no reflection, and the archive held none (test_pipelines_from_a_library_image). Ocarina of Time (chain f9f01e4f9994, M6): 4,125 compute pipelines in a 480 MB archive. With it, all 103 of a route's compute pipelines came from the bundle and none were built. Without it, the first start wrote 88 into the device's archive and the second took 88 from it. PR seedds#1522 measured the cold-cache gain: 195 s of submit-thread pipelines to 0.9 s. Return of the Obra Dinn quick regress: PASS (its route dispatches no compute). Co-authored-by: Pedro Nascimento <91118+lunks@users.noreply.github.com>
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
Who needs it: Star Fox, whose note lists "a
pso_manifest.tsvhas not been recorded" open, and any game in bring-up without a recorded prewarm list. Their compute pipelines are built on the device at the first dispatch, at every start when the bundled archive lacks them. Star Fox has 233 compute shaders. No issue reports it.The host builds every compute pipeline on the device, through the
MTLDevice, at its first dispatch or at the prewarm'sCrows. Two consequences:archive.tsv:basesonly) and compiles them at their first dispatches.MTLDevicebuild goes through no pipeline serializer, sopso_archivenever holds one.A compute pipeline depends on its function alone (no threadgroup size or other option; the dispatch gives the threadgroup size), so it can be compiled without a play session.
pso_offline.pygives the archive a compute pipeline descriptor for each compute shader's<key>_b: the prewarm list'sCrows first, then everycomputerow ofshader_map.tsv(--shader-map,--manifest,--no-compute), leaving out functions with function constants (the host specializes those). They compile in groups of their own (batches.<arch>/c<index>, 16 each), cached by their inputs and merged with the bases bymetal-pack. The host's newcps_newlooks<key>_bup in the bundle first and builds it on a miss. The profile's tables are the default, so iOS archives (build_ipa.sh) get them too.cps_newlooks in the device's own archive files, and after building through theMTLDevice(for the dispatch, as before) compiles the pipeline again through the Metal 4 compiler into the current flush period on a serial utility queue (cps_harvest,arch_capture_cs), so the next start finds it. The capture stays off the dispatch: building the dispatch's pipeline through it took 95 ms for Star Fox's first 5, against 1.2 ms through theMTLDevice. A period with compute pipelines is a plain file, never ap<seq>one; compaction captures them again as it does bases.The log counts them (
compute pipelines so far: N from the archive bundled with the app, M from this device's archive, K built, also in thepipelines up to frameline). Reference entries: Compute pipelines compiled at first use and Compute pipelines outside the bundled archive built at every start.Checked
main056e41f):make -C dm/host/tests GAME=040074A01BF12000 runpasses.make -C dm/gfx/tests GAME=040074A01BF12000 runpasses (t_pso_bundle69 checks,t_pso_archive111 checks,t_pso_warm99 checks, all 0 failed).t_pso_bundlebuilds an archive with compute shaders: the map's come from it with nothing built; one with constants and one outside the map are built, and only the latter is captured.t_pso_archive(compute_pipelines): built, captured in the background, written to a plain file, taken from it without a build, captured again at compaction; a function without a descriptor is never looked up.lz4,zstd):test_pso_offline.py(newComputeclass and group tests),test_nsp_mac_app.py,test_nsp_incremental.py,test_nsp_nup.py,test_docs.py: 219 tests pass (the three NSP tests skip 32 cases without azstdmodule in the system Python and pass with the venv's: 164 tests).docs_symptoms.py --checkpasses.ae59da13e481), M5 Pro, macOS 27.0, on our fork with other host changes (among them HMMA/IMMA through TensorOps, whose<key>_btlibraries were kept out of the archive; that code is not in this PR):metal-ttrun (background priority, load averages 19-26 from other jobs); unchanged inputs reuse it in 0.3 s. Two 90 s headless boots to the title with it took all 7 of their compute pipelines from it and built none.[RED]lines.regress.py check: not run. It reaches every game with compute shaders (the app's archive grows by their pipelines, and the device's archive files by their captures); Bananza and Star Fox would be the first to check.lint_python.pyandlint_c.pynot run (ruff, pyright and the pinned clang-tidy are not installed here).Docs
docs/reference/shaders.mdentries, with lines in Star Fox's note, andpython3 dm/tools/docs_symptoms.pywas run (the symptom index);docs/development.mddescribes the compute pipelines in the archive.pso_offline.pyis not a chain tool).