Idea: a native macOS build (Apple silicon, x86-64 under Rosetta) with Vulkan on Metal through MoltenVK #420

Open
opened 2026-11-14 12:49:40 +00:00 by mooki · 0 comments
Member

The idea

A native macOS build of Longshot: Apple silicon (arm64) first, x86-64 under Rosetta second, macOS 14.4 or later, with
Vulkan on Metal through MoltenVK 1.4.2. Same game data, same renderer and the same behaviour as Windows and Linux. It
should take no shortcuts: no capped features and no silent restrictions. The changes that only the Mac needs are kept
behind MoltenVK and Darwin checks, so the shader output on other platforms stays byte for byte the same.

Claimed by @mooki. Measured on an M4 Max (MoltenVK 1.4.2, macOS 26), native arm64 and x86-64 under Rosetta.

Score (docs/workflow.md): severity: no build exists for macOS. Reach: Mac players, a new platform. Confidence: high.
It builds and starts on the Mac today; the remaining work is measured, not guessed.

Where it stands (local branches, not pushed yet):

  • Builds: the whole game builds natively for arm64 and for x86-64 (Apple clang, minimum macOS 14.4). The products
    depend on the system libraries only, plus the packaged SDL3 and MoltenVK.
  • Tests: the game-free suite runs on macOS. On top of main's results, it has 553 passed, 1 failed (test_menu_i18n,
    which also fails on main) and 16 skipped.
  • Startup: started without a window, the game brings up the renderer on MoltenVK, and on a warm cache it prepares
    all 38,809 shader programs in 11 s.
    • Not reached yet: the title screen, the first frame and play.
    • The pre-title pipeline build stalls on a few very large compute shaders (255k-564k SPIR-V words) that take Apple's
      Metal compiler from many seconds to minutes. One crashed Apple's compiler service (bad_alloc).
    • The work in progress lets the title start while the rest build in the background, and shrinks those shaders.

Found and fixed on the Mac (each with a game-free test, and a GPU probe passing on both arm64 and Rosetta):

  • Buffer lengths inside shaders read 0 when the buffers come from push descriptors (MoltenVK pushes them without an
    argument buffer). Those layouts use descriptor sets on MoltenVK.
  • Metal's per-stage limits: at most 8 read-write textures, about 31 buffers and 16 samplers.
  • Null descriptors, emulated in the shader where MoltenVK has no null resources.
  • A pointer into a storage-buffer array passed to a function, which Metal rejects.
  • Image reads moved past later writes and atomics. SPIRV-Cross forwards the read, and Metal also needs a texture
    fence between a read and a later atomic on the same texel.
  • Metal's 256-level bracket nesting limit, which long chains of forwarded expressions exceed.
  • A 16 MiB stack for the renderer threads (the default stack overflowed), per-worker pipeline caches, and an opt-in
    to keep Metal's shader cache with the install.
  • The host layer for Darwin: 16 KiB pages for guest memory, waits, control sockets, crash contexts and build flags.

Questions for the maintainers: the plan is one pull request per topic, opened as work in progress after this issue.
Any preference on order, or on whether to land the Darwin host layer first?

Anything else

Not tested yet: the title screen, input and play; a window (all runs so far are windowless); other Macs (M1-M3, Intel
Macs) and macOS 14 itself. The buffer-length, read-write-limit and image-read-ordering findings look worth reporting
to MoltenVK and SPIRV-Cross.

### The idea A native macOS build of Longshot: Apple silicon (arm64) first, x86-64 under Rosetta second, macOS 14.4 or later, with Vulkan on Metal through MoltenVK 1.4.2. Same game data, same renderer and the same behaviour as Windows and Linux. It should take no shortcuts: no capped features and no silent restrictions. The changes that only the Mac needs are kept behind MoltenVK and Darwin checks, so the shader output on other platforms stays byte for byte the same. **Claimed** by @mooki. Measured on an M4 Max (MoltenVK 1.4.2, macOS 26), native arm64 and x86-64 under Rosetta. Score (docs/workflow.md): severity: no build exists for macOS. Reach: Mac players, a new platform. Confidence: high. It builds and starts on the Mac today; the remaining work is measured, not guessed. **Where it stands (local branches, not pushed yet):** - **Builds:** the whole game builds natively for arm64 and for x86-64 (Apple clang, minimum macOS 14.4). The products depend on the system libraries only, plus the packaged SDL3 and MoltenVK. - **Tests:** the game-free suite runs on macOS. On top of main's results, it has 553 passed, 1 failed (`test_menu_i18n`, which also fails on main) and 16 skipped. - **Startup:** started without a window, the game brings up the renderer on MoltenVK, and on a warm cache it prepares all 38,809 shader programs in 11 s. - Not reached yet: the title screen, the first frame and play. - The pre-title pipeline build stalls on a few very large compute shaders (255k-564k SPIR-V words) that take Apple's Metal compiler from many seconds to minutes. One crashed Apple's compiler service (`bad_alloc`). - The work in progress lets the title start while the rest build in the background, and shrinks those shaders. **Found and fixed on the Mac** (each with a game-free test, and a GPU probe passing on both arm64 and Rosetta): - Buffer lengths inside shaders read 0 when the buffers come from push descriptors (MoltenVK pushes them without an argument buffer). Those layouts use descriptor sets on MoltenVK. - Metal's per-stage limits: at most 8 read-write textures, about 31 buffers and 16 samplers. - Null descriptors, emulated in the shader where MoltenVK has no null resources. - A pointer into a storage-buffer array passed to a function, which Metal rejects. - Image reads moved past later writes and atomics. SPIRV-Cross forwards the read, and Metal also needs a texture fence between a read and a later atomic on the same texel. - Metal's 256-level bracket nesting limit, which long chains of forwarded expressions exceed. - A 16 MiB stack for the renderer threads (the default stack overflowed), per-worker pipeline caches, and an opt-in to keep Metal's shader cache with the install. - The host layer for Darwin: 16 KiB pages for guest memory, waits, control sockets, crash contexts and build flags. **Questions for the maintainers:** the plan is one pull request per topic, opened as work in progress after this issue. Any preference on order, or on whether to land the Darwin host layer first? ### Anything else Not tested yet: the title screen, input and play; a window (all runs so far are windowless); other Macs (M1-M3, Intel Macs) and macOS 14 itself. The buffer-length, read-write-limit and image-read-ordering findings look worth reporting to MoltenVK and SPIRV-Cross.
Sign in to join this conversation.
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#420
No description provided.