feat(macos): ship the Vulkan loader with KosmicKrisp and MoltenVK, MoltenVK the default #654
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
beanstalk: approved @ 1697603
beanstalk: approved @ 2c5a62e
beanstalk: approved @ 4fd82b7
beanstalk: approved @ 64d6d09
beanstalk: approved @ 6f91fae
beanstalk: approved @ 70a78f9
beanstalk: approved @ 8d361dc
beanstalk: approved @ 9d8356e
beanstalk: approved @ abf2227
beanstalk: approved @ d82f202
beanstalk: approved @ e5f1e40
beanstalk: approved @ ee8ab5f
ci: approved
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/macos
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
regression
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
ci: approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!654
Loading…
Reference in a new issue
No description provided.
Delete branch "mooki/Longshot:mac/kosmickrisp"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type: feat · Risk: low · Tested on: macOS 26.7.1, Apple M4 Max (MoltenVK 1.4.2; KosmicKrisp from LunarG's Vulkan SDK 1.4.363.1), arm64 and x86_64 under Rosetta · Needs: macOS 14.4 or 15 (start)
Why
On a Mac the game talks to Vulkan through a driver that translates it to Metal. Until now the macOS package carried one
driver, MoltenVK, loaded directly. KosmicKrisp is a second one, LunarG's conformant Vulkan 1.4 driver on Metal 4 (Apple
silicon, macOS 26 or later), and it has much of what our MoltenVK workarounds stand in for (null descriptors, robust
buffer access, indirect count draws, last-vertex provoking, about a million descriptors per stage). It can only be used
through the Vulkan loader. This PR packages the loader with both drivers and lets the game choose one at startup, so the
two can be measured on the same build.
MoltenVK stays the default on every macOS. KosmicKrisp is used only when asked for (
RX_VK_DRIVER=kosmickrisp), becauseit is not good enough yet: its first game start (#20) never reached the title, and a fix it needs (the stored image reads
of #675) is not in yet. It is part of the macOS port. See #420.
What changed
With a new optional build input,
LONGSHOT_VULKAN_LOADER, the macOS package carries LunarG's Vulkan loader, MoltenVKand (arm64 builds with
LONGSHOT_KOSMICKRISP) KosmicKrisp, each driver with a loader manifest beside it. At startup thegame points the loader at one packaged driver: MoltenVK, unless
RX_VK_DRIVER=kosmickrispasks for KosmicKrisp. Withoutthe loader input the package is exactly as before and MoltenVK is loaded directly. Everything new is Apple-only.
host/posix/darwin_vulkan.chos_darwin_vulkan_driver()(a pure function) andhos_darwin_vulkan_choose_driver()(once,pthread_once):RX_VK_DRIVER=kosmickrispormoltenvkwith thatdriver's manifest packaged; else a
VK_DRIVER_FILESorVK_ICD_FILENAMESthe user set is left to the loader; elseMoltenVK when packaged; else KosmicKrisp when it is the only packaged driver; else nothing (no manifests: MoltenVK
loaded directly, as before). The chosen manifest goes into
VK_DRIVER_FILESwithsetenv, so the loader neversearches system folders for a driver. One log line:
vk: Vulkan driver: MoltenVK (default; RX_VK_DRIVER=kosmickrisp to measure KosmicKrisp): VK_DRIVER_FILES=<game>/vulkan_moltenvk_icd.json. KosmicKrisp's own range (Apple silicon,macOS 26 or later, from
kern.osproductversion) only words that line.host/posix/darwin_vulkan.hnames the twomanifests and documents the order.
host/hos_main.cmain()calls the choice right afterlongshot_env.txt(soRX_VK_DRIVERcan come fromthere) and before any thread or display work, inside
#if defined(__APPLE__); the first library load calls it too(
choose_library), so it is never later than that.nx2/darwin_package.pyICD_MANIFESTS,Plan.icd()andicd_manifest()writevulkan_moltenvk_icd.jsonand
vulkan_kosmickrisp_icd.jsonbeside their drivers with a relativelibrary_path(the loader resolves it againstthe manifest's folder, so the game folder still moves as a whole); the manifests are hashed and verified like every
packaged file, and leftovers in a game folder are refused (
RUNTIME_NAMES). KosmicKrisp is linked with dynamic lookup:FLAT_IMPORTS["kosmickrisp"]admits, for that one input, only Mesa's weak dispatch entrypoints (_kk_*,_wsi_*,_vk_common_*,_vk_cmd_enqueue_*) and CoreGraphics' colour spaces, read from all three binding forms; every otherflat import is still refused.
nx2/darwin_build.pydependencies()takes the two inputs: KosmicKrisp needs the loaderand an arm64 build (it has no x86_64 slice).
docs/development.md, theLONGSHOT_VULKAN_LOADERandLONGSHOT_KOSMICKRISPbuild inputs and theRX_VK_DRIVERswitch.tests/test_vk_darwin_admission.py(the driver choice, 27 cases, and the manifest paths beside the executable;the manifest names must equal
ICD_MANIFESTS),tests/test_macos_package.pyVulkanDriverPackage(5 tests: flatimports by name in the chained-fixups, opcode-stream and symbol-table forms; manifests need the loader and their
driver; the build's admission, including the x86_64 refusal; relative manifests survive a move and are verified;
leftovers refused),
tests/display_startup_failure_test.cpp(the driver is chosen once, afterlongshot_env.txt,before any thread or display work).
Measurements
Verdict: KosmicKrisp is not good enough yet to be the default, so it ships opt-in and MoltenVK stays the default. On
the same build and package, KosmicKrisp's start prepared 1,607 of 38,809 shader programs in 1,770 s and never reached
the title; MoltenVK through the packaged loader prepared all 38,809 in 58.0 s and reached the title and the menu.
Baseline: main does not run on macOS yet, so both columns are the macOS port's integration build 09f9a171 (Integration
28), which carries this PR's source commits
61774ddband2ed98ef3(andfe64034a, the follow-up below), with bothdrivers packaged. Run #20 had no
RX_VK_DRIVER, and that build's rule then picked KosmicKrisp on macOS 26; run #21 choseMoltenVK with
RX_VK_DRIVER=moltenvk, the driver this PR's default (73ee181e) picks. One run each; this decides thedefault, not a speed-up.
Hardware: macOS 26.7.1, Apple M4 Max (40-core GPU), 16-core CPU, 64 GB; MoltenVK 1.4.2, KosmicKrisp vulkan-sdk-1.4.363 (git-269a955c89)
Scene: the start from the field-dawn save, windowless (
tools/ls-run.py), program cache translated again in full on bothMethod: the game log's
vk:lines, the runner's receipt and a process sampler; run reportsreports/2026-11-14-mac-startup-20.mdand-21.mdHow to reproduce: a package built with
LONGSHOT_VULKAN_LOADERandLONGSHOT_KOSMICKRISPfrom the SDK, started once withRX_VK_DRIVER=kosmickrispand once without itMost of that gap is not KosmicKrisp compiling 50 times slower (
reports/2026-11-14-kk-slow.md): the renderer's"compiler service" handling, which moves compute pipelines out of the preparation into the precompile, is keyed on
MoltenVK's driver ID only, so on KosmicKrisp every compute pipeline was compiled inside the preparation. MoltenVK doing
the same work cold (start #14) looked the same: 278 programs about 7 minutes in, against KosmicKrisp's 217. KosmicKrisp
also keeps no shader disk cache, and its pipeline cache holds Metal source, so a cache hit compiles again. A probe that
times both drivers on the same SPIR-V (KK-03) is frozen, not run.
Game-free GPU probes on KosmicKrisp (kk02 and kk03, arm64,
reports/2026-11-14-kosmickrisp.mdsection 6), which say whichMoltenVK workaround it needs:
materialize_image_readsoff (KosmicKrisp'sstate on main and with this PR), a whole texel-buffer read before an atomic in the same invocation returned the value
after the atomic (128 of 128 lanes, and 4 of 128 in a second shape); with it on, 44 of 44 correct. KosmicKrisp needs
that fix (follow-up, see Reviewer notes).
MslArrayPtr 22 of 22, SegBind 17 cases and 72 of 72 data checks (count draws match), TexelDesc 27 of 27 (kk03, after
kk02 stopped at the probe's own 512 MiB guard; peak 558 MiB): none of the other workarounds is needed there.
changed) and re-frozen as kk02.
Flags and settings
Two optional macOS build inputs and one macOS-only switch; nothing changes off Apple, and a build without the new inputs
packages and runs as before.
LONGSHOT_VULKAN_LOADERlibvulkan.1.dylibLONGSHOT_KOSMICKRISPlibvulkan_kosmickrisp.dylibRX_VK_DRIVERkosmickrispormoltenvkkosmickrisp: measurement only (run #20 did not reach the title)Testing
Tested: on macOS 26.7.1, Apple M4 Max, at
9e0b02f0, this PR's head before the rebases ontoe090cc48andaa95d35f(main
6c9edffewith #479 and #438 merged; the head runs were at 346b11c8, the same tree832d8dc8before a commitmessage was reworded). Each test also ran against the base
12d1c5e2(an archive of #479 + #438 without this PR) andfails there. Logs:
reports/PubWave-files/PubKk/proof/.tests/test_vk_darwin_admission.py --arch arm64and--arch x86_64(Rosetta): exit 0 on both, "Darwin packaged Vulkandriver choice (27 cases) and manifest paths beside the executable". Against the base (
--source): exit 1 on both,"Darwin: no packaged Vulkan driver choice". Against
908d6b1c(before MoltenVK the default): exit 1 on both, 15 of the27 cases failing, the first "arm64 on 26: MoltenVK by default" (got KosmicKrisp).
tests/test_macos_package.py --arch arm64 --arch x86_64: 29 tests OK, among them the 5VulkanDriverPackagetests(the x86_64 build refuses KosmicKrisp: it has no x86_64 slice) and the real Mach-O package on both architectures. The
same file on the base: 2 failures and 3 errors (no
FLAT_IMPORTS, noPlan.icd, noflatadmission). The pick before77255c6c: 1 error (the fixture patchedtoolchain.architecture, which #479 moved todarwin_toolchain).tests/test_display_startup_failure.py, arm64 and x86_64 (the fixture compiled withclang++ -arch x86_64, run underRosetta:
proc_translated=1): 11 cases pass on both. Against the base'shost/hos_main.c(--source-root): all 11fail on both, "packaged Vulkan driver not chosen once at startup".
tests/test_vk_darwin_sdl.py --arch arm64and--arch x86_64(16 cases, pass) andtests/test_vendor_fixes.py(16 devices, pass).host/posix/darwin_vulkan.ccompiled withnx2/build.py's own host command for arm64 and x86_64, and again with-Wall -Wextra -Werror: 0 warnings; the objects (arm64542c78e7…, x86_64ecfd6b3b…) are byte-identical to thesource branch's at
73ee181e.host/hos_main.c,host/posix/darwin_vulkan.h,tests/display_startup_failure_test.cpp) is main's text afterunifdef -U__APPLE__;host/posix/darwin_vulkan.cdiffers from #438's only in its opening comment, preprocesses to nothing off Apple (
clang -E -U__APPLE__, base andhead equal) and is compiled only on Darwin. So the Windows and Linux host builds get no new code from this PR.
git merge-treewith main6c9edffeand with every open macOS PR (Reviewer notes).the packaged loader), above.
aa95d35f, at9d34f9a1(9e0b02f0before the rebases ontoe090cc48andaa95d35f):Sweep3's checks of this head (the C/C++ it changes compiled, and its own tests run, on arm64 and x86_64 where they
apply) gave the same results as before, but the admission test's ABI line, now
version 23, size 368, all 46 named offsets; Apple version 24, size 384, typed callbacks at 368/376(it carries #438; main's #729 took 23 andoptions_pathat 360);python -I -B tests/run_all.pyon wave Sh3's merge546e0b8d, which carries this head:577/32/18 (passed/failed/skipped) against main aa95d35f's 543/33/17 on this Mac, no new failure
(
reports/2026-11-15-rebase-sweep-3.md).Not tested:
#22 to #26 (MoltenVK through the packaged loader) were on integration builds that carry these commits.
measured on identical SPIR-V (KK-03), and the compiler-service handling keyed on it.
(
hos_main.canddarwin_vulkan.hthe same off Apple,darwin_vulkan.ccomments only and built only on Darwin; thenx2/darwin_*.pyfiles run only on macOS;reports/PubWave-files/offapple/kosmickrisp.txt); #479 and #438, which thisbranch carries, have their own test requests.
Must be tested:
LONGSHOT_VULKAN_LOADER(MoltenVK only), started to the title, expectingvk: Vulkan driver: MoltenVK (default; RX_VK_DRIVER=kosmickrisp to measure KosmicKrisp)and the title (by hand: a tester on #420 with such a Mac; the log's firstvk:lines).Risks
What could break: on macOS packages built with the loader, Vulkan now goes through the loader to MoltenVK instead
of loading MoltenVK directly (starts #21 to #26 ran that way). A system-wide implicit Vulkan layer would now be loaded,
as for any Vulkan app (none on this Mac). A
VK_DRIVER_FILESset by the user is honoured unlessRX_VK_DRIVERis set.A game folder holding a leftover driver or manifest is refused by the packager. With
RX_VK_DRIVER=kosmickrisp: run #20'sslow start, TexOrder's read order (above), and KosmicKrisp's
fillModeNonSolid0 (point and line polygon modes invalid;the mode census of run #21 saw only fill). Windows and Linux: nothing.
Turning it off: build without
LONGSHOT_VULKAN_LOADER(MoltenVK loaded directly, as before), or leaveRX_VK_DRIVERunset; otherwise revert the PR.Compatibility: nothing changes for Windows and Linux players. On macOS: no shader stamp, program cache or
MKWVK_API_VERSIONchange (this PR's commits touch nogpu/file); one new log line at startup. Switching to KosmicKrisp starts a new pipelinecache (it has its own
pipelineCacheUUID).Reviewer notes
12d1c5e2..9e0b02f0(5 commits, 9 files): the change (picked from61774ddb), its package fixture following#479's
darwin_toolchain(77255c6c), a Pyright type (picked from2ed98ef3), MoltenVK the default (picked from73ee181e) and the display fixture's counter gated to Apple (9e0b02f0).mac/build-complete, headba0afee2, which carries #453276787dband #4378348672c) and #438(
mac/vk-admission, head0f2262a3): this branch starts at #479's head with #438 merged (12d1c5e2, no conflict).#479 brings
nx2/darwin_build.py,nx2/darwin_package.pyandtests/test_macos_package.py; #438 bringshost/posix/darwin_vulkan.{c,h}andtests/test_vk_darwin_admission.py. Until they merge, this PR's diff shows theircommits too. Neither alone is enough (
git merge-treetrials of the pick on main, on #438, on #479 and on #490 eachleave files deleted on that side), and #490 (darwin-host) is not needed.
docs/development.md: the source branch's context carried theLONGSHOT_VK_PREBUILTrow (#479'sba0afee2removedit, as main has no prebuilt renderer) and #653's
RX_VK_METAL_CACHE_DIRrow; onlyLONGSHOT_VULKAN_LOADER,LONGSHOT_KOSMICKRISPandRX_VK_DRIVERare added.nx2/darwin_build.py: the same prebuilt-renderer block, not brought back.tests/display_startup_failure_test.cpp: main'shos_game_layout()stub kept; the source branch's #490 kept-startupstub left to #490; the driver stub and its check are this PR's.
tests/test_vendor_fixes.py: main's version kept. On main no vendor fix keys on MoltenVK's driver ID (the MoltenVKfixes and their columns come with #643 and #644), so a KosmicKrisp row would check
fetch_boundsalone; it goes withthe follow-up below.
fe64034akeysProfile::materialize_image_readsonVK_DRIVER_ID_MESA_KOSMICKRISPbeside MoltenVK's, with its fixture. It needs#675 (tex-read-order: the stored image reads and the barrier before image writes and atomics), which is not open yet,
so it is not in this PR; it follows once #675 exists. Until then KosmicKrisp is opt-in, and with
RX_VK_DRIVER=kosmickrispa texel read before an atomic in the same invocation can return the value after the atomic(TO-03's flag-off part on KosmicKrisp: 128 of 128 and 4 of 128 lanes;
reports/2026-11-14-kosmickrisp.mdsection 6).run #20 above; the compiler-service handling is keyed on MoltenVK only (
reports/2026-11-14-kk-slow.md; keying iton KosmicKrisp waits for an A/B start and probe KK-03); KosmicKrisp's
fillModeNonSolidis 0, so the game's point andline polygon modes are invalid there (an owner item: count first, then emulate; run #21's census saw fill only).
a65c707c…a547a6, 397,584,552 bytes, as LunarG'sfiles.json), unpacked user-local; nothing of it is in the repository, andthird_party/is untouched. The packagetakes the files only through the two build inputs: a MoltenVK-only build without them is unchanged.
6c9edffe: none (git merge-tree). With open macOS PRs: #490 (darwin-host,af75d5af) intests/display_startup_failure_test.cpp, its kept-startup stub and check beside this PR's driver stub and check:keep both (both inside
#if defined(__APPLE__)). #653 (metal-cache) indocs/development.md: itsRX_VK_METAL_CACHE_DIRrow after this PR'sRX_VK_DRIVERrow, keep both. #511, #436, #480 and #478 conflict inhost/posix/win32_posix.cthrough #438, which this branch carries (the same conflict as #438's own with them, notthis PR's change). Every other open macOS PR merges without a conflict (
merge-tree.txtin the evidence below).reports/PubWave-files/PubKk/(proof logs, merge-tree, range-diff, off-Apple text check), the run reportsnamed above and
reports/2026-11-14-kosmickrisp.md.Issues
See #420
Checklist
Everyone
type(scope): description) of the status line's type.main: onaa95d35f(#761, 2026-11-15), head9d34f9a1, on #438681e3708, on #4791bd269c7, with one conflict:host/hos_main.c, #729'shos_options_choosethen this PR's driver choice, bothbefore any thread; this PR's own commits, old → new:
12d1c5e2→cd4d074a,dfef2c1c→fbd56242,77255c6c→3472d670,908d6b1c→296b5347,ccb7e89b→86c017e4,9e0b02f0→9d34f9a1; the commit names elsewhere inthis description are from before the rebase.
python -I -B tests/run_all.pypasses (any failure that also fails onmainis named). (At9e0b02f0: 535 passed, 33 failed, 17 skipped against main 6c9edffe's 531/32/17 on this Mac: the 32 also fail on main;test_darwin_waits,test_macos_build_flags,test_macos_package,test_vk_darwin_admissionandtest_vk_darwin_sdlare new here (with #479 and #438) and pass;test_gpu_hang_watchfailed under a load of 32-44 and passes on its own at this head;reports/PubWave-files/PubKk/full-9e0b02f0/,hang-watch-rerun/. After the rebase onto mainaa95d35f(head9d34f9a1): wave Sh3's merge546e0b8d, 577/32/18 against main aa95d35f's 543/33/17, no new failure.)third_party/untouched (a new folder with aPROVENANCE.mdinstead).When it applies
Assisted-by:/Co-Authored-By:trailers.mainand passing here.tests/that reads changed source updated in this PR, not weakened.Carries the vk-admission pull request's commits, which the packaged driver choice needs (host/posix/darwin_vulkan.{c,h} and test_vk_darwin_admission). #479 (mac/build-complete, this branch's start) brings darwin_build.py and darwin_package.py; the two merge without a conflict. Assisted-by: Claude Opus 5.5 Co-Authored-By: Claude Opus 5.5 <[email protected]>WIP: feat(macos): ship the Vulkan loader with KosmicKrisp and MoltenVK, MoltenVK the defaultto feat(macos): ship the Vulkan loader with KosmicKrisp and MoltenVK, MoltenVK the default9e0b02f0e57872c6e71c7872c6e71c9d34f9a1c8Folded into #846 (feat(macos): Vulkan on Metal: pinned host imports, the unified-memory budget, Metal's cache folder and KosmicKrisp)
Pull request closed