Repository navigation
vulkan: link graphics pipeline libraries optimized on AMD's Windows driver - #1
Closed
justin-wq1337 wants to merge 16 commits into
Closed
justin-wq1337 wants to merge 16 commits into
justin-wq1337 wants to merge 16 commits into
Conversation
…pute build logs With EDEN_NX2_FAST_FIRST=1 a pipeline built while the game runs is first created with VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT and used as soon as the driver returns it. The optimized build follows on a worker and replaces it at a later draw or dispatch. On a demanding Switch 2 test title the first-visit stutter is the driver compile (draws held up to 7 s of every 10 s with an empty driver cache); this trades it for a slower shader during that time. Compute pipelines built synchronously log their driver build time when it is over 100 ms (Nx2Perf compute_build), since a dispatch waits for the whole build. Off by default; nothing changes without the variable.
EDEN_NX2_HEAVY_SKIP=<N> skips the draws of a native pipeline with at
least N converted instructions over all stages while the driver builds
it, instead of holding every draw behind it ("1" picks 4000). On a
demanding Switch 2 test title every driver build over 500 ms was above
4000 instructions, and those made about 90% of the compile time.
A heavy draw is still held when skipping it could lose data for good:
the pipeline writes storage or uses transform feedback, or its render
target was not cleared this frame, or is a new small target (a baked
texture rather than a scene-sized image redrawn every frame). Clears
are reported to the pipeline cache for this (NoteClear). Held draws
build their own pipeline instead of queueing behind skipped builds.
Environments report their converted instruction count (CodeWords) so
the pipeline knows its size. Counters are logged every 10 s, and held
draws over 100 ms log the pipeline size, the reason they were held and
the render target size (Nx2Perf held_draw).
Off by default; nothing changes without the variable.
EDEN_NX2_COMPUTE_WARM=<file> loads compute programs prepared offline from the game's own shader archives (pipeline cache format, no recorded reads) and builds them on 4 low priority threads after the disk cache has loaded. The pipelines are thrown away; they only fill the driver's shader cache, so a dispatch that later converts to the same SPIR-V finds the driver build ready instead of waiting seconds for it. Programs already in the disk cache are left out. Reads a file record does not hold are answered with guesses (FileEnvironment::SetGuessMissing / VideoCommon::GuessMissing) instead of an error. With the defaults about 95% of the programs converted to the same SPIR-V as the runtime records on the test title. Tools: - shader-sweep --guess-missing [--guess-cbuf0, --guess-cbuf-other, --guess-texture-type, --guess-pixel-format, --guess-storage-pointer, --guess-storage-pointer-other] translates records with missing reads the same way, and accepts cache version 53. - shader-sweep --override-out FILE writes the lowering as a program override record and reports the program's native_key. - tools/nx2/make_compute_warm.py builds the warm-up file from a static shader dump (extract_static_shaders.py output). Off by default; nothing changes without the variable.
…GPL) With EDEN_NX2_GPL=1 and a device that supports VK_EXT_graphics_pipeline_library with fast linking, graphics pipelines are linked from per-stage libraries instead of being built whole. Without the variable the device is created exactly as before (the extension stays out of the extension tables). Each stage gets its own descriptor set (set index = stage index, independent sets, plain descriptor sets, Bindings::descriptor_set in the SPIR-V backend) with bindings from 0, and the fragment stage's rescaling bits start at fixed indices, so a stage's SPIR-V and library do not depend on the partner stage's resources. Pre-rasterization and fragment shader libraries are cached in PipelineCache, keyed by the emitted SPIR-V hash, the stage's set layout, the state the part bakes in, the dynamic state list and the render pass; a worker that needs a library another worker is building waits for it. Vertex input and fragment output libraries are built per pipeline, then fast linked. EDEN_NX2_GPL_OPTIMIZE=1 adds an optimized link on a worker, swapped in like the fast first build. A failed library or link falls back to a monolithic pipeline with the same sets. Task/mesh, transform feedback, static rasterizer discard and stages with too many textures or images for the split rescaling bits stay on the monolithic route. PipelineCache::WarmGraphicsLibraries builds a pipeline's shader libraries with no draw; with a single stage and no partner it builds only that stage's library. Nx2Perf gpl stats are logged every 10 s. Based in part on suyu-main 3157ae0ce0 and f80bbc701a (library cache built outside the lock, fast then optimized link, link statistics); unlike there, shader libraries are shared across partner stages.
EDEN_NX2_GRAPHICS_WARM=<file> (with graphics pipeline libraries on) builds the game's vertex and fragment programs as single-stage libraries in the background after the compute warm-up. The file is streamed (it can be over a gigabyte); worker threads take the next entry under the reader's lock. Libraries built by the warm-up are not kept (GraphicsPipelineLibraryCache::SetNoRetainOnThisThread), so only the driver's disk cache keeps them and a later pipeline with those stages costs a link. tools/nx2/make_graphics_warm.py writes the file from a static shader dump: one single-stage entry per unique vertex or fragment program, with draw state taken from the most common state in pipeline caches the game recorded (--runtime-cache). --validate compares the generated entries' SPIR-V with the runtime records. Off by default; nothing changes without the variable.
…tus bar progress The warm-up files no longer need the offline scripts. A game list entry "Prepare shaders (warm-up)" (with Rebuild and Delete) runs ShaderWarm::BuildWarmFiles in a progress window with Cancel; games do not start while it runs. The builder: - reads the game's RomFS through the VFS (.nup base and update packages, other formats through the loader); - extracts shader programs with C++ readers for the archive formats (zstd with the game's dictionaries, DCX, LZ4, SARC, BND4, BFSHA/BNSH, effect bundles, CGSB, SHN, BAHS v11), a port of the PR zortos293#214 extractor with the same checks; - lowers and converts them in process with the runtime code layout, on low priority threads in program order, and writes the per-title warm-compute.bin and warm-graphics.bin atomically, with the same records make_compute_warm.py and make_graphics_warm.py write. Draw state comes from the title's v53 pipeline caches when there are any, else from built-in defaults measured on one test title. At boot the warm-ups load the title's warm files when EDEN_NX2_COMPUTE_WARM / EDEN_NX2_GRAPHICS_WARM are unset. A warm-up that ran to the end writes a .done marker (file size and write time, entries, driver version, device ID, cache version); a matching marker skips that warm-up at the next boot. Rebuild and Delete remove the warm files, markers and temporary files. Progress goes through ShaderNotify (SetWarmProgress / GetWarmProgress, lock-free, 4 updates a second) to the status bar, next to the shader build count: compute and graphics counts, with a percent when the total is known. A "Shader libraries" setting (use_graphics_pipeline_library) in the advanced graphics tab turns on graphics pipeline libraries like EDEN_NX2_GPL=1. yuzu_cmd gets a hidden --build-warm <game> <title id> <output dir> [--defaults | caches] that runs the builder without booting the game (test hook).
…nditional, library params default argument)
…ent of fast-first compute, warm path encoding, builder exceptions, JSON summary, offline tool failures and defaults) - vk_graphics_pipeline: the GPU thread checks a has_pipeline flag the build sets before it completes, instead of reading pipeline while the scheduler thread swaps in the optimized one. - vk_compute_pipeline: a retired pipeline also waits for its pending optimized build. - vk_pipeline_cache: open the warm files and their markers through one path in its native encoding (environment overrides stay native narrow). - shader_warm_dialog: a builder exception is reported as a failed build instead of leaving the worker thread; deleting uses the shared DeleteWarmFiles (markers and temporary files too). - yuzu-cmd: escape the error text in the JSON summary. - make_compute_warm.py: a hung or unstartable sweep counts as a failed program. - make_graphics_warm.py: clear error when a stage has no runtime records. - shader-sweep: --guess-missing guesses no storage pointers in banks 1-17, as the emulator does.
…he right after a version bump The builder read only the current version's runtime cache. Just after a version bump (53 to 54) that cache does not exist yet, so it fell back to the built-in defaults; a first-visit walk on the test machine after that warm-up still compiled many libraries in game (0.5 to 0.8 s each). Read the newest cache of a version with the same record layout instead.
…, keep a warm file's marker until it is replaced
…river Pipelines fast-linked from graphics pipeline libraries draw white fringes around alpha blended effects on AMD's Windows driver (Ocarina of Time's clouds, particles and Navi in Kakariko, RX 7800 XT, driver 26.10.1). The same SPIR-V and per-stage descriptor sets built as a monolithic pipeline, or linked with link time optimization, draw correctly. Libraries there keep link time optimization info and every link is optimized; EDEN_NX2_GPL_FAST_LINK=0/1 overrides the choice on any driver. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
10 tasks done
PantACRO4life
force-pushed
the
perf/gpl-shader-warmup-v2
branch
from
November 7, 2026 21:36
42b6868 to
6c89ff1
Compare
Owner
|
Thanks! Taken into zortos293#579 as c196b4f (your authorship kept, title names removed from the commit message and one comment). Closing this one since the commit is in the PR branch now. |
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.
An AMD test of zortos293#579, and a fix for the one problem I found.
Problem
With "Shader libraries" on, Ocarina of Time draws dotted white fringes around alpha blended effects on AMD's Windows driver: the clouds, the particles in the air, Navi's glow and parts of a tree in Kakariko. A frame dump shows them already in the 1280x720 effects layer (pass 062, shaders
5bcce0f7d9cda422/cfba60f48bde7a1c/e8193481decd0a8e), before it is composited.The SPIR-V on the library route differs from the monolithic one only in the descriptor set decorations, so I compared four builds of the same scene:
FallBackToMonolithicforced)VK_PIPELINE_CREATE_LINK_TIME_OPTIMIZATION_BIT_EXTSo Lichii's per-stage sets, bindings and SPIR-V are fine; only the fast (unoptimized) link draws wrong. That points at the driver's fast-link path, though I can't rule out something the optimized link happens to tolerate.
EDEN_NX2_GPL_OPTIMIZE=1alone doesn't avoid it: pipelines loaded at boot have no build worker, so they keep their fast link, and runtime ones show the fast link until the optimized one is swapped in.Change
Device::CanFastLinkGraphicsLibraries(): false onVK_DRIVER_ID_AMD_PROPRIETARY, true elsewhere.EDEN_NX2_GPL_FAST_LINK=0/1overrides it on any driver (same pattern asCanUseUnbackedSparseImages).EDEN_NX2_GPL_OPTIMIZEis skipped there, since the first link is already optimized.Note for AMD users who already ran the warm-up: the libraries now carry the retain flag, so the earlier warm-up no longer matches. The warm-up marker doesn't include the library flags, so it should be rebuilt once ("Rebuild shader warm-up").
Cost
Optimized links are slower than fast links. On the first boot after the change, the ~6,900 pipelines from the disk cache took 320 ms per optimized link on average (max 5.9 s), since the driver had no optimized code cached yet. On the next boot, with the driver cache holding them, the same ~6,500 links took 74 ms on average (max 0.9 s). Links during play took 4 to 5 ms each, and draws held for unbuilt pipelines were 7 to 14 ms per 10 s window in Kakariko. That is still far below a full monolithic build of a new pipeline, but it isn't the near-free fast link.
Results on AMD
nx2d549411 + zortos293#243 + this PR on zortos293#579%LOCALAPPDATA%\AMD\VkCachegrew from 1.1 to 2.6 GBgraphicsPipelineLibraryandgraphicsPipelineLibraryFastLinkingUnrelated freeze during testing
Once, about 80 s into a session, the game froze for good: the same deadlock as zortos293#379 (GPUBack in the texture GC →
BackSparseWrite→MapPageGroupwaiting on the page table lock, a guest core waiting inHandleRasterizerDownload). It isn't caused by this change; I'm continuing with the higherEDEN_NX2_TC_*_MIBthresholds from that issue.Screenshots
Fast link (current zortos293#579): dotted white fringes around the clouds, Navi and particles.
The 1280x720 effects layer from the frame dump (passes 062 to 064), where the fringes already are:
With this change (optimized links), same spot:
AI disclosure: I tested in game; Claude Code (Opus 5.5) narrowed it down with the frame dump and the A/B builds, wrote the change and this description.
🤖 Generated with Claude Code