Skip to content

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

Draft
jrldorival wants to merge 37 commits into
NotRexed:mainfrom
jrldorival:macos-metal
Draft

jrldorival wants to merge 37 commits into
NotRexed:mainfrom
jrldorival:macos-metal

Conversation

@jrldorival

Copy link
Copy Markdown

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).

yeetbot90 and others added 30 commits November 8, 2026 12:27
Add a Darwin POSIX and WebGPU/Metal build path targeting x86-64 under Rosetta, with an Apple Silicon CI smoke check and focused tests. Also retry Builder downloads when a successful HTTP response has a truncated body.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…rt-slice

# Conflicts:
#	builder/setup/setup.c
Enable Darwin ucontext declarations and make the relative sleep helper available on all POSIX targets. Generate the a64 runtime header for the macOS smoke compile and verify the runner can link an x86-64 executable.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Pass the binary before the architecture flag to lipo. Use the same valid argument order in the macOS builder preflight and CI executable check.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The host translation units do not require generated suyu helpers. Use a minimal generated-header placeholder so the smoke check does not need to build the C++ translator or require game data.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Define Darwin's feature-test macro alongside X/Open for ucontext and the BSD/POSIX declarations used by the macOS host. Complete the generated-header placeholders in the host compile check.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Include the temporary generated-header directory when syntax-checking the SDL display shim, which also includes the runtime header.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The Metal display path does not define the Vulkan window descriptor; keep its initialization Linux-only.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Cover the production lipo invocation order so dependency architecture checks remain valid on macOS.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
yeetbot90 and others added 7 commits November 8, 2026 19:43
Fix host compiler selection for native helpers and give ZBIC its macOS dynamic-library build path. Make the regression runner distinguish Windows, Linux, and macOS, guard platform-specific packaging scripts, and remove an undefined stale Windows preflight branch.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Package the standalone HookshotBuilder.app with its tools inside Resources and publish an app-only zip asset for version tags.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…ainda branca

Por cima do PR NotRexed#715. O jogo compila e roda nativo em Apple Silicon (testado num M5, macOS 27.2):
título com vídeo e menu em português, abertura com vídeo e legendas a ~50-59 fps, saves e controle.
A cena 3D da gameplay ainda sai branca (detalhes em docs/macos-port-status.md).

Build e dependências
- deps.sh: cmake/meson/ninja/pkg-config num venv quando faltam (sem Homebrew); dav1d e FFmpeg com
  libdav1d (o decodificador AV1 nativo do FFmpeg só usa hardware: vídeos pretos).
- zbic.py: sysroot do SDK; build.py: -lc++ e libwgpu_native.dylib na pasta do jogo.
- make_game.py copia scripts/macos/hookshot_env.txt; scripts/macos/build-game.sh roda o builder.

Runtime
- win32_posix.c: fibras com a página de guarda depois do makecontext (páginas de 16 KB);
  CriticalSection/SleepConditionVariable no macOS; esperas por endereço com os_sync_wait_on_address
  (os baldes compartilhados acordavam a thread errada: abertura de 13-16 para 50-59 fps); QoS interativa.
- display_sdl.c: janela Cocoa (sem a dica wayland/x11); F7-F10 ligadas aos ajustes de imagem.
- nn_video.c: núcleo virtual liberado durante o decode; quadros só em NV12; HOS_VIDEO_TIMES.
- hos_main.c/rx_backend.c: presets HOS_QUALITY (alta, media, baixa, minima); resolução fixa,
  resolução dinâmica e passo de tempo lidos do ambiente quando não há menu Insert.

Renderizador WebGPU/Metal
- Buffers de vértice com strides inteiros (o Metal lia o último vértice como zero: logo cortado).
- Push constants do compute NVN2 no c0 (o passo YUV->RGB dos vídeos não rodava).
- Base vertex/instance e draw ID no c0 0x640/0x644/0x648 (o texto da interface sumia).
- Substituto das redes com HMMA: vídeo (upscale linear) e TAAU da gameplay (RGB9E5 empacotado,
  decodificado e elevado ao quadrado, como taau_upscale.comp do Vulkan).
- Samplers compartilhados a partir do 16o (limite do Metal; com 17-18 o lote inteiro era descartado).
- Diagnóstico nos frame dumps (índices, blend, bindings e chave dos dispatches).

Tradutor de shaders
- lower_to_maxwell.h do lado WebGPU agora é o do eden (backend Vulkan), bem mais completo.
- Emissor WGSL: atômicos de imagem emulados (leitura, operação e escrita).

Onde paramos
- Gameplay 3D branca: a cena HDR é desenhada (luminância ~22) mas a imagem final satura. Com
  RX_SYNC_DISPATCH_WRITES=1 aparecem formas (~4 fps). Faltam as correções que o Vulkan tem para o
  Zelda: shaders com ponteiros de 64 bits (3720e3ab9f56fbfa, ba00b8c5a7b43504, c7f6c62cc98485ac,
  e7d75810de840595), grades indiretas da folhagem e sincronizar GPU->CPU os resultados que a CPU lê.
- Escritas em texturas Depth32Float proibidas no WebGPU (~2 lotes descartados por execução).
- Um sampler compartilhado filtrável numa textura não filtrável (1-2 pipelines inválidas).
Conflitos resolvidos: hos_sdl.c (GetWindowSize do main e as funções Metal do macOS),
display_sdl.c (window movido pelo main; vk_api.h agora incluído também no macOS para
MkwVkPointer/HOS_MOUSE_*), hos_main.c (presets de qualidade do macOS e os cheats do main),
make_game.py (free_memory_gb removido pelo main), tests/run_all.py (testes Linux pulados no macOS).
Compilado e testado no M5: título com vídeo e menu; gameplay 3D ainda branca, como antes.
@hckrman101

Copy link
Copy Markdown
Collaborator

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants