feat(macos): ship the Vulkan loader with KosmicKrisp and MoltenVK, MoltenVK the default #654

Closed
mooki wants to merge 28 commits from mooki/Longshot:mac/kosmickrisp into main
Member

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), because
it 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, MoltenVK
and (arm64 builds with LONGSHOT_KOSMICKRISP) KosmicKrisp, each driver with a loader manifest beside it. At startup the
game points the loader at one packaged driver: MoltenVK, unless RX_VK_DRIVER=kosmickrisp asks for KosmicKrisp. Without
the loader input the package is exactly as before and MoltenVK is loaded directly. Everything new is Apple-only.

  • Driver choice: host/posix/darwin_vulkan.c hos_darwin_vulkan_driver() (a pure function) and
    hos_darwin_vulkan_choose_driver() (once, pthread_once): RX_VK_DRIVER=kosmickrisp or moltenvk with that
    driver's manifest packaged; else a VK_DRIVER_FILES or VK_ICD_FILENAMES the user set is left to the loader; else
    MoltenVK 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_FILES with setenv, so the loader never
    searches 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.h names the two
    manifests and documents the order.
  • Startup: host/hos_main.c main() calls the choice right after longshot_env.txt (so RX_VK_DRIVER can come from
    there) 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.
  • Packaging: nx2/darwin_package.py ICD_MANIFESTS, Plan.icd() and icd_manifest() write vulkan_moltenvk_icd.json
    and vulkan_kosmickrisp_icd.json beside their drivers with a relative library_path (the loader resolves it against
    the 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 other
    flat import is still refused. nx2/darwin_build.py dependencies() takes the two inputs: KosmicKrisp needs the loader
    and an arm64 build (it has no x86_64 slice).
  • Docs: docs/development.md, the LONGSHOT_VULKAN_LOADER and LONGSHOT_KOSMICKRISP build inputs and the
    RX_VK_DRIVER switch.
  • Tests: 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.py VulkanDriverPackage (5 tests: flat
    imports 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, after longshot_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 61774ddb and 2ed98ef3 (and fe64034a, the follow-up below), with both
drivers packaged. Run #20 had no RX_VK_DRIVER, and that build's rule then picked KosmicKrisp on macOS 26; run #21 chose
MoltenVK with RX_VK_DRIVER=moltenvk, the driver this PR's default (73ee181e) picks. One run each; this decides the
default, 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 both
Method: the game log's vk: lines, the runner's receipt and a process sampler; run reports reports/2026-11-14-mac-startup-20.md and -21.md
How to reproduce: a package built with LONGSHOT_VULKAN_LOADER and LONGSHOT_KOSMICKRISP from the SDK, started once with RX_VK_DRIVER=kosmickrisp and once without it

Before, KosmicKrisp by 09f9a171's macOS 26 rule: integration @ 09f9a171 (run #20) After, RX_VK_DRIVER=moltenvk (this PR's default): integration @ 09f9a171 (run #21)
shader preparation 1,607 of 38,809 in 1,770 s (0.9 a second), never finished 38,809 in 58.0 s
precompile hand-off not reached after 694 s, first present 5.0 s later
title and menu not reached reached
Metal's compiler service about 1,400% CPU throughout, 3 crashes, 3 interrupted connections 1,415% CPU at its peak, 6 crashes, all during the precompile
end clean, at the runner's quit device lost after the first A press (GPU memory, a separate macOS PR)
#20: [hos host] vk: Vulkan driver: KosmicKrisp (Apple silicon, macOS 26 or later; macOS 26.7.1): VK_DRIVER_FILES=…/game/vulkan_kosmickrisp_icd.json
#20: vk: selected driver: KosmicKrisp vulkan-sdk-1.4.363 (git-269a955c89)
#20: (last reading, 11:46:43) vk: start-up: 1607 of 38809
#21: [hos host] vk: Vulkan driver: MoltenVK (RX_VK_DRIVER=moltenvk; macOS 26.7.1): VK_DRIVER_FILES=…/game/vulkan_moltenvk_icd.json
#21: vk: prepared 38809 shader programs (46598 new, 69 failed, 0 compute pipelines; 9709 compute variants, 776 programs with texture variants, 59 at the limit) in 58.0 s

Most 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.md section 6), which say which
MoltenVK workaround it needs:

  • TexOrder TO-03 plus TO-01 with the fix off: 60 of 62 predictions. With materialize_image_reads off (KosmicKrisp's
    state 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).
  • MD-01 8 of 8 pipelines made and correct (MoltenVK cannot make two of them), MvkPush 12 of 12, NullRange 31 of 31,
    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.
  • kk01, the first freeze, was refused by every packet's own input check before any GPU work (a pinned tool hash had
    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.

Flag What it is Before this PR After this PR Effect Helps / doesn't help / untested Recommendation
LONGSHOT_VULKAN_LOADER macOS build input: LunarG's libvulkan.1.dylib not read optional, unset packaged with a manifest per driver; the game picks the driver at startup lets one package carry both drivers; MoltenVK through the loader ran starts #21 to #26 set it (with MoltenVK) for packages that should be able to measure KosmicKrisp
LONGSHOT_KOSMICKRISP macOS build input: the SDK's libvulkan_kosmickrisp.dylib not read optional, unset; arm64 builds only, needs the loader packages KosmicKrisp and its manifest only for measuring KosmicKrisp: not good yet (run #20) arm64 measurement builds only
RX_VK_DRIVER macOS switch: kosmickrisp or moltenvk none unset: MoltenVK the packaged driver the loader is given kosmickrisp: measurement only (run #20 did not reach the title) unset

Testing

Tested: on macOS 26.7.1, Apple M4 Max, at 9e0b02f0, this PR's head before the rebases onto e090cc48 and aa95d35f
(main 6c9edffe with #479 and #438 merged; the head runs were at 346b11c8, the same tree 832d8dc8 before a commit
message was reworded). Each test also ran against the base 12d1c5e2 (an archive of #479 + #438 without this PR) and
fails there. Logs: reports/PubWave-files/PubKk/proof/.

  • tests/test_vk_darwin_admission.py --arch arm64 and --arch x86_64 (Rosetta): exit 0 on both, "Darwin packaged Vulkan
    driver 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 the
    27 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 5 VulkanDriverPackage tests
    (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, no Plan.icd, no flat admission). The pick before
    77255c6c: 1 error (the fixture patched toolchain.architecture, which #479 moved to darwin_toolchain).
  • tests/test_display_startup_failure.py, arm64 and x86_64 (the fixture compiled with clang++ -arch x86_64, run under
    Rosetta: proc_translated=1): 11 cases pass on both. Against the base's host/hos_main.c (--source-root): all 11
    fail on both, "packaged Vulkan driver not chosen once at startup".
  • Related, unchanged by this PR: tests/test_vk_darwin_sdl.py --arch arm64 and --arch x86_64 (16 cases, pass) and
    tests/test_vendor_fixes.py (16 devices, pass).
  • host/posix/darwin_vulkan.c compiled with nx2/build.py's own host command for arm64 and x86_64, and again with
    -Wall -Wextra -Werror: 0 warnings; the objects (arm64 542c78e7…, x86_64 ecfd6b3b…) are byte-identical to the
    source branch's at 73ee181e.
  • Off Apple: every C and C++ file this PR's commits change (host/hos_main.c, host/posix/darwin_vulkan.h,
    tests/display_startup_failure_test.cpp) is main's text after unifdef -U__APPLE__; host/posix/darwin_vulkan.c
    differs from #438's only in its opening comment, preprocesses to nothing off Apple (clang -E -U__APPLE__, base and
    head equal) and is compiled only on Darwin. So the Windows and Linux host builds get no new code from this PR.
  • git merge-tree with main 6c9edffe and with every open macOS PR (Reviewer notes).
  • In the game, on integration builds that carry these commits: runs #20 (KosmicKrisp) and #21 to #26 (MoltenVK through
    the packaged loader), above.
  • After the rebase onto main aa95d35f, at 9d34f9a1 (9e0b02f0 before the rebases onto e090cc48 and aa95d35f):
    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 and
    options_path at 360); python -I -B tests/run_all.py on wave Sh3's merge 546e0b8d, 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:

  • The game at this exact head: main does not run on macOS without the other macOS PRs. The game runs above and starts
    #22 to #26 (MoltenVK through the packaged loader) were on integration builds that carry these commits.
  • KosmicKrisp with TexOrder's stored image reads (#675 and the follow-up that keys them on KosmicKrisp), its compile cost
    measured on identical SPIR-V (KK-03), and the compiler-service handling keyed on it.
  • macOS 14.4 and 15 (this Mac runs macOS 26): the choice there is covered by the fixture's cases only.
  • Windows and Linux: not built or run, and they need nothing: this PR's own range compiles there as main's text
    (hos_main.c and darwin_vulkan.h the same off Apple, darwin_vulkan.c comments only and built only on Darwin; the
    nx2/darwin_*.py files run only on macOS; reports/PubWave-files/offapple/kosmickrisp.txt); #479 and #438, which this
    branch carries, have their own test requests.

Must be tested:

  • macOS 14.4 or 15, Apple silicon or Intel: a package built with LONGSHOT_VULKAN_LOADER (MoltenVK only), started to the title, expecting vk: 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 first vk: 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_FILES set by the user is honoured unless RX_VK_DRIVER is set.
A game folder holding a leftover driver or manifest is refused by the packager. With RX_VK_DRIVER=kosmickrisp: run #20's
slow start, TexOrder's read order (above), and KosmicKrisp's fillModeNonSolid 0 (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 leave
RX_VK_DRIVER unset; otherwise revert the PR.

Compatibility: nothing changes for Windows and Linux players. On macOS: no shader stamp, program cache or
MKWVK_API_VERSION change (this PR's commits touch no gpu/ file); one new log line at startup. Switching to KosmicKrisp starts a new pipeline
cache (it has its own pipelineCacheUUID).

Reviewer notes

  • Review 12d1c5e2..9e0b02f0 (5 commits, 9 files): the change (picked from 61774ddb), its package fixture following
    #479's darwin_toolchain (77255c6c), a Pyright type (picked from 2ed98ef3), MoltenVK the default (picked from
    73ee181e) and the display fixture's counter gated to Apple (9e0b02f0).
  • Depends on #479 (mac/build-complete, head ba0afee2, which carries #453 276787db and #437 8348672c) and #438
    (mac/vk-admission, head 0f2262a3): this branch starts at #479's head with #438 merged (12d1c5e2, no conflict).
    #479 brings nx2/darwin_build.py, nx2/darwin_package.py and tests/test_macos_package.py; #438 brings
    host/posix/darwin_vulkan.{c,h} and tests/test_vk_darwin_admission.py. Until they merge, this PR's diff shows their
    commits too. Neither alone is enough (git merge-tree trials of the pick on main, on #438, on #479 and on #490 each
    leave files deleted on that side), and #490 (darwin-host) is not needed.
  • Conflicts in the pick, all resolved by keeping main's (and #479's) lines and adding only this PR's:
    • docs/development.md: the source branch's context carried the LONGSHOT_VK_PREBUILT row (#479's ba0afee2 removed
      it, as main has no prebuilt renderer) and #653's RX_VK_METAL_CACHE_DIR row; only LONGSHOT_VULKAN_LOADER,
      LONGSHOT_KOSMICKRISP and RX_VK_DRIVER are added.
    • nx2/darwin_build.py: the same prebuilt-renderer block, not brought back.
    • tests/display_startup_failure_test.cpp: main's hos_game_layout() stub kept; the source branch's #490 kept-startup
      stub 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 MoltenVK
      fixes and their columns come with #643 and #644), so a KosmicKrisp row would check fetch_bounds alone; it goes with
      the follow-up below.
  • Follow-up: KosmicKrisp in TexOrder's driver list. The source branch's fe64034a keys
    Profile::materialize_image_reads on VK_DRIVER_ID_MESA_KOSMICKRISP beside 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=kosmickrisp a 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.md section 6).
  • Why KosmicKrisp stays opt-in (the owner's rule: measure both, ship both, default to the one that measures good):
    run #20 above; the compiler-service handling is keyed on MoltenVK only (reports/2026-11-14-kk-slow.md; keying it
    on KosmicKrisp waits for an A/B start and probe KK-03); KosmicKrisp's fillModeNonSolid is 0, so the game's point and
    line polygon modes are invalid there (an owner item: count first, then emulate; run #21's census saw fill only).
  • The SDK: LunarG's macOS Vulkan SDK 1.4.363.1 (zip SHA-256 a65c707c…a547a6, 397,584,552 bytes, as LunarG's
    files.json), unpacked user-local; nothing of it is in the repository, and third_party/ is untouched. The package
    takes the files only through the two build inputs: a MoltenVK-only build without them is unchanged.
  • Conflicts with main 6c9edffe: none (git merge-tree). With open macOS PRs: #490 (darwin-host, af75d5af) in
    tests/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) in docs/development.md: its
    RX_VK_METAL_CACHE_DIR row after this PR's RX_VK_DRIVER row, keep both. #511, #436, #480 and #478 conflict in
    host/posix/win32_posix.c through #438, which this branch carries (the same conflict as #438's own with them, not
    this PR's change). Every other open macOS PR merges without a conflict (merge-tree.txt in the evidence below).
  • Evidence: reports/PubWave-files/PubKk/ (proof logs, merge-tree, range-diff, off-Apple text check), the run reports
    named above and reports/2026-11-14-kosmickrisp.md.

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. (No test request: nothing changes for Windows or Linux; the macOS item is by hand.)
  • Rebased on current main: on aa95d35f (#761, 2026-11-15), head 9d34f9a1, on #438 681e3708, on #479
    1bd269c7, with one conflict: host/hos_main.c, #729's hos_options_choose then this PR's driver choice, both
    before 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 in
    this description are from before the rebase.
  • python -I -B tests/run_all.py passes (any failure that also fails on main is named). (At 9e0b02f0: 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_admission and test_vk_darwin_sdl are new here (with #479 and #438) and pass; test_gpu_hang_watch failed 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 main aa95d35f (head 9d34f9a1): wave Sh3's merge 546e0b8d, 577/32/18 against main aa95d35f's 543/33/17, no new failure.)
  • 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.
  • Switches documented (docs/development.md; the README for a menu row) and shipped at their recommended value.
  • A game-free test added for the fix, failing on main and passing here.
  • A check under tests/ that reads changed source updated in this PR, not weakened.
  • Docs updated.
**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`), because it 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, MoltenVK and (arm64 builds with `LONGSHOT_KOSMICKRISP`) KosmicKrisp, each driver with a loader manifest beside it. At startup the game points the loader at one packaged driver: MoltenVK, unless `RX_VK_DRIVER=kosmickrisp` asks for KosmicKrisp. Without the loader input the package is exactly as before and MoltenVK is loaded directly. Everything new is Apple-only. - Driver choice: `host/posix/darwin_vulkan.c` `hos_darwin_vulkan_driver()` (a pure function) and `hos_darwin_vulkan_choose_driver()` (once, `pthread_once`): `RX_VK_DRIVER=kosmickrisp` or `moltenvk` with that driver's manifest packaged; else a `VK_DRIVER_FILES` or `VK_ICD_FILENAMES` the user set is left to the loader; else MoltenVK 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_FILES` with `setenv`, so the loader never searches 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.h` names the two manifests and documents the order. - Startup: `host/hos_main.c` `main()` calls the choice right after `longshot_env.txt` (so `RX_VK_DRIVER` can come from there) 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. - Packaging: `nx2/darwin_package.py` `ICD_MANIFESTS`, `Plan.icd()` and `icd_manifest()` write `vulkan_moltenvk_icd.json` and `vulkan_kosmickrisp_icd.json` beside their drivers with a relative `library_path` (the loader resolves it against the 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 other flat import is still refused. `nx2/darwin_build.py` `dependencies()` takes the two inputs: KosmicKrisp needs the loader and an arm64 build (it has no x86_64 slice). - Docs: `docs/development.md`, the `LONGSHOT_VULKAN_LOADER` and `LONGSHOT_KOSMICKRISP` build inputs and the `RX_VK_DRIVER` switch. - Tests: `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.py` `VulkanDriverPackage` (5 tests: flat imports 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, after `longshot_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 `61774ddb` and `2ed98ef3` (and `fe64034a`, the follow-up below), with both drivers packaged. Run #20 had no `RX_VK_DRIVER`, and that build's rule then picked KosmicKrisp on macOS 26; run #21 chose MoltenVK with `RX_VK_DRIVER=moltenvk`, the driver this PR's default (`73ee181e`) picks. One run each; this decides the default, 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 both Method: the game log's `vk:` lines, the runner's receipt and a process sampler; run reports `reports/2026-11-14-mac-startup-20.md` and `-21.md` How to reproduce: a package built with `LONGSHOT_VULKAN_LOADER` and `LONGSHOT_KOSMICKRISP` from the SDK, started once with `RX_VK_DRIVER=kosmickrisp` and once without it | | Before, KosmicKrisp by 09f9a171's macOS 26 rule: integration @ 09f9a171 (run #20) | After, RX_VK_DRIVER=moltenvk (this PR's default): integration @ 09f9a171 (run #21) | |---|---|---| | shader preparation | 1,607 of 38,809 in 1,770 s (0.9 a second), never finished | 38,809 in 58.0 s | | precompile hand-off | not reached | after 694 s, first present 5.0 s later | | title and menu | not reached | reached | | Metal's compiler service | about 1,400% CPU throughout, 3 crashes, 3 interrupted connections | 1,415% CPU at its peak, 6 crashes, all during the precompile | | end | clean, at the runner's quit | device lost after the first A press (GPU memory, a separate macOS PR) | ``` #20: [hos host] vk: Vulkan driver: KosmicKrisp (Apple silicon, macOS 26 or later; macOS 26.7.1): VK_DRIVER_FILES=…/game/vulkan_kosmickrisp_icd.json #20: vk: selected driver: KosmicKrisp vulkan-sdk-1.4.363 (git-269a955c89) #20: (last reading, 11:46:43) vk: start-up: 1607 of 38809 #21: [hos host] vk: Vulkan driver: MoltenVK (RX_VK_DRIVER=moltenvk; macOS 26.7.1): VK_DRIVER_FILES=…/game/vulkan_moltenvk_icd.json #21: vk: prepared 38809 shader programs (46598 new, 69 failed, 0 compute pipelines; 9709 compute variants, 776 programs with texture variants, 59 at the limit) in 58.0 s ``` Most 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.md` section 6), which say which MoltenVK workaround it needs: - TexOrder TO-03 plus TO-01 with the fix off: 60 of 62 predictions. With `materialize_image_reads` off (KosmicKrisp's state 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). - MD-01 8 of 8 pipelines made and correct (MoltenVK cannot make two of them), MvkPush 12 of 12, NullRange 31 of 31, 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. - kk01, the first freeze, was refused by every packet's own input check before any GPU work (a pinned tool hash had 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. | Flag | What it is | Before this PR | After this PR | Effect | Helps / doesn't help / untested | Recommendation | |---|---|---|---|---|---|---| | `LONGSHOT_VULKAN_LOADER` | macOS build input: LunarG's `libvulkan.1.dylib` | not read | optional, unset | packaged with a manifest per driver; the game picks the driver at startup | lets one package carry both drivers; MoltenVK through the loader ran starts #21 to #26 | set it (with MoltenVK) for packages that should be able to measure KosmicKrisp | | `LONGSHOT_KOSMICKRISP` | macOS build input: the SDK's `libvulkan_kosmickrisp.dylib` | not read | optional, unset; arm64 builds only, needs the loader | packages KosmicKrisp and its manifest | only for measuring KosmicKrisp: not good yet (run #20) | arm64 measurement builds only | | `RX_VK_DRIVER` | macOS switch: `kosmickrisp` or `moltenvk` | none | unset: MoltenVK | the packaged driver the loader is given | `kosmickrisp`: measurement only (run #20 did not reach the title) | unset | ## Testing **Tested:** on macOS 26.7.1, Apple M4 Max, at `9e0b02f0`, this PR's head before the rebases onto e090cc48 and aa95d35f (main 6c9edffe with #479 and #438 merged; the head runs were at 346b11c8, the same tree `832d8dc8` before a commit message was reworded). Each test also ran against the base `12d1c5e2` (an archive of #479 + #438 without this PR) and fails there. Logs: `reports/PubWave-files/PubKk/proof/`. - `tests/test_vk_darwin_admission.py --arch arm64` and `--arch x86_64` (Rosetta): exit 0 on both, "Darwin packaged Vulkan driver 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 the 27 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 5 `VulkanDriverPackage` tests (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`, no `Plan.icd`, no `flat` admission). The pick before `77255c6c`: 1 error (the fixture patched `toolchain.architecture`, which #479 moved to `darwin_toolchain`). - `tests/test_display_startup_failure.py`, arm64 and x86_64 (the fixture compiled with `clang++ -arch x86_64`, run under Rosetta: `proc_translated=1`): 11 cases pass on both. Against the base's `host/hos_main.c` (`--source-root`): all 11 fail on both, "packaged Vulkan driver not chosen once at startup". - Related, unchanged by this PR: `tests/test_vk_darwin_sdl.py --arch arm64` and `--arch x86_64` (16 cases, pass) and `tests/test_vendor_fixes.py` (16 devices, pass). - `host/posix/darwin_vulkan.c` compiled with `nx2/build.py`'s own host command for arm64 and x86_64, and again with `-Wall -Wextra -Werror`: 0 warnings; the objects (arm64 `542c78e7…`, x86_64 `ecfd6b3b…`) are byte-identical to the source branch's at `73ee181e`. - Off Apple: every C and C++ file this PR's commits change (`host/hos_main.c`, `host/posix/darwin_vulkan.h`, `tests/display_startup_failure_test.cpp`) is main's text after `unifdef -U__APPLE__`; `host/posix/darwin_vulkan.c` differs from #438's only in its opening comment, preprocesses to nothing off Apple (`clang -E -U__APPLE__`, base and head equal) and is compiled only on Darwin. So the Windows and Linux host builds get no new code from this PR. - `git merge-tree` with main 6c9edffe and with every open macOS PR (Reviewer notes). - In the game, on integration builds that carry these commits: runs #20 (KosmicKrisp) and #21 to #26 (MoltenVK through the packaged loader), above. - **After the rebase onto main aa95d35f**, at `9d34f9a1` (`9e0b02f0` before the rebases onto e090cc48 and aa95d35f): 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 and `options_path` at 360); `python -I -B tests/run_all.py` on wave Sh3's merge `546e0b8d`, 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:** - The game at this exact head: main does not run on macOS without the other macOS PRs. The game runs above and starts #22 to #26 (MoltenVK through the packaged loader) were on integration builds that carry these commits. - KosmicKrisp with TexOrder's stored image reads (#675 and the follow-up that keys them on KosmicKrisp), its compile cost measured on identical SPIR-V (KK-03), and the compiler-service handling keyed on it. - macOS 14.4 and 15 (this Mac runs macOS 26): the choice there is covered by the fixture's cases only. - Windows and Linux: not built or run, and they need nothing: this PR's own range compiles there as main's text (`hos_main.c` and `darwin_vulkan.h` the same off Apple, `darwin_vulkan.c` comments only and built only on Darwin; the `nx2/darwin_*.py` files run only on macOS; `reports/PubWave-files/offapple/kosmickrisp.txt`); #479 and #438, which this branch carries, have their own test requests. **Must be tested:** - macOS 14.4 or 15, Apple silicon or Intel: a package built with `LONGSHOT_VULKAN_LOADER` (MoltenVK only), started to the title, expecting `vk: 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 first `vk:` 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_FILES` set by the user is honoured unless `RX_VK_DRIVER` is set. A game folder holding a leftover driver or manifest is refused by the packager. With `RX_VK_DRIVER=kosmickrisp`: run #20's slow start, TexOrder's read order (above), and KosmicKrisp's `fillModeNonSolid` 0 (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 leave `RX_VK_DRIVER` unset; otherwise revert the PR. **Compatibility:** nothing changes for Windows and Linux players. On macOS: no shader stamp, program cache or `MKWVK_API_VERSION` change (this PR's commits touch no `gpu/` file); one new log line at startup. Switching to KosmicKrisp starts a new pipeline cache (it has its own `pipelineCacheUUID`). ## Reviewer notes - Review `12d1c5e2..9e0b02f0` (5 commits, 9 files): the change (picked from `61774ddb`), its package fixture following #479's `darwin_toolchain` (`77255c6c`), a Pyright type (picked from `2ed98ef3`), MoltenVK the default (picked from `73ee181e`) and the display fixture's counter gated to Apple (`9e0b02f0`). - Depends on #479 (`mac/build-complete`, head `ba0afee2`, which carries #453 `276787db` and #437 `8348672c`) and #438 (`mac/vk-admission`, head `0f2262a3`): this branch starts at #479's head with #438 merged (`12d1c5e2`, no conflict). #479 brings `nx2/darwin_build.py`, `nx2/darwin_package.py` and `tests/test_macos_package.py`; #438 brings `host/posix/darwin_vulkan.{c,h}` and `tests/test_vk_darwin_admission.py`. Until they merge, this PR's diff shows their commits too. Neither alone is enough (`git merge-tree` trials of the pick on main, on #438, on #479 and on #490 each leave files deleted on that side), and #490 (darwin-host) is not needed. - Conflicts in the pick, all resolved by keeping main's (and #479's) lines and adding only this PR's: - `docs/development.md`: the source branch's context carried the `LONGSHOT_VK_PREBUILT` row (#479's `ba0afee2` removed it, as main has no prebuilt renderer) and #653's `RX_VK_METAL_CACHE_DIR` row; only `LONGSHOT_VULKAN_LOADER`, `LONGSHOT_KOSMICKRISP` and `RX_VK_DRIVER` are added. - `nx2/darwin_build.py`: the same prebuilt-renderer block, not brought back. - `tests/display_startup_failure_test.cpp`: main's `hos_game_layout()` stub kept; the source branch's #490 kept-startup stub 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 MoltenVK fixes and their columns come with #643 and #644), so a KosmicKrisp row would check `fetch_bounds` alone; it goes with the follow-up below. - **Follow-up: KosmicKrisp in TexOrder's driver list.** The source branch's `fe64034a` keys `Profile::materialize_image_reads` on `VK_DRIVER_ID_MESA_KOSMICKRISP` beside 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=kosmickrisp` a 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.md` section 6). - Why KosmicKrisp stays opt-in (the owner's rule: measure both, ship both, default to the one that measures good): run #20 above; the compiler-service handling is keyed on MoltenVK only (`reports/2026-11-14-kk-slow.md`; keying it on KosmicKrisp waits for an A/B start and probe KK-03); KosmicKrisp's `fillModeNonSolid` is 0, so the game's point and line polygon modes are invalid there (an owner item: count first, then emulate; run #21's census saw fill only). - The SDK: LunarG's macOS Vulkan SDK 1.4.363.1 (zip SHA-256 `a65c707c…a547a6`, 397,584,552 bytes, as LunarG's `files.json`), unpacked user-local; nothing of it is in the repository, and `third_party/` is untouched. The package takes the files only through the two build inputs: a MoltenVK-only build without them is unchanged. - Conflicts with main 6c9edffe: none (`git merge-tree`). With open macOS PRs: #490 (darwin-host, `af75d5af`) in `tests/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) in `docs/development.md`: its `RX_VK_METAL_CACHE_DIR` row after this PR's `RX_VK_DRIVER` row, keep both. #511, #436, #480 and #478 conflict in `host/posix/win32_posix.c` through #438, which this branch carries (the same conflict as #438's own with them, not this PR's change). Every other open macOS PR merges without a conflict (`merge-tree.txt` in the evidence below). - Evidence: `reports/PubWave-files/PubKk/` (proof logs, merge-tree, range-diff, off-Apple text check), the run reports named above and `reports/2026-11-14-kosmickrisp.md`. ## 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. - [x] Testing names what was tested, what wasn't, and what must still be tested, where and how. (No test request: nothing changes for Windows or Linux; the macOS item is by hand.) - [x] Rebased on current `main`: on aa95d35f (#761, 2026-11-15), head `9d34f9a1`, on #438 `681e3708`, on #479 `1bd269c7`, with one conflict: `host/hos_main.c`, #729's `hos_options_choose` then this PR's driver choice, both before 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 in this description are from before the rebase. - [x] `python -I -B tests/run_all.py` passes (any failure that also fails on `main` is named). (At `9e0b02f0`: 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_admission` and `test_vk_darwin_sdl` are new here (with #479 and #438) and pass; `test_gpu_hang_watch` failed 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 main aa95d35f (head `9d34f9a1`): wave Sh3's merge `546e0b8d`, 577/32/18 against main aa95d35f's 543/33/17, no new failure.) - [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. - [x] Switches documented (docs/development.md; the README for a menu row) and shipped at their recommended value. - [x] A game-free test added for the fix, failing on `main` and passing here. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. - [x] Docs updated.
Add macOS 14.4 public address waits, awake-uptime deadlines, relative
monitor waits, absolute timer sleeps, and real initialization for zeroed
SRW locks. Keep the Linux source behind each Apple branch byte-identical.

Exercise the extracted layer with real primitives and C/C++ header clients
on arm64 and x86_64. Document the existing broadcast address wakes, timer
restrictions and non-atomic polling, without claiming a complete host port.

Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra <[email protected]>
Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The pointer mailbox test uses the real POSIX SRW wrappers, which now call
into darwin_wait.c. Compile that production C dependency and link it into
the existing native-default fixture on macOS only. Keep the Linux and
Windows compiler invocation and every existing assertion unchanged.

The full-suite failure at 1da4f9b3 is retained. This repair does not resolve
the held cross-image address-wait contract decision or qualify the port.

Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra <[email protected]>
Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Move the address sequence words into an executable-only provider and resolve
that canonical service once in each shim image. Fail closed when the host
provider is absent; retain full-width comparisons and broadcast ordering.

Extend the existing wait driver with real host/two-dylib behavior coverage,
including upper-half and cross-word changes, same-image controls, image-local
error state, broadcast wakes, and rejection of a dylib-only provider. Keep
the existing timing campaign intact and independently selectable.

Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra <[email protected]>
Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
docs(runtime): describe the executable-owned macOS address-wait state
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
Website / Website on Cloudflare (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 3s
CI / Workflow lint (actionlint) (pull_request) Successful in 4s
Third-party parts / As their notes say (pull_request) Successful in 8s
PR description / Description follows the template (pull_request_target) Successful in 4s
8348672cfa
The wait section still said each image's darwin_wait.c is self-contained.
Since the address sequence words moved into the executable, the renderer's
copy finds them through RTLD_MAIN_ONLY, a missing provider aborts, and the
build has to link darwin_wait_state.c into the host only. Say so, and name
the test's --shared-only mode.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Select a local Vulkan loader or MoltenVK with RX_VK_LIBRARY and keep SDL and
renderer startup on that same library. Capture SDL's instance extensions on
the window thread, enable advertised portability extensions, and name missing
admission requirements and VkResults without changing Windows or Linux paths.

Keep the non-Apple host API at 21 for existing prebuilt renderers; append the
two macOS callbacks only in Apple's version 22. Cover pure decisions, legacy
platform expectations, ABI layout, and the SDL2/SDL3 bridge without a GPU.

Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra <[email protected]>
Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Remove the SDL source-string wiring and source-index ordering assertions while
keeping the sixteen compiled behavioral cases and surface adapter compile.
Replace the comment-sensitive API header hash with fixed baseline field names,
byte offsets, versions and sizes, retaining typed Apple callback checks and
both platforms' actual admission decisions and negative controls.

Production code is unchanged. Prior full-suite and device receipts remain
bound to 818e33b1; only the affected game-free tests were rerun.

Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra <[email protected]>
Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The fixture cuts LoadLibraryA and create_device out of main's source. Since
#34, LoadLibraryA keeps dlerror()'s text in load_error, and since #191
native_fadd_wanted also takes the device ID and driver version, so the cut
code no longer compiled against the fixture's stand-ins on this base. Give
it load_error and a dlerror stand-in, and the four-argument stand-in. No
expectation changed.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
test(vk): pin the admission ABI at main's version 22 and macOS 23
All checks were successful
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
CI / Conventional Commit title (pull_request) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 5s
Third-party parts / As their notes say (pull_request) Successful in 10s
PR description / Description follows the template (pull_request_target) Successful in 4s
0f2262a378
Main's custom output size (#51) took MKWVK_API_VERSION 22 on every
platform and appended take_text at offset 352. The macOS loader
callbacks now follow it: the rebased header says 23 on Apple and 22
elsewhere. The fixed contract follows: non-Apple version 22, size 360
and 45 named offsets (take_text added); Apple version 23, size 376,
vulkan_library and vulkan_extensions at 360 and 368.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Separate macOS from Linux, select arm64 or x86_64 without changing the
Windows/Linux command arrays, and discover LLVM/Apple tools through the
existing prefix, PATH and xcrun. Use the Apple SDK, a 14.4 deployment
target, Mach-O export/map/order/rpath options and system libc++.

Keep the runtime port outside this change. Pick up Darwin host helpers,
exclude the Linux sampler, and test command construction, prefix quoting,
missing tools and real C/C++ dylib loading on native ARM and Rosetta.
The a64-kit adaptation and refreshed tree digest are recorded in its
PROVENANCE.md. No game or generated game content is included.

Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra <[email protected]>
Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Do not require Rosetta in an ordinary Apple Silicon game-free suite or
skip all native x86_64 evidence on Intel. Keep both simulated target
contracts everywhere and add repeatable --arch options for explicit
dual-route qualification. Keep every binding, export, symbol-order,
C++ runtime and undefined-symbol assertion and all production flags.

Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra <[email protected]>
Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Use canonical POSIX paths only for the virtual /fixture command inputs.
Keep host paths for real discovery, temporary-directory helpers and real
Mach-O runs, and patch exists on the selected namespace path object.

Exercise the real frozen-array comparison under an ntpath host view for
all six configurations. Leave the baseline JSON and production flags
unchanged.

Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra <[email protected]>
Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
CONTRIBUTING keeps third_party/ as its upstream copies. Restore a64-kit's
toolchain.py, build_emit.py and PROVENANCE.md to main's bytes and move
their macOS behaviour into nx2/darwin_toolchain.py, an LS file.

On macOS, kit.py and make_game.py install it: toolchain's find, need,
native_target and native_link become its own (tools from EMSDK, PATH,
then xcrun; -arch, a 14.4 target and Apple's SDK; a Mach-O linker), and
build_emit builds liba64emit-<process arch>.dylib, rebuilt when the
compiler, its version or the flags change, as nx2/insn.py overrides
a64emit.translate. system(), architecture(), darwin_sdk() and version()
move with it; build.py, make_game.py, darwin_build.py, host_cpu.py and
zbic.py call them there. Windows and Linux never import the file.

kit.py's import makes the new file one of the lift's sources
(make_game.py lift_sources), so every platform lifts the game again
once, as the a64-kit edit did; build_emit is imported by name for that
walk (tests/test_lift_cache.py).

The command contracts now load main's own toolchain.py, so the 36
Windows/Linux arrays are compared with a64-kit untouched. A new check
proves kit.py and make_game.py install the functions on a Mac. The
test_cpm_discovery stub no longer needs toolchain.system().

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
build(macos): follow main's one-way renderer build and CPU target in the moved build code
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
Website / Website on Cloudflare (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 7s
Third-party parts / As their notes say (pull_request) Successful in 9s
PR description / Description follows the template (pull_request_target) Successful in 4s
276787db6f
Rebased on main 6c9edffe, three of main's changes meet code this branch
moved or added:

- The renderer link (#569): RX_VK_PGO=gen links the Windows back end
  without clang's profile library. renderer_link, which this branch made
  from main's inline link, now says so too, with main's comments.
- No prebuilt renderer (#574): build.prebuilt_renderer() and
  LONGSHOT_VK_PREBUILT are gone, so the macOS preflight checks the
  Vulkan headers always, not a prebuilt dylib's Mach-O slice.
- -march=native and no -mtune (#524): host_mtune is gone. The command
  contract loads host_march and native_cpu, and its frozen Windows/Linux
  arrays are recaptured from main 6c9edffe with --capture-main: the same
  arrays without -mtune=znver2.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This PR needs the macOS waits PR (#437); its commits are kept as they are.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Classify Darwin image-local helpers separately from executable-owned wait,
guest-memory, profiler, CPU, control and SDL/Vulkan state. Fail on unknown
helpers instead of broad-globbing host dependencies into the renderer.

Resolve and inspect exact target/minimum-OS dependency inputs before the
standalone build. Relocate and ad-hoc sign only owned copies, preserve the
SDL3 compatibility alias, and deploy the complete hash-bound output manifest.
Qualify otherwise unstamped NASM members only through bounded source/tool,
member/import/ISA evidence and an exact semantic-review receipt.

Keep strict two-level undefined-error links and the frozen Windows/Linux
command arrays. Retain real negative weak-link evidence and verify the real
canonical wait provider with production host flags on arm64 and Rosetta.
Document carried evidence and unproved full-game/dependency qualification.
No experiment-specific lifted-source reuse subsystem or third-party edits.

Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra <[email protected]>
Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
SDL2-compat dlopens SDL3 by file name and its own build carries only an
@loader_path rpath. Require LONGSHOT_SDL3 and its internal alias whenever
the selected SDL2 file names SDL3, not only when an rpath spells sdl3; a
classic SDL2 that names no SDL3 still needs none.

The strict-import check read only LC_SYMTAB ordinals. Also decode the
binding dyld applies: LC_DYLD_INFO bind, weak-bind and lazy-bind opcodes
and the LC_DYLD_CHAINED_FIXUPS import table (all three import formats).
Flat-lookup or main-executable imports, and library ordinals that name no
loaded dylib, refuse. C++ weak-definition coalescing (-3) stays two-level.

Validate ar member sizes as plain in-archive decimals before special
members are handled, which also ends the negative-size infinite loop.
A Mach-O member's object is the extent its own load commands describe;
only fewer than eight trailing newlines (Apple ar framing) may follow,
and member SHA-256 covers the object bytes, equal to the original NASM
output. Unknown file-bearing load commands and fat members refuse.

Only alias entries in longshot-macos.json may be symlinks. The maker
clears a dangling staged .new alias with lexists on macOS, so a retry
after a stopped first SDL3 install recovers. Windows/Linux staging is
unchanged.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
dyld keeps the library ordinal across a lazy-stream DONE; DONE only ends
the entry. The reader reset it to 0, so a lazy entry without its own
SET_DYLIB after a flat-lookup (-2) entry was checked as a self import
and admitted while dyld binds it flat. Clear only the symbol on a lazy
DONE and keep the ordinal, with that exact stream as a regression.

Correct the wording for ordinal -3 (BIND_SPECIAL_DYLIB_WEAK_LOOKUP): it
is not two-level. dyld resolves it by a process-wide, load-order search
among images with weak definitions. It stays admitted because ld emits
it only for weak definitions it resolved while linking. The previous
commit message's "stays two-level" was wrong in the same way.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The macOS build flags PR moved system(), architecture(), darwin_sdk()
and version() from a64-kit's toolchain.py into nx2/darwin_toolchain.py,
which main's toolchain.py does not have. The dependency plan and the
post-link check still asked toolchain for them, so on a Mac
dependencies() and post_link() stopped with AttributeError. Ask
darwin_toolchain there, and point the package and preflight tests'
stand-ins at it (the preflight's scope gets _darwin, as make_game.py
names it).

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
build(macos): no prebuilt renderer on macOS either, as main has none
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
Website / Website on Cloudflare (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 3s
CI / Workflow lint (actionlint) (pull_request) Successful in 6s
Third-party parts / As their notes say (pull_request) Successful in 8s
PR description / Description follows the template (pull_request_target) Successful in 4s
ba0afee28d
Main builds the Vulkan back end in every build, the AppImage's too, and
removed build.prebuilt_renderer() and LONGSHOT_VK_PREBUILT (#574). The
macOS packaging this branch adds still took an optional prebuilt
longshot_vk.dylib through them. It goes the same way: darwin_build's
dependencies() admits no renderer input, package() always adds the
renderer the build linked, the preflight always checks the Vulkan
headers, and the variable leaves the macOS dependency table.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
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]>
With LONGSHOT_VULKAN_LOADER (LunarG's macOS SDK loader) the package carries the loader, MoltenVK and, on arm64 builds
with LONGSHOT_KOSMICKRISP, KosmicKrisp, each driver with a loader manifest beside it whose library_path is relative to
the manifest's folder. At startup, after longshot_env.txt and before any thread, the game points VK_DRIVER_FILES at
one packaged manifest: KosmicKrisp on Apple silicon with macOS 26 or later (it needs Metal 4), MoltenVK otherwise
(Rosetta, macOS 14 and 15). RX_VK_DRIVER=kosmickrisp|moltenvk chooses for measurement; a VK_DRIVER_FILES or
VK_ICD_FILENAMES already set is left to the loader. No system loader or driver is searched while a packaged driver is
there. Without the loader the package is as before: MoltenVK loaded directly.

KosmicKrisp is linked with flat (dynamic-lookup) imports: Mesa's weak dispatch entrypoints and CoreGraphics' colour
spaces. The packager admits exactly those for that one input (FLAT_IMPORTS["kosmickrisp"]) and keeps refusing every
other flat import. Its fixes stay keyed to MoltenVK's driver ID: KosmicKrisp gets none of them.

Apple only: hos_main.c's call and every new path are inside defined(__APPLE__); other platforms are unchanged.

On main with #479 and #438: main keys no vendor fix on MoltenVK's driver ID, so test_vendor_fixes.py stays as main has
it (a KosmicKrisp row would check fetch_bounds alone; it goes with the follow-up that keys TexOrder's stored image
reads on KosmicKrisp). The display fixture's driver stub stands beside main's hos_game_layout() stub, without the
kept-startup stub of the Darwin host PR. #479 packages no prebuilt renderer, so that block and the LONGSHOT_VK_PREBUILT
row are not brought back; development.md gets no RX_VK_METAL_CACHE_DIR row (another PR's).

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
(cherry picked from commit 61774ddb7c189db7e82cbdf7c5258bb122e7fd67)
#479's build takes the architecture and SDK from darwin_toolchain (cd2490c6), so VulkanDriverPackage's build_plan
patches darwin_build.darwin_toolchain.architecture and .darwin_sdk, as the other build fixtures in this file do;
toolchain.need stays patched where it is.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
icd_manifest's dict holds a bool (is_portability_driver) beside strings; Pyright inferred dict[str, str] and refused it.
No change to the written manifest.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
(cherry picked from commit 2ed98ef31814a46ef9c023774a1c996f2cc628bd)
Start #20 (both drivers packaged, KosmicKrisp chosen by the macOS 26
rule) compiled 244 of 38,809 pipelines in about 14 minutes; MoltenVK's
whole precompile took 626 s in #19. KosmicKrisp is now used only with
RX_VK_DRIVER=kosmickrisp (its manifest packaged), or when it is the
only packaged driver. Its Apple silicon and macOS 26 range only words
the log. The log reads `vk: Vulkan driver: MoltenVK (default;
RX_VK_DRIVER=kosmickrisp to measure KosmicKrisp): VK_DRIVER_FILES=...`
(the macOS version left the parenthesis). Apple only.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
(cherry picked from commit 73ee181e0fd8c29d795b835f18fc178f91794a52)
test(macos): the display fixture counts longshot_env.txt's loads on Apple only
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 6s
Third-party parts / As their notes say (pull_request) Successful in 8s
PR description / Description follows the template (pull_request_target) Successful in 18s
9e0b02f0e5
The count is read only by the Darwin driver-choice stub, so off Apple the fixture keeps main's empty
load_longshot_env() stub and its text off Apple is main's again.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
mooki changed title from WIP: feat(macos): ship the Vulkan loader with KosmicKrisp and MoltenVK, MoltenVK the default to feat(macos): ship the Vulkan loader with KosmicKrisp and MoltenVK, MoltenVK the default 2026-11-15 02:46:52 +00:00
mooki force-pushed mac/kosmickrisp from 9e0b02f0e5
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 6s
Third-party parts / As their notes say (pull_request) Successful in 8s
PR description / Description follows the template (pull_request_target) Successful in 18s
to 7872c6e71c
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
CI / Conventional Commit title (pull_request) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 8s
Guide screenshots / Guide images and local UI source review (pull_request) Failing after 8s
Third-party parts / As their notes say (pull_request) Successful in 18s
PR description / Description follows the template (pull_request_target) Successful in 6s
2026-11-15 07:52:57 +00:00
Compare
mooki force-pushed mac/kosmickrisp from 7872c6e71c
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
CI / Conventional Commit title (pull_request) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 8s
Guide screenshots / Guide images and local UI source review (pull_request) Failing after 8s
Third-party parts / As their notes say (pull_request) Successful in 18s
PR description / Description follows the template (pull_request_target) Successful in 6s
to 9d34f9a1c8
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
CI / Conventional Commit title (pull_request) Successful in 6s
CI / Workflow lint (actionlint) (pull_request) Successful in 10s
Guide screenshots / Guide images and local UI source review (pull_request) Failing after 8s
Third-party parts / As their notes say (pull_request) Successful in 12s
Website / Website on Cloudflare (pull_request_target) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 5s
2026-11-15 13:54:41 +00:00
Compare
Author
Member

Folded into #846 (feat(macos): Vulkan on Metal: pinned host imports, the unified-memory budget, Metal's cache folder and KosmicKrisp)

Folded into #846 (feat(macos): Vulkan on Metal: pinned host imports, the unified-memory budget, Metal's cache folder and KosmicKrisp)
mooki closed this pull request 2026-11-15 18:07:44 +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
CI / Conventional Commit title (pull_request) Successful in 6s
CI / Workflow lint (actionlint) (pull_request) Successful in 10s
Guide screenshots / Guide images and local UI source review (pull_request) Failing after 8s
Third-party parts / As their notes say (pull_request) Successful in 12s
Website / Website on Cloudflare (pull_request_target) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 5s

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!654
No description provided.