WIP: perf(vk): per-worker pipeline caches, every CPU and no saved pipeline cache for shader preparation on MoltenVK #650

Closed
mooki wants to merge 5 commits from mooki/Longshot:mac/prep-fast into main
Member

Type: perf · Risk: medium · Tested on: macOS 26.7.1, Apple M4 Max (MoltenVK 1.4.2), arm64 and x86_64 under Rosetta · Needs: a Mac game A/B at this head; Windows, Linux (build and start)

Why

On a Mac the first start's shader preparation crawled: 39 of 38,809 shader programs in its first 180 s, an estimated 33
minutes for the compute programs alone. MoltenVK holds one lock per pipeline cache across each shader's conversion to Metal's
language and Metal's compile (MVKPipeline.mm getShaderLibrary), and every preparation worker compiled with the one shared
cache, so the 8 workers compiled one pipeline at a time; the preparation also used only half the CPUs, a Windows rule. And a
later start that reads the saved pipeline cache back is no faster on MoltenVK: it compiles every stored Metal library again,
one after another, as the file is read. This PR gives each preparation worker a pipeline cache of its own on MoltenVK, uses
every CPU on macOS, and on MoltenVK neither reads nor writes the pipeline cache's files. It is part of the macOS port. See #420.

What changed

On MoltenVK each worker of every preparation phase compiles with a pipeline cache of its own, asks the shared cache first, and
the phase merges the workers' caches into the shared one after they join; a compile in play goes into a cache of its own that is
merged into the play generation's at once. On macOS the preparation runs on every usable CPU. On MoltenVK the pipeline cache's
files are not read at the start and not written. Elsewhere the code paths are main's: no new lock, no new file behaviour.

  • worker caches: gpu/vk/worker_caches.h (new, no Vulkan call of its own): Set (a cache per worker, made without
    EXTERNALLY_SYNCHRONIZED), Scope, create (the shared cache asked with FAIL_ON_PIPELINE_COMPILE_REQUIRED, then a compile
    with the worker's own), merge_into (one vkMergePipelineCaches in slot order), made_alone (play), runs/claim (a program
    variant's notes taken whole by one worker, so its libraries compile once).
  • gpu/vk/vk_draw.cpp: create_cached (the worker path; play's compile alone), worker_caches_on, worker_caches_merge,
    pcache_use_held; the four phases prepare_run, precompile_warm (both passes), precompile_at_create, precompile_run
    (notes sorted by program variant and handed out in runs only with the caches on); pipeline_cache_save and
    pcache_saved_now hold the merge lock before #190's fetch lock.
  • locks, on main's #190 and #252: pcache_mu (a merge into the shared cache needs it to itself, vk.xml dstCache
    externsync) and a play generation's lock are taken only where the worker caches are on. The worker caches are never on
    with #190's fetch lock (pcache_fetch_lock_on: Intel's Windows driver, RX_VK_PCACHE_FETCH_LOCK=1): a merge reads its
    caches' data as a fetch does, and #252's gate lets one compile at a time into Intel's compiler there anyway. The order is
    Intel's gate, then pcache_mu, then the fetch lock. prepare_run's compute warm keeps #252's gate around its compile.
  • every CPU: gpu/vk/prepare_workers.h count(..., every) and kEveryCpu (__APPLE__), passed by prepare_cpus
    (gpu/vk/vk_progcache.cpp); the vk: preparation: line says which rule and whether the caches are per worker.
  • no saved cache on MoltenVK: gpu/vk/vk_vendor.h pcache_no_load (and worker_caches), vk_draw.cpp pcache_load_off
    (RX_VK_PCACHE_LOAD decides when set, the device otherwise), pcache_files_unused: pcache_init reads neither the main file
    nor the play files, nothing is saved, play_cache_save writes no play file, and RX_VK_LOAD=create's .built marker is
    dropped at each start (no later start's cache holds what the warm built). The vendor-fix log line (gpu/vk/vk_main.cpp) gains
    two fields.
  • docs: docs/development.md rows for RX_VK_WORKER_CACHES (new) and RX_VK_PCACHE_LOAD (its default changes on MoltenVK),
    docs/pipeline-cache-storage.md, gpu/vk/VULKAN.md.
  • tests: tests/test_worker_caches.py (new; vk_draw.cpp's own pieces against a made-up driver whose caches hold a lock across
    a compile, as MoltenVK's do), tests/test_prepare_every_cpu.py (new), new checks in test_vendor_fixes, test_pcache_stamp,
    test_pipeline_play_cache (cases alone and unused, and no lock taken off MoltenVK in route) and test_warm_bounded;
    stand-ins in tests/pipeline_play_cache_test.cpp and tests/pipeline_cache_save_test.cpp.

Measurements

Verdict: helps on MoltenVK, measured at the topic's own commits before the rebase: 4.4× the compute and 4.6× the graphics
pipelines a second in LS's own code against one shared cache (16 workers), a later start's pipelines in 1.41 s compiled anew
against 6.65 s through the saved cache, and the first game start reached 230 programs in 180 s against 39. Not measured again at
this head: on MoltenVK the rebase takes the same locks and makes the same Vulkan calls in the same order (#190's fetch lock and
#252's gate are off there), but a game A/B at this head is in Must be tested.

Baseline: main does not run on macOS yet. The GPU check compares the code before this change (one shared cache, 8 workers) with
it, in one process, through LS's own create_cached, worker_caches_merge and friends cut from 61bfbf1a's source; the game
starts compare macOS integration builds without the topic (run #2) and with it (run #4, aefe6b0a).

Hardware: macOS 26.7.1, Apple M4 Max (40-core GPU), MoltenVK 1.4.2, 16-core CPU, 64 GB
Scene: the first start's shader preparation (game runs #2 and #4, windowless, defaults); a synthetic 48 compute + 16 graphics
pipeline bench through LS's code (GPU check), Metal's per-user shader cache blocked
Method: GPU check run 01 (reports/PrepFast-files/gpu-check/runs-01/): one process per route, wall time and pipelines a second
per arm; game: the startup counter at 180 s and the OS log's Metal compiles. One run each
How to reproduce: the GPU check's frozen command in reports/2026-11-14-prep-fast.md §7; in the game, RX_VK_WORKER_CACHES=0
against unset, and RX_VK_PCACHE_LOAD=1 against unset, on the same build

Before, one shared cache, 8 workers (main's way): the GPU check @ aa8ecba5 After, a cache per worker, 8 workers: the GPU check @ aa8ecba5 After, a cache per worker, 16 workers (this PR's way): the GPU check @ aa8ecba5
compute pipelines a second (48) 10.35 30.81 (3.0×) 45.41 (4.4×)
graphics pipelines a second (16) 9.20 42.58 (4.6×)
Metal compiler cores busy, mean 2.4-2.7 7.8 11.9
later start (new process) Before, the saved cache read (main's way, reload-one): the GPU check @ aa8ecba5 After, nothing read, compiled anew (this PR's way, recompile): the GPU check @ aa8ecba5
load + 48 compute + 16 graphics pipelines 5,748 + 656 + 247 ms = 6.65 s 0 + 1,025 + 381 ms = 1.41 s
first game start, 180 s Before: integration @ b7a7c95f (run #2, without the topic) After: integration @ aefe6b0a (run #4, with it)
shader programs prepared 39 of 38,809 230 of 38,809
Metal compiles under way at once up to 4, in strict pairs up to 17
#4: vk: preparation: 16 workers for the shader programs (16 CPUs usable; every one: macOS, …), each compiling with a pipeline cache of its own
#4: vk: vendor fixes for MoltenVK (…): …, a pipeline cache per worker 1, the pipeline cache's files unread 1 (RX_VK_PCACHE_LOAD over it)
GPU check: vk: pipeline cache: the compute warm: the caches of its 16 workers merged into it in 0.1 ms

Reports: reports/2026-11-14-prep-fast.md (GPU check run 01, its receipts), reports/2026-11-14-prep-speed.md,
reports/2026-11-14-mac-startup-04.md (run #4, and run #2 for the before).

Flags and settings

One new switch and a new MoltenVK default for an existing one; nothing changes elsewhere.

Flag What it is Before this PR After this PR Effect Helps / doesn't help / untested Recommendation
RX_VK_WORKER_CACHES =0: every compile with the one shared cache; =1: a cache per worker on any driver with pipelineCreationCacheControl (no switch; one shared cache) on for MoltenVK only; never where RX_VK_PCACHE_FETCH_LOCK is on the preparation's compiles run side by side on MoltenVK helps: MoltenVK (3.0-4.6×); doesn't help: drivers whose caches don't hold a lock across a compile; untested: every other driver with =1 on for MoltenVK as it ships; =0 only to compare
RX_VK_PCACHE_LOAD =0: the main pipeline cache file neither read nor saved; =1: read and saved unset: read and saved on every driver unset: read and saved, but on MoltenVK neither, nor the play files; =0/=1 as before on every other driver a later start on MoltenVK compiles its pipelines anew instead of recompiling every saved library one by one helps: later starts on MoltenVK (6.65 → 1.41 s in the check); untested: players with Metal's per-user cache warm as it ships; =1 on a Mac only to compare

Testing

Tested: on macOS 26.7.1, Apple M4 Max, at this head (main 6c9edffe plus this PR), arm64 with the native compiler and
x86_64 with clang++ -arch x86_64 (the fixtures run under Rosetta):

  • test_worker_caches, test_prepare_every_cpu, test_pcache_stamp, test_vendor_fixes, test_warm_bounded, and main's
    test_pcache_fetch_lock (#190): pass on both arches; test_intel_compiler_gate (#252, source only) and test_recorder_sync
    (arm64): pass. reports/PubWave-files/PubPrep/p22-head-01/.
  • test_pipeline_play_cache and test_pipeline_cache_save fail on main 6c9edffe on macOS before they run (posix_fallocate
    in pipeline_cache_file.h, fixed by the macOS PR #476), so they ran on this head with #476's pipeline_cache_file.h hunk
    applied in a scratch copy: pass on both arches (every case of the play caches, alone, unused and #190's lock-users
    among them). reports/PubWave-files/PubPrep/proof-02/p22-h476-*.
  • On main 6c9edffe (with #476's hunk) and this PR's test files copied in: every one of those fails on both arches
    (test_worker_caches: no worker_caches in vk_vendor.h, no phase wiring; test_prepare_every_cpu: "prepare_cpus() = 8
    workers of 16 usable CPUs; want 16"; test_pcache_stamp: on MoltenVK the file read, merged, saved and stamped;
    test_vendor_fixes: no worker_caches/pcache_no_load; test_pipeline_play_cache: no worker_caches.h).
    reports/PubWave-files/PubPrep/proof-02/p22-base-*.
  • On this branch before the rework (f5905178, the topic's four commits on main, with #476's hunk) and the head's test files:
    test_worker_caches fails on both arches on the rework's checks (worker caches on beside the fetch lock, pcache_mu taken
    off MoltenVK, the save's lock order), and test_pipeline_play_cache's route case fails on both (a pipeline is not made
    while pcache_mu is held elsewhere: the lock was taken on every driver). reports/PubWave-files/PubPrep/proof-02/p22-pre-*.
  • Every check that reads a file this PR changes (142), through tools/ls-suite.py: 129 pass, 13 fail; 11 of them fail on
    main 6c9edffe on macOS too (reports/Baseline-files/main-6c9edffe/), and the other 2 (test_precompile_compute_guard,
    test_shader_module_guard: "Windows structured exceptions required") are skipped on macOS in a full run and fail only
    because they were named. reports/PubWave-files/PubPrep/p22-related-02/, p22-p37-related-02-compare.txt.
  • vk_draw.cpp, vk_main.cpp and vk_progcache.cpp compiled whole for arm64 and x86_64 with the renderer's flags, with the
    macOS compile pieces of #476, #490 and #492 applied (main's renderer does not compile on macOS without them): 6 of 6.
    reports/PubWave-files/PubPrep/p22-tu-stack-02/.
  • git merge-tree of this head with each open macOS PR's head (#431, #437, #438, #476, #479, #480, #490, #491, #492): no
    conflict.
  • GPU check run 01 and game runs #4 (and later integration runs) as in Measurements, at the topic's own commits.

Not tested: the game at this head (main does not run on macOS without the other macOS PRs), players' later starts with
Metal's per-user shader cache warm, and Windows and Linux (no change in behaviour there: the new locks, the per-worker caches
and the file rule are MoltenVK-only by worker_caches_on/pcache_files_unused; not built or run on them: its test request
covers the build and two starts).

Must be tested:

  • macOS, Apple silicon, on the rebased macOS stack: a first start with RX_VK_WORKER_CACHES=0 against unset on the same build,
    expecting about 3-5× the shader programs at 180 s with the caches and 0 [mvk-error] lines (by hand: the parent's GPU slot,
    the vk: preparation: and startup counter lines).
  • Windows and Linux, any GPU: build this PR (head 078c9a7a) and main 6c9edffe the same way and start each to the title twice,
    expecting the vk: vendor fixes line to say a pipeline cache per worker 0, the pipeline cache's files unread 0, the first
    start to save the pipeline cache and the second to read it, with the same preparation lines and timings in line with
    main's (by hand: test request T-N598)

Risks

What could break: on MoltenVK, a merge into the shared cache waits for asks of it under way (milliseconds), and a shader
stage shared by different programs may be compiled once per worker on a first start instead of once (MoltenVK's cache-only ask
is per whole pipeline); a later start compiles every pipeline it needs instead of reading the saved cache (with Metal's own
shader cache warm that is mostly conversion; not measured). Other drivers: the vendor-fix log line gains two fields; the
precompile claims its items through worker_caches::claim (one at a time without the caches, as next++ did).

Turning it off: RX_VK_WORKER_CACHES=0 (one shared cache), RX_VK_PCACHE_LOAD=1 (read and write the files on MoltenVK),
or revert the PR.

Compatibility: Nothing changes for players on Windows and Linux: no shader stamp, cache format or MKWVK_API_VERSION change,
and their cache/longshot_vk_pipelines.bin is read and written as before. On macOS the pipeline cache's files are no longer
written (an old one is left unread; the stamp is untouched).

Reviewer notes

  • Review 1843f6f8..078c9a7a (5 commits): every CPU on macOS; the worker caches; the header made Vulkan-free (a vkDestroy*
    in a header bypasses the recorder's boundary, test_recorder_sync); no saved cache on MoltenVK; and the rework on main's
    #190 and #252 (078c9a7a), which is where to look for the locks.
  • Based on main 6c9edffe; no dependency. The topic's four commits were cherry-picked with -x from macos/prep-fast
    (3573e736 2174e33f aa8ecba5 61bfbf1a). Conflicts in the pick:
    • gpu/vk/vk_draw.cpp prepare_run's compute warm: #252 (57f4eaec) put IntelDriverGate and guarded_compute_held there;
      kept, and the caches' path goes through create_cached under that gate (cached_compute would take the gate twice).
    • gpu/vk/vk_draw.cpp pipeline_cache_save: #190 (f41a5c59) and #404 (b2911d33) rewrote the fetch as PcacheFetch with
      put-offs; main's fetch kept, with the merge lock taken before it.
    • gpu/vk/vk_draw.cpp play_cache_save: #190 added its wait_ms/queue parameters; main's signature with this PR's early
      return.
    • gpu/vk/VULKAN.md: #321 (76699aad) renamed the file to cache/longshot_vk_pipelines.bin; main's name with this PR's
      sentences. tests/test_pipeline_play_cache.py: #190's lock-users case and docstring kept beside alone and unused.
  • The semantic rework on #190/#252/#321 is its own commit, 078c9a7a: the merge lock and the generation's lock gated to the
    worker caches, the worker caches off wherever the fetch lock is on, the lock order, and the tests and docs that follow
    (test_pcache_stamp's fixture follows #321's cache_path and #190's PcacheFetch in pcache_init).
  • Sibling macOS PRs: #651 (renderer threads with 16 MiB stacks) changes the same thread lines in prepare_run,
    precompile_at_create and precompile_run (4 blocks, resolved in #652's merge of it, f9f166f7); #644 (descriptor sets for
    storage buffer lengths) extends the same vendor-fix logf in vk_main.cpp: one line with every field (as integration 7 did
    with #643's); #652 builds on this branch.
  • Conflicts with main: none. With the open macOS PRs (#431, #437, #438, #476, #479, #480, #490, #491, #492): none
    (git merge-tree).

Issues

See #420

Checklist

Everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description) of the status line's type.
  • Why gives the reason plainly and links the issues that fit (the tracker searched).
  • Measurements has a verdict, or one line saying why there is nothing to measure.
  • Testing names what was tested, what wasn't, and what must still be tested, where and how. (Test requests not open yet.)
  • Rebased on current main (main e090cc48).
  • python -I -B tests/run_all.py passes (any failure that also fails on main is named). (On the wave's merge of every macOS PR built with this one, 453eb678: 553 passed, 32 failed, 18 skipped against main 6c9edffe's 531/32/17 on this Mac; the failures are main's own except test_split_ub_flush, which #658's tree at the time broke and its head fixes; test_collect_wide now passes; the new tests pass, test_worker_caches and test_prepare_every_cpu among them, and test_darwin_audio_service, Darwin-only, is skipped by the runner; reports/PubWave-files/waveA/.)
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

When it applies

  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • Numbers from the same PC, scene and settings, at least 2 interleaved runs each, the median reported. (One run each, at the topic's own commits; the A/B at this head is in Must be tested.)
  • Switches documented (docs/development.md; the README for a menu row) and shipped at their recommended value.
  • A file in docs/tested/ for each new measured run (format in docs/tested-configurations.md). (Added with the A/B at this head.)
  • A check under tests/ that reads changed source updated in this PR, not weakened.
  • Docs updated.
**Type:** perf · **Risk:** medium · **Tested on:** macOS 26.7.1, Apple M4 Max (MoltenVK 1.4.2), arm64 and x86_64 under Rosetta · **Needs:** a Mac game A/B at this head; Windows, Linux (build and start) ## Why On a Mac the first start's shader preparation crawled: 39 of 38,809 shader programs in its first 180 s, an estimated 33 minutes for the compute programs alone. MoltenVK holds one lock per pipeline cache across each shader's conversion to Metal's language and Metal's compile (`MVKPipeline.mm` `getShaderLibrary`), and every preparation worker compiled with the one shared cache, so the 8 workers compiled one pipeline at a time; the preparation also used only half the CPUs, a Windows rule. And a later start that reads the saved pipeline cache back is no faster on MoltenVK: it compiles every stored Metal library again, one after another, as the file is read. This PR gives each preparation worker a pipeline cache of its own on MoltenVK, uses every CPU on macOS, and on MoltenVK neither reads nor writes the pipeline cache's files. It is part of the macOS port. See #420. ## What changed On MoltenVK each worker of every preparation phase compiles with a pipeline cache of its own, asks the shared cache first, and the phase merges the workers' caches into the shared one after they join; a compile in play goes into a cache of its own that is merged into the play generation's at once. On macOS the preparation runs on every usable CPU. On MoltenVK the pipeline cache's files are not read at the start and not written. Elsewhere the code paths are main's: no new lock, no new file behaviour. - worker caches: `gpu/vk/worker_caches.h` (new, no Vulkan call of its own): `Set` (a cache per worker, made without `EXTERNALLY_SYNCHRONIZED`), `Scope`, `create` (the shared cache asked with `FAIL_ON_PIPELINE_COMPILE_REQUIRED`, then a compile with the worker's own), `merge_into` (one `vkMergePipelineCaches` in slot order), `made_alone` (play), `runs`/`claim` (a program variant's notes taken whole by one worker, so its libraries compile once). - `gpu/vk/vk_draw.cpp`: `create_cached` (the worker path; play's compile alone), `worker_caches_on`, `worker_caches_merge`, `pcache_use_held`; the four phases `prepare_run`, `precompile_warm` (both passes), `precompile_at_create`, `precompile_run` (notes sorted by program variant and handed out in runs only with the caches on); `pipeline_cache_save` and `pcache_saved_now` hold the merge lock before #190's fetch lock. - locks, on main's #190 and #252: `pcache_mu` (a merge into the shared cache needs it to itself, `vk.xml` `dstCache` `externsync`) and a play generation's lock are taken only where the worker caches are on. The worker caches are never on with #190's fetch lock (`pcache_fetch_lock_on`: Intel's Windows driver, `RX_VK_PCACHE_FETCH_LOCK=1`): a merge reads its caches' data as a fetch does, and #252's gate lets one compile at a time into Intel's compiler there anyway. The order is Intel's gate, then `pcache_mu`, then the fetch lock. `prepare_run`'s compute warm keeps #252's gate around its compile. - every CPU: `gpu/vk/prepare_workers.h` `count(..., every)` and `kEveryCpu` (`__APPLE__`), passed by `prepare_cpus` (`gpu/vk/vk_progcache.cpp`); the `vk: preparation:` line says which rule and whether the caches are per worker. - no saved cache on MoltenVK: `gpu/vk/vk_vendor.h` `pcache_no_load` (and `worker_caches`), `vk_draw.cpp` `pcache_load_off` (`RX_VK_PCACHE_LOAD` decides when set, the device otherwise), `pcache_files_unused`: `pcache_init` reads neither the main file nor the play files, nothing is saved, `play_cache_save` writes no play file, and `RX_VK_LOAD=create`'s `.built` marker is dropped at each start (no later start's cache holds what the warm built). The vendor-fix log line (`gpu/vk/vk_main.cpp`) gains two fields. - docs: `docs/development.md` rows for `RX_VK_WORKER_CACHES` (new) and `RX_VK_PCACHE_LOAD` (its default changes on MoltenVK), `docs/pipeline-cache-storage.md`, `gpu/vk/VULKAN.md`. - tests: `tests/test_worker_caches.py` (new; `vk_draw.cpp`'s own pieces against a made-up driver whose caches hold a lock across a compile, as MoltenVK's do), `tests/test_prepare_every_cpu.py` (new), new checks in `test_vendor_fixes`, `test_pcache_stamp`, `test_pipeline_play_cache` (cases `alone` and `unused`, and no lock taken off MoltenVK in `route`) and `test_warm_bounded`; stand-ins in `tests/pipeline_play_cache_test.cpp` and `tests/pipeline_cache_save_test.cpp`. ## Measurements **Verdict:** helps on MoltenVK, measured at the topic's own commits before the rebase: 4.4× the compute and 4.6× the graphics pipelines a second in LS's own code against one shared cache (16 workers), a later start's pipelines in 1.41 s compiled anew against 6.65 s through the saved cache, and the first game start reached 230 programs in 180 s against 39. Not measured again at this head: on MoltenVK the rebase takes the same locks and makes the same Vulkan calls in the same order (#190's fetch lock and #252's gate are off there), but a game A/B at this head is in Must be tested. Baseline: main does not run on macOS yet. The GPU check compares the code before this change (one shared cache, 8 workers) with it, in one process, through LS's own `create_cached`, `worker_caches_merge` and friends cut from `61bfbf1a`'s source; the game starts compare macOS integration builds without the topic (run #2) and with it (run #4, `aefe6b0a`). Hardware: macOS 26.7.1, Apple M4 Max (40-core GPU), MoltenVK 1.4.2, 16-core CPU, 64 GB Scene: the first start's shader preparation (game runs #2 and #4, windowless, defaults); a synthetic 48 compute + 16 graphics pipeline bench through LS's code (GPU check), Metal's per-user shader cache blocked Method: GPU check run 01 (`reports/PrepFast-files/gpu-check/runs-01/`): one process per route, wall time and pipelines a second per arm; game: the startup counter at 180 s and the OS log's Metal compiles. One run each How to reproduce: the GPU check's frozen command in `reports/2026-11-14-prep-fast.md` §7; in the game, `RX_VK_WORKER_CACHES=0` against unset, and `RX_VK_PCACHE_LOAD=1` against unset, on the same build | | Before, one shared cache, 8 workers (main's way): the GPU check @ aa8ecba5 | After, a cache per worker, 8 workers: the GPU check @ aa8ecba5 | After, a cache per worker, 16 workers (this PR's way): the GPU check @ aa8ecba5 | |---|---|---|---| | compute pipelines a second (48) | 10.35 | 30.81 (3.0×) | 45.41 (4.4×) | | graphics pipelines a second (16) | 9.20 | | 42.58 (4.6×) | | Metal compiler cores busy, mean | 2.4-2.7 | 7.8 | 11.9 | | later start (new process) | Before, the saved cache read (main's way, `reload-one`): the GPU check @ aa8ecba5 | After, nothing read, compiled anew (this PR's way, `recompile`): the GPU check @ aa8ecba5 | |---|---|---| | load + 48 compute + 16 graphics pipelines | 5,748 + 656 + 247 ms = 6.65 s | 0 + 1,025 + 381 ms = 1.41 s | | first game start, 180 s | Before: integration @ b7a7c95f (run #2, without the topic) | After: integration @ aefe6b0a (run #4, with it) | |---|---|---| | shader programs prepared | 39 of 38,809 | 230 of 38,809 | | Metal compiles under way at once | up to 4, in strict pairs | up to 17 | ``` #4: vk: preparation: 16 workers for the shader programs (16 CPUs usable; every one: macOS, …), each compiling with a pipeline cache of its own #4: vk: vendor fixes for MoltenVK (…): …, a pipeline cache per worker 1, the pipeline cache's files unread 1 (RX_VK_PCACHE_LOAD over it) GPU check: vk: pipeline cache: the compute warm: the caches of its 16 workers merged into it in 0.1 ms ``` Reports: `reports/2026-11-14-prep-fast.md` (GPU check run 01, its receipts), `reports/2026-11-14-prep-speed.md`, `reports/2026-11-14-mac-startup-04.md` (run #4, and run #2 for the before). ### Flags and settings One new switch and a new MoltenVK default for an existing one; nothing changes elsewhere. | Flag | What it is | Before this PR | After this PR | Effect | Helps / doesn't help / untested | Recommendation | |---|---|---|---|---|---|---| | `RX_VK_WORKER_CACHES` | `=0`: every compile with the one shared cache; `=1`: a cache per worker on any driver with `pipelineCreationCacheControl` | (no switch; one shared cache) | on for MoltenVK only; never where `RX_VK_PCACHE_FETCH_LOCK` is on | the preparation's compiles run side by side on MoltenVK | helps: MoltenVK (3.0-4.6×); doesn't help: drivers whose caches don't hold a lock across a compile; untested: every other driver with `=1` | on for MoltenVK as it ships; `=0` only to compare | | `RX_VK_PCACHE_LOAD` | `=0`: the main pipeline cache file neither read nor saved; `=1`: read and saved | unset: read and saved on every driver | unset: read and saved, but on MoltenVK neither, nor the play files; `=0`/`=1` as before on every other driver | a later start on MoltenVK compiles its pipelines anew instead of recompiling every saved library one by one | helps: later starts on MoltenVK (6.65 → 1.41 s in the check); untested: players with Metal's per-user cache warm | as it ships; `=1` on a Mac only to compare | ## Testing **Tested:** on macOS 26.7.1, Apple M4 Max, at this head (main 6c9edffe plus this PR), arm64 with the native compiler and x86_64 with `clang++ -arch x86_64` (the fixtures run under Rosetta): - `test_worker_caches`, `test_prepare_every_cpu`, `test_pcache_stamp`, `test_vendor_fixes`, `test_warm_bounded`, and main's `test_pcache_fetch_lock` (#190): pass on both arches; `test_intel_compiler_gate` (#252, source only) and `test_recorder_sync` (arm64): pass. `reports/PubWave-files/PubPrep/p22-head-01/`. - `test_pipeline_play_cache` and `test_pipeline_cache_save` fail on main 6c9edffe on macOS before they run (`posix_fallocate` in `pipeline_cache_file.h`, fixed by the macOS PR #476), so they ran on this head with #476's `pipeline_cache_file.h` hunk applied in a scratch copy: pass on both arches (every case of the play caches, `alone`, `unused` and #190's `lock-users` among them). `reports/PubWave-files/PubPrep/proof-02/p22-h476-*`. - On main 6c9edffe (with #476's hunk) and this PR's test files copied in: every one of those fails on both arches (`test_worker_caches`: no `worker_caches` in `vk_vendor.h`, no phase wiring; `test_prepare_every_cpu`: "prepare_cpus() = 8 workers of 16 usable CPUs; want 16"; `test_pcache_stamp`: on MoltenVK the file read, merged, saved and stamped; `test_vendor_fixes`: no `worker_caches`/`pcache_no_load`; `test_pipeline_play_cache`: no `worker_caches.h`). `reports/PubWave-files/PubPrep/proof-02/p22-base-*`. - On this branch before the rework (`f5905178`, the topic's four commits on main, with #476's hunk) and the head's test files: `test_worker_caches` fails on both arches on the rework's checks (worker caches on beside the fetch lock, `pcache_mu` taken off MoltenVK, the save's lock order), and `test_pipeline_play_cache`'s `route` case fails on both (a pipeline is not made while `pcache_mu` is held elsewhere: the lock was taken on every driver). `reports/PubWave-files/PubPrep/proof-02/p22-pre-*`. - Every check that reads a file this PR changes (142), through `tools/ls-suite.py`: 129 pass, 13 fail; 11 of them fail on main 6c9edffe on macOS too (`reports/Baseline-files/main-6c9edffe/`), and the other 2 (`test_precompile_compute_guard`, `test_shader_module_guard`: "Windows structured exceptions required") are skipped on macOS in a full run and fail only because they were named. `reports/PubWave-files/PubPrep/p22-related-02/`, `p22-p37-related-02-compare.txt`. - `vk_draw.cpp`, `vk_main.cpp` and `vk_progcache.cpp` compiled whole for arm64 and x86_64 with the renderer's flags, with the macOS compile pieces of #476, #490 and #492 applied (main's renderer does not compile on macOS without them): 6 of 6. `reports/PubWave-files/PubPrep/p22-tu-stack-02/`. - `git merge-tree` of this head with each open macOS PR's head (#431, #437, #438, #476, #479, #480, #490, #491, #492): no conflict. - GPU check run 01 and game runs #4 (and later integration runs) as in Measurements, at the topic's own commits. **Not tested:** the game at this head (main does not run on macOS without the other macOS PRs), players' later starts with Metal's per-user shader cache warm, and Windows and Linux (no change in behaviour there: the new locks, the per-worker caches and the file rule are MoltenVK-only by `worker_caches_on`/`pcache_files_unused`; not built or run on them: its test request covers the build and two starts). **Must be tested:** - macOS, Apple silicon, on the rebased macOS stack: a first start with `RX_VK_WORKER_CACHES=0` against unset on the same build, expecting about 3-5× the shader programs at 180 s with the caches and 0 `[mvk-error]` lines (by hand: the parent's GPU slot, the `vk: preparation:` and startup counter lines). - Windows and Linux, any GPU: build this PR (head 078c9a7a) and main 6c9edffe the same way and start each to the title twice, expecting the `vk: vendor fixes` line to say `a pipeline cache per worker 0, the pipeline cache's files unread 0`, the first start to save the pipeline cache and the second to read it, with the same preparation lines and timings in line with main's (by hand: test request T-N598) ## Risks **What could break:** on MoltenVK, a merge into the shared cache waits for asks of it under way (milliseconds), and a shader stage shared by different programs may be compiled once per worker on a first start instead of once (MoltenVK's cache-only ask is per whole pipeline); a later start compiles every pipeline it needs instead of reading the saved cache (with Metal's own shader cache warm that is mostly conversion; not measured). Other drivers: the vendor-fix log line gains two fields; the precompile claims its items through `worker_caches::claim` (one at a time without the caches, as `next++` did). **Turning it off:** `RX_VK_WORKER_CACHES=0` (one shared cache), `RX_VK_PCACHE_LOAD=1` (read and write the files on MoltenVK), or revert the PR. **Compatibility:** Nothing changes for players on Windows and Linux: no shader stamp, cache format or `MKWVK_API_VERSION` change, and their `cache/longshot_vk_pipelines.bin` is read and written as before. On macOS the pipeline cache's files are no longer written (an old one is left unread; the stamp is untouched). ## Reviewer notes - Review `1843f6f8..078c9a7a` (5 commits): every CPU on macOS; the worker caches; the header made Vulkan-free (a `vkDestroy*` in a header bypasses the recorder's boundary, `test_recorder_sync`); no saved cache on MoltenVK; and the rework on main's #190 and #252 (`078c9a7a`), which is where to look for the locks. - Based on main 6c9edffe; no dependency. The topic's four commits were cherry-picked with `-x` from `macos/prep-fast` (`3573e736 2174e33f aa8ecba5 61bfbf1a`). Conflicts in the pick: - `gpu/vk/vk_draw.cpp` `prepare_run`'s compute warm: #252 (`57f4eaec`) put `IntelDriverGate` and `guarded_compute_held` there; kept, and the caches' path goes through `create_cached` under that gate (`cached_compute` would take the gate twice). - `gpu/vk/vk_draw.cpp` `pipeline_cache_save`: #190 (`f41a5c59`) and #404 (`b2911d33`) rewrote the fetch as `PcacheFetch` with put-offs; main's fetch kept, with the merge lock taken before it. - `gpu/vk/vk_draw.cpp` `play_cache_save`: #190 added its `wait_ms`/`queue` parameters; main's signature with this PR's early return. - `gpu/vk/VULKAN.md`: #321 (`76699aad`) renamed the file to `cache/longshot_vk_pipelines.bin`; main's name with this PR's sentences. `tests/test_pipeline_play_cache.py`: #190's `lock-users` case and docstring kept beside `alone` and `unused`. - The semantic rework on #190/#252/#321 is its own commit, `078c9a7a`: the merge lock and the generation's lock gated to the worker caches, the worker caches off wherever the fetch lock is on, the lock order, and the tests and docs that follow (`test_pcache_stamp`'s fixture follows #321's `cache_path` and #190's `PcacheFetch` in `pcache_init`). - Sibling macOS PRs: #651 (renderer threads with 16 MiB stacks) changes the same thread lines in `prepare_run`, `precompile_at_create` and `precompile_run` (4 blocks, resolved in #652's merge of it, `f9f166f7`); #644 (descriptor sets for storage buffer lengths) extends the same vendor-fix `logf` in `vk_main.cpp`: one line with every field (as integration 7 did with #643's); #652 builds on this branch. - Conflicts with main: none. With the open macOS PRs (#431, #437, #438, #476, #479, #480, #490, #491, #492): none (`git merge-tree`). ## Issues See #420 ## Checklist ### Everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`) of the status line's type. - [x] Why gives the reason plainly and links the issues that fit (the tracker searched). - [x] Measurements has a verdict, or one line saying why there is nothing to measure. - [ ] Testing names what was tested, what wasn't, and what must still be tested, where and how. (Test requests not open yet.) - [x] Rebased on current `main` (main e090cc48). - [x] `python -I -B tests/run_all.py` passes (any failure that also fails on `main` is named). (On the wave's merge of every macOS PR built with this one, `453eb678`: 553 passed, 32 failed, 18 skipped against main 6c9edffe's 531/32/17 on this Mac; the failures are main's own except `test_split_ub_flush`, which #658's tree at the time broke and its head fixes; `test_collect_wide` now passes; the new tests pass, `test_worker_caches` and `test_prepare_every_cpu` among them, and `test_darwin_audio_service`, Darwin-only, is skipped by the runner; `reports/PubWave-files/waveA/`.) - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### When it applies - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [ ] Numbers from the same PC, scene and settings, at least 2 interleaved runs each, the median reported. (One run each, at the topic's own commits; the A/B at this head is in Must be tested.) - [x] Switches documented (docs/development.md; the README for a menu row) and shipped at their recommended value. - [ ] A file in `docs/tested/` for each new measured run (format in `docs/tested-configurations.md`). (Added with the A/B at this head.) - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. - [x] Docs updated.
prepare_run's first part (every shader translated, and each compute
variant compiled for the pipeline cache) ran on half the CPUs
everywhere: prepare_cpus gives half_cpus unless the Windows process heap
is the segment heap. Half exists for the NT heap's lock that an
in-process driver compiler queued on, and for whole-PC freezes when
those compilers ran on every CPU at High priority. Neither holds on
macOS: MoltenVK converts SPIR-V to MSL in a few ms and hands every
compile to Metal's compiler service (MTLCompilerService, a launchd
service at nice 0), so the workers mostly wait on it. The Mac's first
start logged "8 workers for the shader programs (16 CPUs usable; half
the CPUs)".

prepare_workers::count takes an `every` argument (default false): each
usable CPU gets a worker, at least half_cpus and 1, at most kMaxWorkers.
kEveryCpu is true where __APPLE__ is defined, and prepare_cpus passes
it; the preparation's log line says "every one: macOS". In a synthetic
MoltenVK 1.4.2 benchmark on an M4 Max (16 CPUs, macOS 26.7.1) 16 threads
compiled 56-61 % more compute and 35-39 % more graphics pipelines a
second than 8, but only with a pipeline cache per thread; with the one
shared cache LS uses today the rate is flat from 4 to 16 threads. The
next commit gives each worker its own cache.

tests/test_prepare_every_cpu.py compiles the real prepare_workers.h and
prepare_cpus() cut from vk_progcache.cpp: on this compiler's target
prepare_cpus() gives 16 of 16 usable CPUs on macOS (8 elsewhere); count
with and without `every`; prepare_cpus with kEveryCpu forced either way.
On the base it fails: prepare_cpus() gives 8 of 16 on macOS.

Windows and Linux: unchanged (kEveryCpu false; the existing
test_prepare_workers and test_prepare_cpus cases are untouched).

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
(cherry picked from commit 3573e736e15078554543e4740c5e2d53c719fa89)
MoltenVK 1.4.2 holds one mutex per VkPipelineCache across the SPIR-V to
MSL conversion and Metal's library compile (MVKPipeline.mm
getShaderLibrary, _shaderCacheLock). Every worker of the start's
preparation compiled with the one start cache, pcache, so they compiled
one pipeline at a time: the Mac's first start (mac-arm64-a5f2d6f9-01,
M4 Max) logged strict pairs of Metal compiles every ~195 ms, 5.05
compute variants a second, an estimated 33 minutes for the compute
programs alone. A synthetic benchmark on that Mac (PrepSpeed, MoltenVK
1.4.2, macOS 26.7.1): one shared cache stays at 10.5 compute and 9.6
graphics pipelines a second from 4 to 16 threads; a cache per thread,
merged after, gives 31.5 / 29.7 at 8 threads and 49-51 / 40-41 at 16.

gpu/vk/worker_caches.h: a Set of empty caches, one per worker of a
phase, that a worker enters with Scope; create() asks the shared cache
first under VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT
and compiles what it lacks with the worker's own; merge_into() merges
them into the start cache with one vkMergePipelineCaches in slot order
(MoltenVK retains each Metal library, no compile). The caches are not
externally synchronized, so a shared slot stays correct.

vk_draw.cpp:
- create_cached: before play, a worker in a phase with a set goes
  through worker_caches::create (pcache asked first, so a saved file's
  or an earlier phase's pipelines are found as before); every use of
  pcache holds pcache_mu shared, and worker_caches_merge holds it alone
  (vkMergePipelineCaches's dstCache is externally synchronized:
  vk.xml externsync without INTERNALLY_SYNCHRONIZED_MERGE). A save's
  reads of pcache's data hold it shared too.
- prepare_run (the compute warm), precompile_run, precompile_warm (both
  passes) and precompile_at_create make a set under worker_caches_on,
  enter it in their workers and merge it right after the join; the
  precompile's stragglers left running are merged as far as they got.
- precompile_run, with the caches on, sorts the notes by program
  variant and hands a variant's notes to one worker in runs of up to
  32 (worker_caches::runs/claim), so its shader libraries are compiled
  once, not by every worker that met one of its notes at the same time.
- In play, with the caches on, a compile goes into a new cache of its
  own merged into the generation's at once under the generation's lock
  (made_alone), which the asks of the generation hold shared: the asks
  keep their order and find what any thread compiled.

vk_vendor.h: worker_caches, on for VK_DRIVER_ID_MOLTENVK only, and only
with pipelineCreationCacheControl; RX_VK_WORKER_CACHES forces it either
way. Elsewhere nothing measured says a driver's cache is locked across
its compiles, and the caches would only add merges. The pipelines built,
the persisted cache's format and its stamp are the same either way.

tests/test_worker_caches.py runs vk_draw.cpp's own create_cached,
play_cache_make, worker_caches_on and worker_caches_merge and the real
header against a made-up driver whose caches lock across a compile:
four workers compile at once (without a set they cannot); pcache asked
first, nothing compiled with it; ask-only callers never compiled; caches
not externally synchronized; one merge in slot order whatever order the
slots were taken in, with no ask of pcache beside it; play's compiles
merged into the generation at once; runs and claims. The four-workers
part runs against the base's create_cached and fails there (1 of 4
compiles met the others). tests/test_pipeline_play_cache.py gets an
"alone" case, and both play-cache and cache-save harnesses the new
stand-ins (pcache_mu, worker_caches_on).

Windows and Linux: the same code paths as before (no set, one shared
cache), plus an uncontended shared lock around pcache's uses.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
(cherry picked from commit 2174e33f14ccce8c1635565e734773a33bc32455)
tests/test_recorder_sync.py holds every header to no recorder boundary
call: vk_work.h, included last by each renderer .cpp, turns
vkDestroyPipelineCache into a drain of RX_VK_RECORDER's packets and then
the call, and a header's calls would be wrapped or not depending on what
included it. worker_caches.h called vkCreatePipelineCache,
vkDestroyPipelineCache and vkMergePipelineCaches itself, from vk_draw.cpp
before vk_work.h.

worker_caches::Set and made_alone now take a Calls struct with the
three operations; vk_draw.cpp defines worker_cache_calls after
vk_work.h, so a set's cache destruction goes through the recorder's
boundary like every other vkDestroy* in the file. Behaviour is
otherwise the same: the same caches, made, merged and destroyed in the
same order.

tests/test_worker_caches.py and tests/pipeline_play_cache_test.cpp take
worker_cache_calls from the source and the model respectively.
test_recorder_sync now fails only as on the base (vk_vma.cpp's
vmaDestroyAllocator), and the four tests that touch the caches pass.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
(cherry picked from commit aa8ecba546465f1ce65ac872ecf21d232b65cef0)
MoltenVK 1.4.2 keeps MSL, not GPU code, in a VkPipelineCache, and
vkCreatePipelineCache with saved data compiles every stored library one
after another on the calling thread (MVKPipeline.mm readData).
pcache_init hands it all of mkw_vk_pipelines.bin, and play_files_merge
every play file, at every start. The PrepFast GPU check on the M4 Max,
with Metal's per-user cache blocked as in the runner, ran three later
starts of the same 64 pipelines (80 libraries):
- reading the one file: 5.7 s of serial compiles, 6.6 s in all;
- 16 shards read in parallel: 1.4 s;
- nothing read, pipelines compiled from SPIR-V with the worker caches:
  1.4 s.
At the game's scale the read is tens of minutes per start.

vk_vendor.h gets pcache_no_load, on for VK_DRIVER_ID_MOLTENVK only and
logged in the vendor fixes' line with worker_caches. It is the device's
default for RX_VK_PCACHE_LOAD: pcache_load_off reads the switch first
(any value, as before) and falls back to the fix, so
RX_VK_PCACHE_LOAD=1 reads the files on MoltenVK as everywhere else. With
the fix on and the switch unset or off, pcache_files_unused holds:
- pcache_init reads neither the main file nor the play files and says
  so in the log; the cache lives in memory for the run;
- nothing is saved (pcache_no_save, as RX_VK_PCACHE_LOAD=0 already
  did), and play_cache_save writes no play file and keeps the
  generation in memory: unread play files would pile up, since they
  are compacted only where they are read;
- the stamp is left alone (pcache_drop_stale returns as for any
  start that does not read the files);
- precompile_run drops RX_VK_LOAD=create's longshot_pipelines.bin.built
  marker at each start, as it does when the files were dropped: no
  cache a later start reads holds the notes, so the warm builds them
  into the in-memory cache again before loads at create ask for them.

Saving does not continue on MoltenVK because nothing reads the file or
its stamp there:
- the prepared record (prepared_for) has its own file and stamp;
- --prepare-shaders' saved cache is useful only to a start that reads
  it;
- pcache_saved_now serves graphics pipeline libraries, which MoltenVK
  does not have;
- a start with RX_VK_PCACHE_LOAD=1 makes, saves and stamps its own.
Every other driver is unchanged, RX_VK_PCACHE_LOAD=0 there included
(main file unread and unsaved, play files still merged and written).

tests/test_vendor_fixes.py checks the two cache fixes on its 16 devices
and on MoltenVK: on for MoltenVK alone, RX_VK_WORKER_CACHES forcing
the first, vendor_fixes_env leaving pcache_no_load to the files'
switch. It also checks the log line and pcache_load_off's order.
tests/test_pcache_stamp.py runs pcache_load_off, pcache_files_unused,
pcache_drop_stale and pcache_init, cut from the source, in eight starts:
other drivers unset, 0, empty and 1 as before, and MoltenVK unset, 0,
off and 1. test_pipeline_play_cache gets an "unused" case. All three
fail on aa8ecba5. tests/test_warm_bounded.py's source check of the
marker's removal takes the new condition.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
(cherry picked from commit 61bfbf1ae7c0966a9436ab0c556a9ee547bb3b5f)
fix(macos): the worker caches follow main's fetch lock (#190) and Intel's gate (#252)
All checks were successful
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 3s
CI / Workflow lint (actionlint) (pull_request) Successful in 5s
Third-party parts / As their notes say (pull_request) Successful in 8s
PR description / Description follows the template (pull_request_target) Successful in 4s
078c9a7a54
Main's #190 (f41a5c59) put a fetch lock (PcacheFetch, PcacheUse) on the pipeline cache's save and compile path, the
very path where the worker caches add the lock their merges need, and #252 (57f4eaec) put Intel's compiler gate
(IntelDriverGate) around the compiles. The worker caches now sit on top of both:

- pcache_mu, the merge lock, is taken only where the worker caches are on (pcache_use_held: MoltenVK by default).
  Elsewhere create_cached, the save's fetch and pcache_saved_now take exactly #190's locks, as on main: no new lock
  on any other driver. A play generation's lock is likewise taken only where play's compiles are merged into it
  (g->alone).
- The worker caches are never on with the fetch lock (worker_caches_on: not with pcache_fetch_lock_on, Intel's
  Windows driver or RX_VK_PCACHE_FETCH_LOCK=1): a merge reads its caches' data as a fetch does, which that lock keeps
  apart from the builds there, and the gate lets one compile at a time into Intel's compiler there anyway.
- The order of the locks: Intel's gate, then pcache_mu, then the fetch lock. The save's fetch and pcache_saved_now
  take pcache_mu before PcacheFetch; a merge takes pcache_mu alone.
- prepare_run's compute warm keeps #252's gate around its compile and, with the caches, goes through create_cached
  under it (cached_compute would take the gate a second time).
- tests: test_worker_caches checks the gating (with the caches off a pipeline is made while pcache_mu or the
  generation's lock is held alone elsewhere; with them on it waits for pcache_mu), worker_caches_on against the
  fetch lock, the warm's gate and pcache_mu ahead of PcacheFetch in the save. test_pipeline_play_cache's route case
  checks that no lock of theirs is taken off MoltenVK; pipeline_play_cache_test.cpp and pipeline_cache_save_test.cpp
  get pcache_use_held stand-ins. test_pcache_stamp's load fixture follows #321's cache_path and #190's PcacheFetch in
  pcache_init, and play_cache_save's new parameters.
- docs: #321's cache/longshot_vk_pipelines.bin in the RX_VK_WORKER_CACHES row and in vk_vendor.h, and a row for
  RX_VK_PCACHE_LOAD, whose default this PR changes on MoltenVK.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
mooki force-pushed mac/prep-fast from 078c9a7a54
All checks were successful
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 3s
CI / Workflow lint (actionlint) (pull_request) Successful in 5s
Third-party parts / As their notes say (pull_request) Successful in 8s
PR description / Description follows the template (pull_request_target) Successful in 4s
to eee107e2f1
Some checks failed
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
Website / Website on Cloudflare (pull_request) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 17s
CI / Conventional Commit title (pull_request) Successful in 15s
CI / Workflow lint (actionlint) (pull_request) Successful in 22s
Guide screenshots / Guide images and local UI source review (pull_request) Failing after 20s
Third-party parts / As their notes say (pull_request) Successful in 27s
2026-11-15 07:25:26 +00:00
Compare
mooki force-pushed mac/prep-fast from eee107e2f1
Some checks failed
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
Website / Website on Cloudflare (pull_request) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 17s
CI / Conventional Commit title (pull_request) Successful in 15s
CI / Workflow lint (actionlint) (pull_request) Successful in 22s
Guide screenshots / Guide images and local UI source review (pull_request) Failing after 20s
Third-party parts / As their notes say (pull_request) Successful in 27s
to 6bc44ad83b
Some checks failed
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
Website / Website on Cloudflare (pull_request) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 6s
CI / Conventional Commit title (pull_request) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 6s
Guide screenshots / Guide images and local UI source review (pull_request) Failing after 6s
Third-party parts / As their notes say (pull_request) Successful in 10s
Website / Website on Cloudflare (pull_request_target) Has been skipped
2026-11-15 13:10:06 +00:00
Compare
Author
Member

Folded into #849 (fix(macos): MoltenVK's precompile and pipelines on Metal)

Folded into #849 (fix(macos): MoltenVK's precompile and pipelines on Metal)
mooki closed this pull request 2026-11-15 18:08:23 +00:00
Some checks failed
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
Website / Website on Cloudflare (pull_request) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 6s
CI / Conventional Commit title (pull_request) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 6s
Guide screenshots / Guide images and local UI source review (pull_request) Failing after 6s
Third-party parts / As their notes say (pull_request) Successful in 10s
Website / Website on Cloudflare (pull_request_target) Has been skipped

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!650
No description provided.