Large compute shaders that no cache holds are built on one background thread after the boot prebuild, smallest first, so their first dispatch no longer compiles them on the replay thread. NX2NAVI_BIG_BUILD_MAX_MB stops that builder once the process has grown by that much: on a 31 GB machine one of these shaders took the process up by 12.8 GB and the game stuttered while Windows paged. The builder takes the queue under its lock and copies each entry out before compiling it, so a shader queued meanwhile cannot free the arrays under it. The same change reports what pipeline-cache.bin gives back, and starts over when the file grows past NX2NAVI_PIPELINE_CACHE_MAX_MB. A tensor-core program is now tested before its shader is asked for, in both dispatch paths. It runs the stand-in while NX2NAVI_NATIVE_TENSOR is off, and translating it anyway recorded four AI-upscaler shaders of 0.87 to 4.4 million SPIR-V words that this game never dispatches natively. Process memory went from 29.3 GB to 24 GB and the multi-gigabyte builds during play are gone. The frame-time summary says which part a slow frame went to, and with NX2NAVI_REPLAY_TIME how the replay splits between draws, binds and transfers; NX2NAVI_SVC_SLOW_MS logs the kernel services that block, with the thread they blocked on. prof_report.py reads the map this build writes and adds the image base to a runtime Rip, so a profile resolves to function names instead of question marks. build_xlate.py applies the patches with GIT_CEILING_DIRECTORIES set to the build folder's parent. Inside a checkout git read 0021-prmt-byte-permute.patch's paths from the repository's top, skipped every file of it and still exited 0, so PRMT was missing from locally built translators and every byte permute was a no-op. A Skipped patch line now fails the build. The note in AmpereLowering.cs that said the translator emits nothing for PRMT is corrected.
8.3 KiB
Shader delivery: Advanced Shader Delivery and Vulkan equivalents
Research only, 2026-11-07. No code was changed or built. Question: can Microsoft's Advanced Shader Delivery (ASD) cut first-run shader compile cost here?
Verdict: no. ASD is a Direct3D 12 feature, fed by a store, and its inputs are game-derived. A Vulkan equivalent already exists in this codebase. The remaining gap is cheap to close locally.
1. What ASD is
- A "Windows 11 Direct3D 12 technology" with three stages: create a State Object Database (SODB), test it by compiling a Precompiled Shader Database (PSDB) and registering it locally, deploy the SODB to a storefront (overview).
- The SODB is a SQLite file holding serialized PSO and state-object descriptions plus their DXIL bytecode, recorded from
CreateGraphicsPipelineState,CreateStateObjectand similar calls (spec). Recording usesd3dconfig.execapture or a programmatic API; capture "adds significant overhead" (Agility SDK 1.618). - The store compiles the SODB with each GPU vendor's compiler plugin (
D3D12StateObjectCompiler) into a per-GPU PSDB and ships it with the download; Windows refreshes it after driver updates (intro). The D3D12 runtime finds it inside the game process by executable path through the registration API. A PSDB must match the adapter family and compiler ABI. - Requirements: Windows 11 24H2 (build 26100.1000+) and Agility SDK 1.619.5+ (adoption guide). Submission is through Xbox Partner Center with an application identity (GDC 2026).
- Hardware per the 21 Oct 2026 post: AMD RDNA 1-4, Intel Arc B-series and Core Ultra 2/3, NVIDIA RTX, Qualcomm Snapdragon X2, each with a minimum driver. Delivery is through the Microsoft Store and Xbox PC app; Steam and Epic are "coming soon", undated.
- No Microsoft page I read mentions Vulkan.
2. Can it apply here?
No. ASD needs D3D12. The SODB records D3D12 create calls and DXIL; the PSDB is looked up by the D3D12 runtime. A Vulkan process never touches either.
Current flow (from the top comments of runtime/gfx_shader.c and runtime/vk_pipeline.c):
- An NVN program (control section, code, c1, translator answers) goes to
nx2navi_xlate.exe --servechildren and comes back as SPIR-V plus reflection, cached in memory and undershader-cache/(packed intoshader-cache.pack). The key includes the translator identity, so any translator change invalidates every entry.in/<input id>.infiles allow retranslation without guest memory. - Play records
shader-answers.binandshader-programs.bin.pipeline-keys.binrecords every built pipeline, shaders named by input id, independent of the translator. - Pipelines use
VK_EXT_graphics_pipeline_library: stage libraries, a fast link on a miss, an optimised pipeline built on a background thread.pipeline-cache.binis dropped when vendor, device or cache UUID differ. - Boot (
vk_pipeline_prewarm) retranslates and rebuilds what the key file names. The installer runsnx2navi.exe --prepare-shaders, which warms the driver's disk cache. - A comment in
runtime/boot.crecords a cold start (new translator or driver): 2,132 shaders translated and 1,579 pipelines built in 9.8 s. The guest waits at mostNX2NAVI_PREWARM_WAIT_MS(default 3000), then the work continues in the background. - Compute shaders are translated and built synchronously on the replay thread, because "a skipped dispatch can change what the game computes". Graphics misses are asynchronous and the draw is skipped. So wrong lighting early in a cold start more plausibly comes from skipped graphics draws than from compute. I did not reproduce or measure it.
A D3D12 back end would replace about 25,800 lines (28 vk_*.c files plus headers) using push descriptors, mesh shaders, pipeline libraries and dynamic state 3, and the translator would have to emit DXIL, not SPIR-V. My estimate is several person-months. It would still not reach ASD, because delivery needs a store submission (section 3). The local "compile a SODB, register the PSDB" test path runs the same vendor compiler on the player's machine, so it gains nothing over the existing prepare pass.
3. Content boundary
AGENTS.md forbids anything derived from game data in the repository. Shaders, SPIR-V, DXIL, SODBs, PSDBs and pipeline-cache.bin are all derived from game code. check_content.py also blocks .pack files.
- Ruled out: shipping or uploading any shader database, SODB, PSDB, SPIR-V or driver cache. ASD's deploy stage is exactly an upload to a store.
- Still possible: anything built on the player's machine from the player's own files. The installer's prepare step does this today, and everything stays in ignored folders.
- Existing precedent: the installer bundles a variant list (shader hash plus translator answers, no code), outside the repository. Keep any new shared data to that level. Whether a hash counts as "derived" is the owner's call.
4. Vulkan-side options
| Option | State here | Verdict |
|---|---|---|
VK_EXT_graphics_pipeline_library |
In use (vk_device.c, vk_pipeline.c, NX2NAVI_GPL) |
Done |
| Driver disk cache | Used; a library built again on AMD takes 0.1 ms instead of 55 ms (vk_pipeline.c) |
Done |
| Fossilize-style capture | pipeline-keys.bin, in/*.in and the answer files play that role, with the translator in front |
Done. Fossilize replays on any device. Steam pre-caching needs Steam and uploaded data: unusable |
VK_KHR_pipeline_binary |
Not used | Per-pipeline blobs with your own eviction, still per-driver. 52.8% of Windows devices expose it (database); Mesa has it (RADV, ANV). Per-vendor Windows support unconfirmed. Optional |
VK_EXT_shader_object |
Not used | Skip: replaces pipelines, conflicts with the library design, binaries are device-bound |
| First-run "prepare shaders" pass | Installer runs it; boot prewarm capped at 3 s | The gap to close |
After a translator change the dominant cost is likely translation (CPU, translator children), which neither pipeline binaries nor ASD-style delivery can speed up. The driver compile only repeats for shaders whose SPIR-V changed. Measure the split first; the prewarm logs report both counts.
5. Recommendation
ASD is not feasible: D3D12-only, store-gated, built from game-derived data. The practical steps:
- Blocking "Preparing shaders" step on a cold cache (1-3 days). When the translator identity, GPU or driver changes, run the existing prewarm to completion before the guest starts (the
NX2NAVI_PREWARM_WAIT_MS=0behaviour), with progress shown. Theboot.cfigure is about 10 s, once. This removes the wrong-looking start. - Re-run the library prepare pass after a driver change (2-4 days).
lb_warm_preparedand--warm-shadersexist, and the skip list already keys on vendor:device:driverVersion. Triggering the pass whencache_loaddrops a stale cache is the local counterpart of ASD's driver-update handling. I found no such trigger inruntime/; confirm before building. - Measure, then decide on
VK_KHR_pipeline_binary(1-2 weeks, low priority). Prototype it only if the measurement points at the pipeline or driver cache. It could let libraries persist without the roughly 240 MB cache growthvk_pipeline.cavoids today.