WIP: macOS (Apple Silicon) support on the WebGPU/Metal renderer (#715 rebased on main, with fixes) #1088

Closed
jrldorival wants to merge 37 commits from macos-metal into main
jrldorival commented 2026-11-09 10:47:59 +00:00 (Migrated from github.com)

macOS (Apple Silicon) support, rebased on current main as asked when #715 was closed. It carries #715's commits by @yeetbot90 unchanged, the fixes @madebyjordan reported there, and further work on the WebGPU/Metal renderer. Draft: the title screen, the opening and the menus work, but 3D gameplay still renders white (details below).

Tested on a MacBook Air M5 (16 GB), macOS 27.2, Xcode 27.2, with a v131072 dump: full native arm64 build in about 10 minutes, the title screen with its movie and the menu (pt-BR), the opening movie with subtitles at 50-59 fps, saves, a PS4 controller. docs/macos-port-status.md has the full list and how to build.

Build

  • scripts/macos/deps.sh: cmake/meson/ninja/pkg-config in a venv when missing (no Homebrew needed); dav1d, with FFmpeg built --enable-libdav1d (FFmpeg's own AV1 decoder is hardware-only: the movies were black).
  • zbic.py passes the SDK sysroot; build.py links -lc++ and copies libwgpu_native.dylib beside the game; scripts/macos/build-game.sh runs the builder from a checkout.

Runtime (host/)

  • Fibers: the guard page goes on after makecontext (macOS zeroes the whole stack), sized getpagesize().
  • Address waits use os_sync_wait_on_address (macOS 14.4+). The 64 shared condition "buckets" woke the wrong waiter on WakeConditionVariable (lost wakeups): the opening went from 13-16 to 50-59 fps. HOS_WAIT_BUCKETS=1 keeps the old path.
  • The video decoder gives its virtual core up while dav1d works; planar frames are kept as NV12 only.
  • No wayland,x11 hint on macOS (no window); F7-F10 reach rx_present_hotkey.
  • HOS_QUALITY presets (hookshot_env.txt), and rx_option reads HOS_DYNRES/HOS_RES_SCALE/HOS_REALTIME_STEP when there is no Insert menu.

WebGPU/Metal renderer (gpu/render/)

  • Vertex bindings in whole strides: Metal counts size / stride elements and read the last vertex as zero (the title logo cut on a diagonal).
  • NVN2 compute push constants in c0, as vk_bank.cpp (the movies' NV12->RGBA pass never ran).
  • Base vertex / base instance / draw ID in the vertex c0 at 0x640/0x644/0x648, as GetReplaceConstBuffer (all UI text was missing).
  • Stand-ins for HMMA networks that do not translate: the movie upscaler (linear) and gameplay's TAAU (the packed RGB9E5 scene colour decoded and squared, as taau_upscale.comp).
  • Samplers shared past 16 a stage (Metal's limit, as the web build already did): 17-18 made layouts invalid and wgpu dropped whole batches.
  • The Ampere->Maxwell lowering is now eden's (gpu/vk) for this renderer too; image atomics are emulated as read-modify-write in WGSL.

Still open

  • 3D gameplay is white. The HDR scene is drawn (mean luminance ~22) but the final picture saturates. With RX_SYNC_DISPATCH_WRITES=1 shapes appear (at ~4 fps), so it looks like the Vulkan back end's OoT fixes are missing here: compute programs reading 64-bit global pointers (3720e3ab9f56fbfa, ba00b8c5a7b43504, c7f6c62cc98485ac, e7d75810de840595 do not translate), the foliage's indirect grids, and GPU results the CPU reads.
  • CPU uploads to Depth32Float textures are rejected by WebGPU (about 2 dropped batches a run).
macOS (Apple Silicon) support, rebased on current `main` as asked when #715 was closed. It carries #715's commits by @yeetbot90 unchanged, the fixes @madebyjordan reported there, and further work on the WebGPU/Metal renderer. Draft: the title screen, the opening and the menus work, but 3D gameplay still renders white (details below). Tested on a MacBook Air M5 (16 GB), macOS 27.2, Xcode 27.2, with a v131072 dump: full native arm64 build in about 10 minutes, the title screen with its movie and the menu (pt-BR), the opening movie with subtitles at 50-59 fps, saves, a PS4 controller. `docs/macos-port-status.md` has the full list and how to build. ### Build - `scripts/macos/deps.sh`: cmake/meson/ninja/pkg-config in a venv when missing (no Homebrew needed); dav1d, with FFmpeg built `--enable-libdav1d` (FFmpeg's own AV1 decoder is hardware-only: the movies were black). - `zbic.py` passes the SDK sysroot; `build.py` links `-lc++` and copies `libwgpu_native.dylib` beside the game; `scripts/macos/build-game.sh` runs the builder from a checkout. ### Runtime (`host/`) - Fibers: the guard page goes on after `makecontext` (macOS zeroes the whole stack), sized `getpagesize()`. - Address waits use `os_sync_wait_on_address` (macOS 14.4+). The 64 shared condition "buckets" woke the wrong waiter on `WakeConditionVariable` (lost wakeups): the opening went from 13-16 to 50-59 fps. `HOS_WAIT_BUCKETS=1` keeps the old path. - The video decoder gives its virtual core up while dav1d works; planar frames are kept as NV12 only. - No `wayland,x11` hint on macOS (no window); F7-F10 reach `rx_present_hotkey`. - `HOS_QUALITY` presets (`hookshot_env.txt`), and `rx_option` reads `HOS_DYNRES`/`HOS_RES_SCALE`/`HOS_REALTIME_STEP` when there is no Insert menu. ### WebGPU/Metal renderer (`gpu/render/`) - Vertex bindings in whole strides: Metal counts `size / stride` elements and read the last vertex as zero (the title logo cut on a diagonal). - NVN2 compute push constants in c0, as `vk_bank.cpp` (the movies' NV12->RGBA pass never ran). - Base vertex / base instance / draw ID in the vertex c0 at 0x640/0x644/0x648, as `GetReplaceConstBuffer` (all UI text was missing). - Stand-ins for HMMA networks that do not translate: the movie upscaler (linear) and gameplay's TAAU (the packed RGB9E5 scene colour decoded and squared, as `taau_upscale.comp`). - Samplers shared past 16 a stage (Metal's limit, as the web build already did): 17-18 made layouts invalid and wgpu dropped whole batches. - The Ampere->Maxwell lowering is now eden's (`gpu/vk`) for this renderer too; image atomics are emulated as read-modify-write in WGSL. ### Still open - **3D gameplay is white.** The HDR scene is drawn (mean luminance ~22) but the final picture saturates. With `RX_SYNC_DISPATCH_WRITES=1` shapes appear (at ~4 fps), so it looks like the Vulkan back end's OoT fixes are missing here: compute programs reading 64-bit global pointers (`3720e3ab9f56fbfa`, `ba00b8c5a7b43504`, `c7f6c62cc98485ac`, `e7d75810de840595` do not translate), the foliage's indirect grids, and GPU results the CPU reads. - CPU uploads to `Depth32Float` textures are rejected by WebGPU (about 2 dropped batches a run).
hckrman101 commented 2026-11-09 18:39:54 +00:00 (Migrated from github.com)

Hi! This PR has merge conflicts with main now (a lot has landed today). Could you merge main in or rebase and resolve them? Happy to give it a proper review once it's up to date.

Hi! This PR has merge conflicts with `main` now (a lot has landed today). Could you merge `main` in or rebase and resolve them? Happy to give it a proper review once it's up to date.
NotRexed commented 2026-11-10 02:08:09 +00:00 (Migrated from github.com)

Thanks for carrying the macOS port forward. We have no way to build or test on Apple Silicon here, and the branch conflicts with main in several renderer and builder files, so closing for now. A rebased PR with a macOS CI job that builds it would make it reviewable.

Thanks for carrying the macOS port forward. We have no way to build or test on Apple Silicon here, and the branch conflicts with main in several renderer and builder files, so closing for now. A rebased PR with a macOS CI job that builds it would make it reviewable.
Commenting is not possible because the repository is archived.
No description provided.