Repository navigation
shader_warm: follow-ups to #579 (warm-up on the loading screen, link mode in the marker, save the driver cache after a warm-up) - #741
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
The warm-up threads only stopped when the pipeline cache was destroyed, late in shutdown. Until then they kept compiling (16 threads in one test), the emulation thread did not stop in time and was terminated. They now also end, after their current build, when the loading stop token fires.
…river Pipelines fast-linked from graphics pipeline libraries draw white fringes around alpha blended effects on AMD's Windows driver (clouds, particles and glows in a test title, 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>
…N_NX2_COMPUTE_NOINLINE) The multi-second whole-process freezes in a test title (one dungeon, 1.4-5.6 s) are the first dispatch of a new compute pipeline waiting for NVIDIA's driver compile. Configure records RunBuild + a condvar wait on the scheduler thread, so the scheduler, the GPU thread behind it and the guest behind its present fence all stop until vkCreateComputePipelines returns. A native program's global memory functions (DefineGlobalMemoryFunctions: the address tested against every storage buffer, ~300-870 instructions each) are called from dozens of places, and NVIDIA inlines every call: single shaders took 2-13 s to build. Mark functions of 128+ instructions called from 2+ places DontInline when the compute module is built (the translation cache keeps the SPIR-V as emitted, so cached and fresh stages build alike and the cache key is unchanged). EDEN_NX2_COMPUTE_NOINLINE=0 builds the SPIR-V as emitted. Also: "Nx2Perf compute driver build" (>100 ms, with CPU time and thread) and "Nx2Perf compute first dispatch ... waited" (>20 ms) log lines, and EDEN_NX2_COMPUTE_SPIRV_DUMP=<dir> to dump compute SPIR-V for offline timing. RTX 3080 Ti, driver 615.78.08: - Offline, all 618 compute shaders of a scene-tour translation cache, NVIDIA disk cache off: the 92 with marked functions 246 s -> 72.5 s of compile, shaders over 1 s 78 -> 20 (the other 526 took 11.7 s and none over 1 s). VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT does not shorten NVIDIA's compile; running spirv-opt first makes it longer. - Harness, cold pipeline + NVIDIA caches, open field -> town -> dungeon, interleaved: first-dispatch waits off 51.3 / 51.6 s total (max 5.27 / 5.51 s, 18 / 19 over 1 s), on 22.1 / 24.1 s (max 3.14 / 4.04 s, 7 / 6 over 1 s); log silences over 1 s 47 / 48 -> 35 / 35. - Second session (warm caches) has no compute waits in either mode: the freeze is paid once per new compute shader per driver cache. Ported to this branch: the translation cache hit path and the "Nx2Perf compute driver build" log are left out (this branch has no translation cache, and already logs compute driver builds through MakeComputePipeline and the build trace). Every compute module, including the fast-first and compute warm-up builds, goes through ComputeModuleCode. (cherry picked from commit 8bd8342) (cherry picked from commit 2fa4ecb5f048df579f58b98db095f0707875bcc3)
…X2_GRAPHICS_NOINLINE) Graphics stages of native programs carry the same global memory functions as compute (DefineGlobalMemoryFunctions), called from many places and inlined at each one by the driver. Run the same DontInline pass on graphics SPIR-V in EmitShaderModule, where every graphics module is made: monolithic pipelines, pipeline libraries, deferred worker emission and the graphics warm-up. EDEN_NX2_GRAPHICS_NOINLINE=0 builds the SPIR-V as emitted. The pipeline library key (spirv_hashes) is now the hash of the words handed to the driver, after the pass, so a library built by the warm-up and one needed in game share a key only when their modules are identical. The device SaveShader copy and the log dumps stay the emitted SPIR-V, as for compute. Test: vertex and fragment entry points are marked like compute. (cherry picked from commit e4e4fbea121b7e59584539e671a0ca57ee80a1c8)
The compute and graphics warm-ups each ran on a fixed 4 threads. EDEN_NX2_WARM_THREADS=N (1 to 32) sets the count for both; anything else keeps 4, with a warning. The threads stay at low priority, and the existing "on N low priority threads" log lines show the count used. (cherry picked from commit cfb80df71b445613c36a0d60ccb708ada6828d40)
… and the warm-up thread count
…starts its own LoadDiskResources can run again in the same pipeline cache (another title or program). The earlier load's warm-up threads were neither stopped nor joined, so StartComputeWarm and StartGraphicsWarm returned early and never warmed the new title, and clearing warm_cancel let old threads keep working on the previous title's file. A later load now cancels and joins them, drops the old stop callback and resets the compute-done flag first.
…ode, driver failures keep a graphics warm-up pending, heavy skip needs full clears, no warm-up deletion while preparing) - The warm-up marker also records whether shared shader functions are built as calls (compute and graphics); after that setting changes, the driver compiled different SPIR-V, so the warm-up runs again. Markers without the field no longer match. - Graphics warm-up entries now end as built, not buildable (a guess the translation rejects) or refused by the driver. A warm-up with driver failures writes no marker, so the next start tries again. - A library-only build whose library the driver did not make reports a failed build instead of returning as if built. - Heavy skip: a color clear counts only when it clears every channel, and a target counts as rebuilt only when each of its attachments was cleared. - Deleting a game's warm-up while it is being prepared is refused.
…hole attachment and every depth/stencil aspect; the deletion guard applies to the title being prepared) - A clear marks a target rebuilt only when it covers the attachment's own size and layers (scaled like the framebuffer), not just the framebuffer, which can be smaller than an atlas or array. Pitch-linear color targets never count. - A combined depth/stencil target counts only when depth is cleared and stencil is cleared with every bit written. - Deleting a game's warm-up is refused only while that same game's warm-up is being prepared.
…ame starts A pending shader warm-up can now run on the loading screen, as a new "Preparing shaders x / y" stage after the cached pipelines, on every CPU thread at normal priority; the game starts once it is done. Before, it only ran in the background at low priority while the game played, with more stutter until it ended and a game that felt slow. New setting "Prepare shaders before the game starts" (Ask, Always, Never; Configure, Graphics, Advanced). With Ask the desktop app asks at launch when the title has a warm-up that never finished, with "Don't ask again" keeping the answer as the setting. Never keeps the background warm-up. Where nothing asks (command line, Android) Ask acts as Never. Closing the game during the stage cancels the warm-up; it runs again at the next launch. The new LoadCallbackStage::Warm is last, so the other stages keep their values; Android shows it as shader building.
…e of EDEN_NX2_GPL_FAST_LINK, EDEN_NX2_GPL_OPTIMIZE or the driver's fast link support runs the warm-up again
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. |
|
Thanks for picking up the link-mode marker. Happy to test this on AMD (RX 7800 XT, Windows) once it leaves draft: the loading-screen stage, cancelling midway, and whether the marker reruns the warm-up when One more AMD-specific idea for later, not for this PR: since every link there is an optimized one (20-190 ms each, and the driver cache doesn't shorten it; see my numbers on #579), the warm-up could also pre-link the pipelines of the used list on the warm-up threads, so the first draw in an area doesn't pay for it. The remaining cost for me is now exactly that: on entering Castle Town 173 links in 10 s with every library a cache hit. Whether the render pass is available at that point you know better. |
|
@justin-wq1337 no need to wait for it to leave draft. It's a draft only because #579 isn't merged yet and it hasn't been tested in game. You can build it now from my fork, branch Pre-linking on AMD: agreed, that's the remaining cost there. The catch is the render pass: the warm-up keys carry no render targets today (jarynclouatre's finding on #579). The pipelines in the play history do record theirs, so pre-linking those becomes possible once the warm-up reads that state. Noted for after this one. |
Co-authored-by: gamehhh7 <gamehhh7@users.noreply.github.com> # Conflicts: # src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
* vulkan: compute warm-up from the game files (EDEN_NX2_COMPUTE_WARM) 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. * yuzu: in-app shader warm-up builder, Shader libraries setting and status 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 #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). Ported for the shader precache option (#579) without the Shader libraries setting, the graphics warm-up start and the pipeline library device setup. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * shader_warm: write and read warm files with the current pipeline cache version * vulkan, shader_warm, tools: review fixes (pipeline swap race, retirement 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. Ported for the shader precache option (#579) without the fast first build, pipeline swap and graphics warm-up parts. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * tools/nx2: write warm files with cache version 54 by default * docs: describe the functions added by the shader warm-up and pipeline library work Ported for the shader precache option (#579): only the comments on the shader warm-up code. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * shader_warm: take the draw state guesses from an older compatible cache 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. * shader_warm: record the warm files' entry counts so the warm-up shows exact progress Ported for the shader precache option (#579) without the graphics warm-up part. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * vulkan, shader_warm: note only whole-target clears for the heavy skip, keep a warm file's marker until it is replaced Ported for the shader precache option (#579): only the warm file marker part. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * vulkan: stop the shader warm-ups as soon as emulation is asked to stop The warm-up threads only stopped when the pipeline cache was destroyed, late in shutdown. Until then they kept compiling (16 threads in one test), the emulation thread did not stop in time and was terminated. They now also end, after their current build, when the loading stop token fires. Ported for the shader precache option (#579) without the graphics warm-up part. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * vulkan: stop the previous load's shader warm-ups before a later load starts its own LoadDiskResources can run again in the same pipeline cache (another title or program). The earlier load's warm-up threads were neither stopped nor joined, so StartComputeWarm and StartGraphicsWarm returned early and never warmed the new title, and clearing warm_cancel let old threads keep working on the previous title's file. A later load now cancels and joins them, drops the old stop callback and resets the compute-done flag first. * vulkan, yuzu: review fixes (warm-up marker records the shader calls mode, driver failures keep a graphics warm-up pending, heavy skip needs full clears, no warm-up deletion while preparing) - The warm-up marker also records whether shared shader functions are built as calls (compute and graphics); after that setting changes, the driver compiled different SPIR-V, so the warm-up runs again. Markers without the field no longer match. - Graphics warm-up entries now end as built, not buildable (a guess the translation rejects) or refused by the driver. A warm-up with driver failures writes no marker, so the next start tries again. - A library-only build whose library the driver did not make reports a failed build instead of returning as if built. - Heavy skip: a color clear counts only when it clears every channel, and a target counts as rebuilt only when each of its attachments was cleared. - Deleting a game's warm-up while it is being prepared is refused. Ported for the shader precache option (#579): only the deletion guard. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * vulkan, yuzu: review fixes (heavy skip counts a clear only over the whole attachment and every depth/stencil aspect; the deletion guard applies to the title being prepared) - A clear marks a target rebuilt only when it covers the attachment's own size and layers (scaled like the framebuffer), not just the framebuffer, which can be smaller than an atlas or array. Pitch-linear color targets never count. - A combined depth/stencil target counts only when depth is cleared and stencil is cleared with every bit written. - Deleting a game's warm-up is refused only while that same game's warm-up is being prepared. Ported for the shader precache option (#579): only the deletion guard. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * shader_warm, yuzu: prepare shaders on the loading screen before the game starts A pending shader warm-up can now run on the loading screen, as a new "Preparing shaders x / y" stage after the cached pipelines, on every CPU thread at normal priority; the game starts once it is done. Before, it only ran in the background at low priority while the game played, with more stutter until it ended and a game that felt slow. New setting "Prepare shaders before the game starts" (Ask, Always, Never; Configure, Graphics, Advanced). With Ask the desktop app asks at launch when the title has a warm-up that never finished, with "Don't ask again" keeping the answer as the setting. Never keeps the background warm-up. Where nothing asks (command line, Android) Ask acts as Never. Closing the game during the stage cancels the warm-up; it runs again at the next launch. The new LoadCallbackStage::Warm is last, so the other stages keep their values; Android shows it as shader building. Ported for the shader precache option (#579/#741) without the graphics warm-up, the shader calls and warm-up thread settings and EDEN_NX2_WARM_THREADS: before the game starts the compute warm-up uses every CPU thread, else 4. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * vulkan: save the driver pipeline cache when a warm-up stage finishes The warm-ups' builds go into the driver pipeline cache, but the timed flush counts only pipelines built during play, so a finished warm-up reached the file only at the next boot, game change or clean close, and was lost when the app ended otherwise (reported on Linux/RADV by IorekByr, where the cache lives in that file). Ported for the shader precache option (#741) without the graphics warm-up part. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * shader precache: compute-only "Precache shaders before game start" option Turns the ported warm-up into one setting, precache_shaders (Off, Ask, Always; default Off; global or per game, Graphics > Advanced). With it on, the compute programs found in the game files are built on every CPU thread on the loading screen before the game starts, so the driver's shader cache already holds them when the game first dispatches them. The loading screen shows "Precaching shaders N / M" and has a Skip button; a finished precache is remembered per file, GPU driver, device and cache version and is not run again. Ask asks at launch while the game has no finished precache. Ask and Always build the precache file from the game files first when it is missing (the existing dialog); the game list keeps Build/Rebuild/Delete Shader Precache. Compared with #579/#741 this drops the graphics warm-up and pipeline libraries (GPL), the DontInline/shader-call setting, fast-first, heavy skip, the background warm-up during play, the status bar warm-up label and every EDEN_NX2_* switch: the setting is the only switch. Adds unit tests for the setting, the launch requests, the marker and count files and the compute entry format. Co-authored-by: PantACRO4life <6643071+PantACRO4life@users.noreply.github.com> * Precache shaders before game start (compute, from #579/#741) * Remove a stray install log * shader precache: review fixes Skip and stop cancel every precache thread before joining them; a title precached before goes back to the renderer's driver and file check (Ask included); no precache without the disk shader cache, which is where it runs; precache entries keep the texture bank the program names, like InitializeNativeCode. * shader precache: precache the pipeline cache's skipped pipelines; no error for games without readable shaders Kirby Air Riders keeps its assets in its own packs (assets.cat and assets_a000.dat) and Mario Tennis Fever in one archive, so the builder found no shader archives and failed. It now writes an empty precache file and the dialog says the game's shaders can't be read from its files. The precache also builds the cached graphics pipelines the boot leaves for later (not used by recent sessions, or past the cap without a used list), then throws them away so only the driver's cache keeps them. The ones built are listed per driver in precache-graphics.bin, so later launches build only new ones. Their captured environments are held up to 1 GiB per boot; the rest wait for a later launch. --------- Co-authored-by: PantACRO4life <6643071+PantACRO4life@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…s293#741) (zortos293#781) * vulkan: compute warm-up from the game files (EDEN_NX2_COMPUTE_WARM) 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. * yuzu: in-app shader warm-up builder, Shader libraries setting and status 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). Ported for the shader precache option (zortos293#579) without the Shader libraries setting, the graphics warm-up start and the pipeline library device setup. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * shader_warm: write and read warm files with the current pipeline cache version * vulkan, shader_warm, tools: review fixes (pipeline swap race, retirement 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. Ported for the shader precache option (zortos293#579) without the fast first build, pipeline swap and graphics warm-up parts. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * tools/nx2: write warm files with cache version 54 by default * docs: describe the functions added by the shader warm-up and pipeline library work Ported for the shader precache option (zortos293#579): only the comments on the shader warm-up code. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * shader_warm: take the draw state guesses from an older compatible cache 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. * shader_warm: record the warm files' entry counts so the warm-up shows exact progress Ported for the shader precache option (zortos293#579) without the graphics warm-up part. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * vulkan, shader_warm: note only whole-target clears for the heavy skip, keep a warm file's marker until it is replaced Ported for the shader precache option (zortos293#579): only the warm file marker part. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * vulkan: stop the shader warm-ups as soon as emulation is asked to stop The warm-up threads only stopped when the pipeline cache was destroyed, late in shutdown. Until then they kept compiling (16 threads in one test), the emulation thread did not stop in time and was terminated. They now also end, after their current build, when the loading stop token fires. Ported for the shader precache option (zortos293#579) without the graphics warm-up part. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * vulkan: stop the previous load's shader warm-ups before a later load starts its own LoadDiskResources can run again in the same pipeline cache (another title or program). The earlier load's warm-up threads were neither stopped nor joined, so StartComputeWarm and StartGraphicsWarm returned early and never warmed the new title, and clearing warm_cancel let old threads keep working on the previous title's file. A later load now cancels and joins them, drops the old stop callback and resets the compute-done flag first. * vulkan, yuzu: review fixes (warm-up marker records the shader calls mode, driver failures keep a graphics warm-up pending, heavy skip needs full clears, no warm-up deletion while preparing) - The warm-up marker also records whether shared shader functions are built as calls (compute and graphics); after that setting changes, the driver compiled different SPIR-V, so the warm-up runs again. Markers without the field no longer match. - Graphics warm-up entries now end as built, not buildable (a guess the translation rejects) or refused by the driver. A warm-up with driver failures writes no marker, so the next start tries again. - A library-only build whose library the driver did not make reports a failed build instead of returning as if built. - Heavy skip: a color clear counts only when it clears every channel, and a target counts as rebuilt only when each of its attachments was cleared. - Deleting a game's warm-up while it is being prepared is refused. Ported for the shader precache option (zortos293#579): only the deletion guard. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * vulkan, yuzu: review fixes (heavy skip counts a clear only over the whole attachment and every depth/stencil aspect; the deletion guard applies to the title being prepared) - A clear marks a target rebuilt only when it covers the attachment's own size and layers (scaled like the framebuffer), not just the framebuffer, which can be smaller than an atlas or array. Pitch-linear color targets never count. - A combined depth/stencil target counts only when depth is cleared and stencil is cleared with every bit written. - Deleting a game's warm-up is refused only while that same game's warm-up is being prepared. Ported for the shader precache option (zortos293#579): only the deletion guard. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * shader_warm, yuzu: prepare shaders on the loading screen before the game starts A pending shader warm-up can now run on the loading screen, as a new "Preparing shaders x / y" stage after the cached pipelines, on every CPU thread at normal priority; the game starts once it is done. Before, it only ran in the background at low priority while the game played, with more stutter until it ended and a game that felt slow. New setting "Prepare shaders before the game starts" (Ask, Always, Never; Configure, Graphics, Advanced). With Ask the desktop app asks at launch when the title has a warm-up that never finished, with "Don't ask again" keeping the answer as the setting. Never keeps the background warm-up. Where nothing asks (command line, Android) Ask acts as Never. Closing the game during the stage cancels the warm-up; it runs again at the next launch. The new LoadCallbackStage::Warm is last, so the other stages keep their values; Android shows it as shader building. Ported for the shader precache option (zortos293#579/zortos293#741) without the graphics warm-up, the shader calls and warm-up thread settings and EDEN_NX2_WARM_THREADS: before the game starts the compute warm-up uses every CPU thread, else 4. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * vulkan: save the driver pipeline cache when a warm-up stage finishes The warm-ups' builds go into the driver pipeline cache, but the timed flush counts only pipelines built during play, so a finished warm-up reached the file only at the next boot, game change or clean close, and was lost when the app ended otherwise (reported on Linux/RADV by IorekByr, where the cache lives in that file). Ported for the shader precache option (zortos293#741) without the graphics warm-up part. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * shader precache: compute-only "Precache shaders before game start" option Turns the ported warm-up into one setting, precache_shaders (Off, Ask, Always; default Off; global or per game, Graphics > Advanced). With it on, the compute programs found in the game files are built on every CPU thread on the loading screen before the game starts, so the driver's shader cache already holds them when the game first dispatches them. The loading screen shows "Precaching shaders N / M" and has a Skip button; a finished precache is remembered per file, GPU driver, device and cache version and is not run again. Ask asks at launch while the game has no finished precache. Ask and Always build the precache file from the game files first when it is missing (the existing dialog); the game list keeps Build/Rebuild/Delete Shader Precache. Compared with zortos293#579/zortos293#741 this drops the graphics warm-up and pipeline libraries (GPL), the DontInline/shader-call setting, fast-first, heavy skip, the background warm-up during play, the status bar warm-up label and every EDEN_NX2_* switch: the setting is the only switch. Adds unit tests for the setting, the launch requests, the marker and count files and the compute entry format. Co-authored-by: PantACRO4life <6643071+PantACRO4life@users.noreply.github.com> * Precache shaders before game start (compute, from zortos293#579/zortos293#741) * Remove a stray install log * shader precache: review fixes Skip and stop cancel every precache thread before joining them; a title precached before goes back to the renderer's driver and file check (Ask included); no precache without the disk shader cache, which is where it runs; precache entries keep the texture bank the program names, like InitializeNativeCode. * shader precache: precache the pipeline cache's skipped pipelines; no error for games without readable shaders Kirby Air Riders keeps its assets in its own packs (assets.cat and assets_a000.dat) and Mario Tennis Fever in one archive, so the builder found no shader archives and failed. It now writes an empty precache file and the dialog says the game's shaders can't be read from its files. The precache also builds the cached graphics pipelines the boot leaves for later (not used by recent sessions, or past the cap without a used list), then throws them away so only the driver's cache keeps them. The ones built are listed per driver in precache-graphics.bin, so later launches build only new ones. Their captured environments are held up to 1 GiB per boot; the rest wait for a later launch. --------- Co-authored-by: PantACRO4life <6643071+PantACRO4life@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> (cherry picked from commit 44ac3b7)
nx2 now has the compute part of this work through zortos293#781 (shader precache on the loading screen, Off by default). Resolved onto that: - The graphics warm-up runs inside the precache stage, after the cached pipelines and the compute programs, on the same threads, with the same progress, Skip and stop handling (PrecacheGraphicsWarm). It needs Shader libraries and precache set to Ask (with yes) or Always. - The separate background warm-up, its status bar label and the EDEN_NX2_COMPUTE_WARM / EDEN_NX2_GRAPHICS_WARM overrides are gone, as in nx2. - "Shader warm-up threads" sets the precache thread count: 0 (default) uses every CPU thread as nx2 does; EDEN_NX2_WARM_THREADS still overrides it. - nx2's markers, Ask and Skip requests, precache-graphics.bin and archive discovery (zortos293#812) are kept; the markers also record the shader calls mode, so a marker written by nx2 runs the precache once more. - warm_records keeps the graphics records and the render target rule for the draw state template.
nx2's precache (zortos293#781) now covers the loading-screen warm-up and the driver cache save; this keeps only the link mode in the graphics warm-up marker.
|
Thanks @PantACRO4life, and everyone who worked on #579 and #741! I tested them on Windows with an AMD RX 7600 XT (RDNA3), an i7-8700, 16 GB of RAM and Adrenalin 26.10.1, using a local MSVC build of #717 + #741 merged together.
Also, thanks to Archi, Tekka and the TuxHub community for their help and for the idea of preloading the shaders. |
Conflicts, both from the split back thread draw work (zortos293#858): - vk_graphics_pipeline.h: kept this branch's members (source words, storage writes, held draw state) and nx2's fast draw members (TakesDeltaDraws, ConfigureDelta, CaptureNextDelta) side by side. - vk_graphics_pipeline.cpp: took nx2's ConfigureDelta and kept this branch's ConfigureDraw comment. ConfigureDelta checks has_pipeline instead of pipeline, as ConfigureImpl does here, since the scheduler thread may swap pipeline (fast first build, optimized link). Delta draws still end in ConfigureDraw, so the held and skipped draw handling of unbuilt pipelines applies to them too. The precache compute memory release (zortos293#878) merged cleanly and runs after this branch's compute module wrapper.
No conflicts; this branch still adds only the link mode to the graphics warm-up marker.
|
@MiguelRogelio thanks for the thorough test, and good to see it combines with #717.
Both #579 and this PR are now merged with the latest nx2 (3153d96). The only conflicts were with #858 in |
…NX2_GPL_OPTIMIZE_THREADS, default 1), dropped when the pipeline goes first
…e ownership Finish the zortos293#656/zortos293#761/zortos293#579 shader stack without replacing zortos293#1021 recorded recipes. Add shared-function SPIR-V controls on both prepared and translated paths, optional fast-first builds, layout-correct motion bindings, cancellation/reload ownership, and driver-persistent mesh warm markers. Preserve compute retirement and bounded prebuild capture budgets. Keep new performance timing diagnostics opt-in. Sources: zortos293#579 6724498; zortos293#741/zortos293#579 7869334 and later graphics controls; zortos293#761 ae537ce reload, warm persistence and conversion review fixes. Validation: targeted Windows clang-cl syntax checks; standalone SPIR-V inline control tests pass (5 cases, 25 assertions). Full linking and gameplay validation remain the parent integration's responsibility. The frontend syntax check needs the main build's generated ui_main.h, which is not present yet. Co-authored-by: PantACRO4life <6643071+PantACRO4life@users.noreply.github.com> Co-authored-by: DJ Lynch <daniel.lynch2016@gmail.com> Co-authored-by: Ewan Betts <ewan.betts@southernforestsvet.com>
Follow-up to #579, built on top of it. Until #579 merges, this diff also shows its commits: only the last three are new here (1385900, cebb8d7 and 5cd5a66).
Changes
Prepare shaders on the loading screen (1385900), suggested by @jpkto on #579.
LoadCallbackStage::Warmis added last, so the other stages keep their values. Android shows it as shader building.Link mode in the warm-up marker (cebb8d7), suggested by @justin-wq1337 on #579.
EDEN_NX2_GPL_FAST_LINK,EDEN_NX2_GPL_OPTIMIZE). Together they decide whether shader libraries keep link time optimization info, which changes what the driver caches. A change of link mode now reruns the warm-up instead of needing a manual rebuild.Save the driver pipeline cache after a warm-up (5cd5a66), reported by @IorekByr on #579.
Testing