Skip to content

MetalSharp 1.0.0: the Arm64 FEX build on MetalSharp Wine - #744

Merged
aaf2tbz merged 49 commits into
mainfrom
feat/arm64-fex-1.0
Oct 28, 2026
Merged

aaf2tbz merged 49 commits into
mainfrom
feat/arm64-fex-1.0

Conversation

@aaf2tbz

@aaf2tbz aaf2tbz commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

MetalSharp 1.0.0 adds an Arm64 FEX build (VARIANT=fex). It runs games on MetalSharp Wine v4.0, an arm64 Wine 11.20 that runs x86 code through FEX, with no Rosetta. The regular x86_64 build behaves as on main: every FEX behavior sits behind the backend's compile-time variant (MS_BACKEND_VARIANT) or the app's variant (app-gpui/src/variant.rs). The updater moves between the two builds.

What changes on the FEX build

  • Runtime: MetalSharp Wine replaces CrossOver/GPTK Wine through a runtime facade. Wine Steam starts directly through MetalSharp Wine's wrapper and is stopped with its wineserver. Setup works offline from MetalSharp Wine's bundled assets and shows each step of preparing a prefix.
  • Lanes:
    • Each game's lane goes into <game>/.metalsharp/launch.env. MetalSharp Wine applies that file however the game starts, whether from Steam, a launcher, or its executable.
    • VKD3D-Proton runs most D3D12 games, including the D3DMetal and GPTK lanes.
    • Mullion runs D3D12, D3D11 and D3D10, for x86_64 and 32-bit x86 games, and is the default.
    • DXVK runs the D3D9 route and is a lane of its own for x86_64 games.
    • OpenGL is Wine's Direct3D over WineMetalGL, with the game asked for its OpenGL renderer.
  • No D3D DLLs in game folders: MetalSharp Wine's loader routes D3D and DXGI DLLs to the lane's copies by name. Copies next to a game's executable look like an injected proxy to anti-tamper (Dragon's Dogma 2 quit about 40 s in). Copies older versions left are removed.
  • Launchers: Epic, GOG, Ubisoft and GameJolt get the same launch.env routing. Epic launches are tracked by process group, because legendary does not wait for the game.
  • Settings:
    • Frame rate limits per lane (GET/POST /framerate), a Metal HUD switch, and ray tracing on the VKD3D-Proton lane.
    • Mullion reads MetalFX and frame rate limits from mullion.conf.
    • Caches: Settings shows Mullion, VKD3D-Proton and DXVK caches per lane, with their real size and their own Clear. Games keep these caches in shader-cache/fex-<lane> and pipeline-cache/fex-<lane>, through VKD3D_SHADER_CACHE_PATH, DXVK_STATE_CACHE_PATH and MULLION_SHADER_CACHE_PATH. Mullion's Clear empties MetalSharp's per-game caches, and asks first before also clearing the Metal cache that every Mullion game shares. The caches earlier FEX builds left in game folders (vkd3d-proton.cache, <exe>.dxvk-cache) are removed once, at the game's next launch.
  • Welcome: after an x86_64 install (0.80.0 or any regular build) moves to the FEX build, the library shows a three-page welcome once: early access and how to report bugs, the graphics lanes, and Get Into It. It never shows on a fresh FEX install, a FEX update or the regular build.
  • Folder access: at the first Wine Steam start, the FEX build asks macOS for access to Documents and to external drives. Both builds' Info.plists explain these requests.

Update paths

Migration takes an explicit path, decided from the running build, the build recorded in setup.json (runtime_variant; missing on 0.80.x means regular) and the version it was migrated at:

  • Regular → regular (0.80.x → 1.0.0 and later): the regular build's migration, as on main.
  • Regular → FEX (0.80.x or any regular build → FEX): the full migration, converting the Steam, GOG, Ubisoft and bottle prefixes to MetalSharp Wine with an x86_64 backup. This also runs when the FEX DMG is installed by hand, without the updater's marker.
  • FEX → FEX (1.0.0 → 1.1.0 and later): a light update. It installs the new MetalSharp Wine and the app's runtime pieces, leaves user data in place, and converts no prefixes.
  • FEX → regular: the regular migration, after restoring the prefixes' x86_64 backups. The read-only MetalSharp Wine app is removed with the old runtime.
  • Recovery (update.sh --recover) reinstalls the installed build. On FEX that is the FEX DMG with its MetalSharp Wine, at the same version too.
  • The backend's startup MetalSharp Wine refresh skips while a migration is pending or running. An install lock keeps it and a migration's install from replacing the app at the same time.

Release and CI

  • The version is 1.0.0. The release builds the FEX DMG beside the regular one and pins MetalSharp Wine v4.0 (MetalSharp-Wine-4.0-arm64.zip, checked against SHA256SUMS). If the FEX build fails, the regular release goes out without the FEX DMG instead of being blocked.
  • PR CI also builds the FEX backend and runs its suite: make VARIANT=fex test.
  • The plan is in docs/arm64-fex-1.0-plan.md.

Testing

  • make -C app/src-c and make -C app/src-c VARIANT=fex BUILD_DIR=build-fex both build with -Werror.
  • make -C app/src-c test (regular) and make -C app/src-c VARIANT=fex BUILD_DIR=build-fex test pass. They cover the migration paths, removing read-only runtime folders, the per-lane caches, the old-cache cleanup, the install lock, and FEX recovery and build switches.
  • cargo fmt --check, cargo check and cargo test pass in app-gpui, as do the backend source contracts and the route inventory check.
  • On an M4, through the installed FEX build:
    • Dragon's Dogma 2 runs on Mullion.
    • Steam starts and stops through the wrapper.

Avery Felts and others added 30 commits October 27, 2026 23:45
wine_runtime.c holds the one switch between the x86_64 Wine runtime (regular) and
MetalSharp Wine (fex). On fex, runtime/wine becomes a facade over the signed
MetalSharp-Wine.app: wrapper scripts that clear the x86_64 runtime's library, DLL
and Vulkan paths and run the app's wine, a lib link and a writable etc. A 0.80.0
runtime is kept as runtime/wine-x86_64. /status and /update/check report the variant.
The regular build's behavior is unchanged.
update.sh takes --variant (or reads it from the DMG name), compares it with the
installed app's MetalSharpVariant, allows the same version only as a build switch,
and checks the Developer ID team of FEX installs. /update/check offers this
build's own asset and the other build as a switch; /update/start takes a variant.
- routes: Mullion for the D3DMetal, VKD3D and GPTK lanes, DXVK as its own lane,
  no native-first overrides, the x86_64 runtime's paths cleared; the Steam client,
  Rockstar launcher and Steam-launcher titles use Mullion
- staging: Mullion and DXVK from the app, cleanup of the x86_64 build's DLLs
- setup: MetalSharp-Wine.app copied, verified and made read-only instead of
  Rosetta and the runtime bundles; the Steam prefix gets wineboot, VC++ 2022 and
  the core fonts before Steam; Epic, GOG, Ubisoft and Sharp bottles likewise
- migration: prefixes converted one way with an x86_64 backup, restored by the
  regular build on a switch back; setup.json records runtime_variant
- process detection, dependencies, readiness, Wine Mono and x86 FNA games know
  the FEX layout; no Rosetta reinstall, wrapper patching or SDL linking on FEX
METALSHARP_VARIANT=fex builds MetalSharp.app with MetalSharp-Wine.app in
Resources, never re-signed (inside-out signing skips it), no runtime bundles,
and Info.plist MetalSharpVariant. Release CI's build-fex job downloads the pinned
signed MetalSharp-Wine zip, checks it, and notarizes MetalSharp-<v>-arm64-FEX.dmg;
the release job uploads both DMGs to one release.
The variant comes from /status and /update/check. On FEX the pipeline menus offer
Mullion (DXMT), DXVK, D3D9, Wine and Mono/FNA, saved D3DMetal/VKD3D/M13 choices
show as Mullion, the x87 toggle is hidden, the Backend row and setup wizard
describe MetalSharp Wine, GPTK stays available through Rosetta, and the update
dialog marks the current build and offers the other as a same-version switch.
…arp Wine wrapper

The MetalSharp Steam helper app is for the x86_64 build only.
Steam URL handoffs on FEX use the direct launch's overrides.
…ne or GPTK cleans up

- Mullion stages the 64-bit (ARM64X) set and Mullion (32) the i386 set, whatever the
  executable; DXVK its x86_64 set.
- Saving a route that stages nothing (Wine, plain Wine, GPTK, D3DMetal) still
  removes the previous route's byte-identical DLLs.
- Fix a free of a string literal in the FEX controller-shim path.
VKD3D no longer maps to Mullion on FEX: it runs GRAPHICS_BACKEND=vkd3d with the
VKMT MoltenVK settings, and saving it stages VKD3D-Proton's d3d12/d3d12core/dxgi
and DXVK's d3d9/10/11 from the app into the game folder. The wrappers (version 2)
let vkd3d through.
…D3D DLLs in game folders

Steam, Epic, GOG, Ubisoft and GameJolt games take their lane from <game>/.metalsharp/launch.env, which MetalSharp Wine applies however the game starts (through Steam, a launcher, or directly). On MetalSharp Wine the D3DMetal and GPTK lanes run on VKD3D-Proton, the D3D9 route on DXVK, DXVK is a lane of its own for x86_64 programs, and the OpenGL lane (Wine's Direct3D over WineMetalGL) passes each engine its OpenGL flag. Epic launches are tracked by process group, since legendary does not wait for the game.

Route DLLs are no longer copied into game folders: MetalSharp Wine's loader routes every D3D and DXGI DLL to the lane's own copies by name, and copies beside the executable look like an injected proxy to anti-tamper (Dragon's Dogma 2 exited about 40 s in). Copies earlier versions staged are still removed.
…ullion's MetalFX and limits live in mullion.conf

GET/POST /framerate sets a frame rate limit per lane: Mullion's in mullion.conf (d3d11 and d3d12), VKD3D-Proton's as VKD3D_FRAME_RATE. The Metal performance HUD (MTL_HUD_ENABLED) and MoltenVK's ray tracing on the VKD3D-Proton lane (DXR 1.1, feature level 12_2) are off until turned on. Mullion reads MetalFX and the limits from mullion.conf when a game starts, so a game the Steam client starts gets them without the client's environment.
…verts the 0.80.0 Steam prefix

Setup stages support assets from MetalSharp Wine's share/metalsharp (Mono and FAudio from its archives) and MetalSharp.app's configs, so it needs no network, and shows each part of preparing a prefix as it runs. A 0.80.0 Steam prefix is backed up before it is converted, and migration does not copy the old one back over it. mullion.conf is MetalSharp's to edit; a dxmt.conf from before becomes it.
Settings for the frame rate limits, the Metal HUD and ray tracing on the FEX build, its logs, and the library's lanes as MetalSharp Wine has them.
…and external and network drive access

Info.plist describes why Wine needs the Documents folder (games keep saves there) and external or network drives (Steam libraries), so macOS can ask instead of denying access.
…ain, and the source contract test finds it again
… extra route DLL cleanup apply to the FEX build only; the regular build behaves as on main
…etalFX starting off are the FEX build's; the regular build looks as on main
… FEX updates swap in the new MetalSharp Wine without converting prefixes again, and only a move back from FEX restores x86_64 backups
… VKD3D-Proton and DXVK caches per lane, and games keep those caches in the lanes' folders
… with early access, the graphics lanes and Get Into It
… back from FEX leaves no MetalSharp Wine app behind
…tion, every prefix included, without the updater's marker
…s before clearing Metal's cache that every Mullion game shares
…er builds left in a game's folders, once, at its next launch
…unning migration, and an install lock keeps both from replacing the app at once
…s-fex.toml is the regular rules on MetalSharp Wine's lanes plus the games verified there
… and updates, and a changelog entry crediting Deadlock's fixes
…Wine and everything it carries are covered with their license texts
…eds METALSHARP_WINE_APP instead of a maintainer's path
…11 and D3D10 (welcome guide, changelog, architecture)
…o longer migrates at its second launch, and a Steam prefix MetalSharp Wine built reads as FEX
…nloaded regular DMG instead of recovering, and so reinstalling, FEX
…, swapped in safely and then consumed, and a failed restore leaves the build unrecorded to retry
…-recover for the regular build updating itself, the downloaded DMG with its variant otherwise
@aaf2tbz
aaf2tbz merged commit b44cc0f into main Oct 28, 2026
7 checks passed
@aaf2tbz

aaf2tbz commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator Author

Released as MetalSharp 1.0.0. The tag is on merge commit b44cc0f, and Release CI run 40653045054 passed: both DMG builds succeeded and the release was published.

What shipped

  • MetalSharp-1.0.0-arm64.dmg (x86_64 build): notarized, and its backend is the regular build.
  • MetalSharp-1.0.0-arm64-FEX.dmg (Arm64 FEX build): notarized. It carries MetalSharp Wine 4.0 unmodified (zip sha256 11ea3716…), and that app is notarized itself.
  • Both were downloaded and checked after the release: spctl accepts both DMGs, both apps and the bundled MetalSharp-Wine.app as Notarized Developer ID, and the staples validate.

Components

Checked before merging

  • PR CI passed on the final commit: C/C++/Obj-C, DMG workflow, docs, GPUI, Metal, rules TOML and shell. PR CI now builds and tests the FEX backend too.
  • An independent review turned up three blockers, all fixed with tests:
    • a fresh FEX install no longer migrates again on its second launch;
    • "switch to x86_64" from the FEX build now installs the x86_64 DMG instead of reinstalling FEX;
    • a half-finished prefix conversion or restore can no longer roll a prefix back to an old backup.

Known follow-ups

  • If a restore to x86_64 keeps failing (for example, the disk is full), migration reports the error at every start; there is no option to skip that prefix yet.
  • make VARIANT=fex test skips the regular build's route, smoke and runtime tests, which assume the x86_64 runtime.
  • The welcome overlay dims the library behind it instead of blurring it, because GPUI 0.2.2 has no backdrop blur.

Thanks to @MohamedElGhandour for #741 and #742.

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.

1 participant