Running Satisfactory (UE 5.6) under CrossOver on Apple silicon with the DirectX 12 renderer, by replacing CrossOver's bundled D3DMetal 3.0 with D3DMetal 4.0b2 from Apple's Game Porting Toolkit 4 beta 2.
Status: working. Verified 2026-08-23 on an M5 Max — multiple clean sessions, D3D12 feature level 12_2, shader model 6.6, resource binding tier 3.
The swap is undone by every CrossOver update. When DX12 starts crashing again, run
./reapply-gptk4.sh. That is the whole maintenance story.
With stock CrossOver, Satisfactory dies during renderer init — before a window
ever appears. From FactoryGame.log:
LogRHI: Using Default RHI: D3D12
LogRHI: RHI D3D12 with Feature Level SM6 is supported and will be used.
LogD3D12RHI: Bindless resources are supported
Assertion failed: GGlobalSamplerDescriptorHeapSize <= MaximumSamplerHeapSize
[File: Engine\Source\Runtime\D3D12RHI\Private\D3D12Device.cpp] [Line: 372]
FactoryGameSteam-D3D12RHI-Win64-Shipping.dll!FD3D12Device::SetupAfterDeviceCreation()
FactoryGameSteam-D3D12RHI-Win64-Shipping.dll!FD3D12Adapter::InitializeDevices()
FactoryGameSteam-D3D12RHI-Win64-Shipping.dll!FD3D12DynamicRHI::Init()
Cause. UE 5.6 sizes its global bindless sampler descriptor heap from what the D3D12 device reports, then asserts the result fits the device maximum. D3DMetal 3.0 reports values that fail that assertion, so the RHI never initializes.
Not the cause, so don't chase these:
- Launch options.
-dx12changes nothing — D3D12 is already the default RHI for this title, which is exactly why it crashes out of the box. - Missing VC++ redistributables. A separate, real issue on some bottles that produces a different symptom (a "Microsoft Visual C++ 2015–2022 Redistributable (x64) required" dialog). This bottle already has v14.51.36247 x64+x86 installed.
- Graphics settings, save files, or a game reinstall. The crash is before any of those are read.
Fix. D3DMetal 4 reports correctly. Confirmed independently on the CodeWeavers Satisfactory forum, where the same assertion also hit StarRupture.
No shipping CrossOver bundles D3DMetal 4 — 26.3 (2026-07-21, the current release) still ships 3.0 — so the swap has to be done by hand.
| Needed | This machine | |
|---|---|---|
| Hardware | Apple silicon | M5 Max, 40-core GPU |
| macOS | 15 Sequoia or newer | 26.6.2 (25G83) |
| CrossOver | 26.x | 26.3.0.39832 |
| Payload | GPTK 4.0 beta 2 | gptk4-b2/lib/ (vendored here) |
| Bottle graphics engine | D3DMetal — not DXVK, not DXMT | CX_GRAPHICS_BACKEND=d3dmetal |
| Permission | Terminal granted App Management | see below |
CrossOver.app is Developer ID signed, notarized, and stapled, and lives in
/Applications. macOS refuses to let an ordinary process modify it — every
write fails with Operation not permitted, and sudo does not help.
Grant it in System Settings → Privacy & Security → App Management for whichever terminal runs the script (here: Ghostty). Add it with + if it isn't listed, and relaunch the terminal afterward — the permission is not picked up by an already-running process.
This permission lets anything run from that terminal modify installed apps. Revoking it when you're done is reasonable; just re-grant before re-applying.
./reapply-gptk4.shIdempotent — exits immediately if D3DMetal 4.0b2 is already active. It quits CrossOver, backs up the stock libraries under the current CrossOver version, swaps in the payload, and updates the bottle.
./reapply-gptk4.sh --status reports what's installed without changing
anything. --dry-run prints the plan.
The procedure is Apple's own, from the Read Me inside the GPTK 4 disk image
(vendored here as gptk4-b2/Apple-ReadMe.rtf):
cd /Applications/CrossOver.app/Contents/SharedSupport/CrossOver/lib64/apple_gptk
mv external external.old; mv wine wine.old
ditto "<payload>/lib/" .Then, still per Apple, to enable the experimental DLSS→MetalFX bridge:
wine/x86_64-unix/nvngx-on-metalfx.so→nvngx.sowine/x86_64-windows/nvngx-on-metalfx.dll→nvngx.dll- copy
nvngx.dllandnvapi64.dllinto the bottle'swindows/system32
GPTK 4 does not ship atidxx64, which CrossOver 26.3 does; Apple's
procedure removes it. GPTK 4 adds d3d10, which CrossOver's apple_gptk
lacked. Both are expected. CrossOver's bin/wine only ever references
apple_gptk/external/libd3dshared.dylib by path — it never names the individual
DLLs — so the loader falls back cleanly for anything absent.
-dx12 -NOTEXTURESTREAMING
-dx12 is redundant but explicit. -NOTEXTURESTREAMING is the one that matters.
Set via Steam's UI (Properties → General), or directly in
.../Steam/userdata/<id>/config/localconfig.vdf under the "526870" block —
with Steam closed, or Steam overwrites the file on exit.
A good run logs this:
LogRHI: Using Default RHI: D3D12
LogD3D12RHI: Max supported Feature Level 12_2, shader model 6.6,
binding tier 3, wave ops supported, atomic64 supported
LogFSR: Successfully initialized FSR Upscaling provider using version '3.1.5'
LogXeSSRHI: Loading XeSS library 2.0.1 on AMD RHI D3D12 — Intel XeSS effect supported
and no GGlobalSamplerDescriptorHeapSize line.
Log: <bottle>/drive_c/users/crossover/AppData/Local/FactoryGame/Saved/Logs/FactoryGame.log
Crashes: .../Saved/Crashes/*/CrashContext.runtime-xml — <ErrorMessage> names the assertion.
The first launch after a swap is slow while D3DMetal rebuilds its shader cache. That is normal and does not repeat.
DLSS does not work. Despite the nvngx step, the log reports:
LogDLSS: NVIDIA NGX DLSS supported SR=0 RR=0
[streamline] Disabling DLSS-G since it is not supported on current hardware
Almost certainly because D3DMetal presents the GPU as AMD Compatibility Mode (DeviceId 0x66af), and NGX requires an NVIDIA adapter. The MetalFX
bridge is documented by Apple as experimental; it does not engage for this title.
SR is Super Resolution (the upscaler) and RR is Ray Reconstruction
(the ray-traced denoiser). Neither costs you much here:
- SR — you have two working substitutes. FSR 3.1 and XeSS 2 are close enough to DLSS 3 that the difference is a matter of taste at equivalent quality presets.
- RR — moot. Satisfactory disables ray tracing at the project level
(
LogRendererCore: Ray tracing is disabled. Reason: disabled through project setting (r.RayTracing=0)), so there is nothing for it to denoise. - DLSS-G (frame generation) is separately unsupported. FSR does register an
FSRSwapchainProvider, which is the hook its frame interpolation uses.
Yes — but understand that it is currently inert, not load-bearing.
Satisfactory ships its own NVIDIA stack (nvngx_dlss.dll, nvngx_dlssd.dll,
nvngx_dlssg.dll, sl.*.dll) and Streamline loads it from the game's own
FactoryGame/Plugins/**/Binaries/ThirdParty/Win64 — never from system32. The
system32 copies Apple's Read Me calls for are also redundant under CrossOver,
which already exposes apple_gptk's DLLs to the loader; that the d3d12.dll
swap works at all, with nothing copied into system32, proves the path exists.
Keep it anyway. It costs ~100 ms of failed Streamline init at startup and some log noise, it's Apple's documented procedure, and it is the zero-effort path to DLSS simply starting to work if a later D3DMetal fixes the adapter vendor reporting. Removing it buys nothing.
Not worth chasing: making D3DMetal spoof an NVIDIA vendor ID to satisfy NGX. That is fighting the translation layer for an upscaler you already have two working substitutes for.
Use FSR or XeSS instead — both initialize cleanly and are the right upscaler choice here:
| Upscaler | Status |
|---|---|
| FSR | ✅ 3.1.5, initializes and runs |
| XeSS | ✅ 2.0.10 (library 2.0.1) |
| DLSS Super Resolution | ❌ SR=0 |
| DLSS Ray Reconstruction | ❌ RR=0 — irrelevant, the game ships with RT off |
| DLSS-G / frame gen | ❌ unsupported on this adapter |
Benign noise. [GPUBreadCrumb] Failed to OpenExistingHeapFromAddress, error: 80004001 (E_NOTIMPL) appears at startup and is harmless — it's UE's crash
breadcrumb feature, unimplemented in D3DMetal.
This is beta software. GPTK 4.0 beta 2, which CodeWeavers never tested
against. Unrelated regressions elsewhere in the bottle are possible. Modifying
the bundle also breaks its code signature seal — already-launched apps aren't
re-verified and CrossOver launches fine, but it is a real modification to a
signed, notarized app. Both replacement libraries are Apple-signed
(Authority=Software Signing), so hardened-runtime library validation accepts
them.
D3DMetal environment variables, set per-bottle in cxbottle.conf under
[EnvironmentVariables]. Full list in gptk4-b2/Apple-ReadMe.rtf.
| Variable | Default | Notes |
|---|---|---|
D3DM_SUPPORT_DXR |
on for M3+ | DirectX Raytracing translation. First thing to set to 0 if DX12 misbehaves. |
D3DM_ENABLE_METALFX |
off | Already 1 here. Maps DLSS→MetalFX where possible — see limitations. |
D3DM_MTL4 |
on, macOS 27+ only | Metal 4 backend. Irrelevant on macOS 26 — you get the Metal 3 backend no matter what the GPU advertises. |
D3DM_MAX_FPS |
unset | Frame rate cap. |
ROSETTA_ADVERTISE_AVX |
off | Advertise AVX to translated code. |
This bottle also carries WINEMSYNC=1, WINED3DMETAL=1, and
DXMT_ENABLE_NVEXT=1 from bottle creation.
D3DMetal logs to the system log under category D3DMetal, prefix D3DM —
visible in Console.app, or when launching from a terminal.
./revert-gptk4.shRestores stock D3DMetal from backup-stock-apple_gptk-CX<version>/, or from the
in-place external.old / wine.old if that's all there is. DX12 will crash
again afterward — that's the point of the rollback, not a failure.
Fully reinstalling CrossOver also reverts everything.
Fallback if DX12 can't be made to work at all: -DX11 in the launch
options. Forum-confirmed to launch 1.2.2, but with noticeable stutter. An escape
hatch, not a destination.
| Path | Purpose |
|---|---|
reapply-gptk4.sh |
Apply the swap. Idempotent. --status, --dry-run. |
revert-gptk4.sh |
Restore stock D3DMetal. |
gptk4-b2/lib/ |
GPTK 4.0b2 payload, symlinks intact — no re-download needed |
gptk4-b2/Apple-ReadMe.rtf |
Apple's own instructions and env var reference |
backup-stock-apple_gptk-CX26.3/ |
Pristine D3DMetal 3.0 from CrossOver 26.3 |
pre-fix-crash-logs/ |
The original crashes, for reference |
localconfig.vdf.bak |
Steam config before the launch-options edit |
Game_Porting_Toolkit_4.0_beta_2.dmg |
Original download (payload already extracted) |
The vendored payload is what makes re-applying a one-liner — Apple gates GPTK
downloads behind a developer login, and beta builds get pulled when superseded.
Don't delete gptk4-b2/.
When a newer GPTK arrives (4.0 final, or a later beta):
- Mount the outer DMG, then the nested Evaluation environment for Windows games DMG inside it.
ditto "<mount>/redist/lib" gptk4-b2/lib—dittopreserves thex86_64-unix/*.sosymlinks that point atlibd3dshared.dylib. Plaincpwithout-Rdoes not.- Update
WANTinreapply-gptk4.shto the newCFBundleShortVersionString, then run it.
Check a candidate's version without installing:
plutil -extract CFBundleShortVersionString raw \
"<mount>/redist/lib/external/D3DMetal.framework/Resources/Info.plist"If CrossOver eventually ships D3DMetal 4 itself, this whole workflow becomes unnecessary — check the changelog after major updates.