WIP: fix(macos): start MoltenVK's giant compiles behind the game only while memory allows, and say a lost device as one #669
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
beanstalk: approved @ 1697603
beanstalk: approved @ 2c5a62e
beanstalk: approved @ 4fd82b7
beanstalk: approved @ 64d6d09
beanstalk: approved @ 6f91fae
beanstalk: approved @ 70a78f9
beanstalk: approved @ 8d361dc
beanstalk: approved @ 9d8356e
beanstalk: approved @ abf2227
beanstalk: approved @ d82f202
beanstalk: approved @ e5f1e40
beanstalk: approved @ ee8ab5f
ci: approved
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/macos
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
regression
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
ci: approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!669
Loading…
Reference in a new issue
No description provided.
Delete branch "mooki/Longshot:mac/giant-mem-gate"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type: fix · Risk: medium · Tested on: macOS 26.7.1, Apple M4 Max (MoltenVK 1.4.2), arm64 and x86_64 under Rosetta · Needs: macOS, Apple silicon (a start to play on the rebased stack); Windows and Linux, any GPU (build and start)
Why
On a Mac, the precompile's giant compute compiles go on behind the game (#652, #664), and each one costs Metal's
compiler service 10-15 GB while it runs. In start 21 the Mac's memory went from normal to critical in about 15 seconds while
the game loaded a scene with three giants compiling behind it, and the GPU device was lost; the log then called the lost wait a
capture's wait that "timed out (done 0)", which hid what happened. Nothing kept a new giant from starting under memory
pressure, and nothing in the log said how much memory the Mac had when the device was lost. This PR starts a giant behind the
game only while macOS's memory pressure is normal and one giant's room is free, no more at once than fit beside the game; play's
own giant compile waits while the pressure is critical; and on the Mac a wait that comes back
VK_ERROR_DEVICE_LOSTis said asa lost device, with the system's memory, and ends the game there. It is part of the macOS port. See #420.
How to reproduce
Seen on: macOS 26.7.1, Apple M4 Max, MoltenVK 1.4.2 (main does not run on macOS yet: seen on the macOS port's integration
build 09f9a171, which carries the open macOS PRs)
Settings and switches: defaults (start mode), the field-dawn save, slot 00
Steps: start the game, press START and A at the title so the save loads while the precompile still builds giants behind it.
Expected: no new giant compile starts while the Mac is short of memory; a lost device is said as one, with the memory.
Actual: start 21: pressure critical 15 s after it read normal, then the device was lost, logged as
a capture's wait for write-back 9646 timed out (done 0), with no memory reading (reports/2026-11-14-mac-startup-21.md,reports/2026-11-14-device-lost-21.md).What changed
Under
compiler_service(MoltenVK only), from the hand-off on, a giant may start behind the game only while the system'smemory pressure is normal and one giant's room (
RX_VK_GIANT_ROOM_MB: a quarter of memory, 16,384 MB at most) is available,and no more giants are in flight than fit beside the game (physical memory less the game's footprint, in rooms; 1 on this Mac
with the game at 34-36 GB). A giant held back is deferred as at the bound and built once memory is back, never dropped; giants
in flight finish. The precompile's tries alone wait for memory the same way. Play's own giant compile waits while the pressure is
critical, and stays ahead of every precompile giant. Before the game nothing changes (the memory is not even read). On the
Mac, a lost wait now ends the game at once with its memory said, after 200 ms for MoltenVK's own line.
gpu/vk/system_memory.h(from #642, which this branch merges): the pressure level, available memory, swap and thisprocess's footprint, read through
sysctlandtask_infoon macOS;known = falseelsewhere.gpu/vk/precompile_handoff.h(no Vulkan):giant_room_mb,memory_lets_giant_start,memory_giant_limit,memory_holds_play,MemoryTurn;Giants::memory(the gate inmay_startandsolo, with its pauses and resumes),Board::memory(play's wait inplay_begin).gpu/vk/vk_draw.cpp:giant_room(),compile_board()installs play's wait,precompile_runinstalls the gate at the hand-offwith its lines (
giant_memory_said,play_memory_said), new deferral reasons, the setup line and the done line's pause count.gpu/vk/vk_replay.cpp, on the Mac only (#if defined(__APPLE__); every other platform compiles the file's text as it was):wait_lostsays a timeline wait that comes backVK_ERROR_DEVICE_LOSTasvk: a wait for recording N came back VK_ERROR_DEVICE_LOST: the GPU device is lost(ora capture's wait for write-back N …; 8 lines at most) and goes todevice_lost()there;device_lost()says the system memory and waits 200 ms before the fatal line; the slow submit, slowpresent and slow frame lines gain the system memory. Every platform's version of the line is its own PR,
#670.
tests/test_giant_memory.py+tests/giant_memory_test.cpp(the gate's rules and lines, play's wait, the lost wait saidand ended on the Mac and as before elsewhere, every other platform's text of the waits, the memory not read before the game,
this Mac's real reading);
tests/test_slow_frame_limit.py(a stub for the new suffix).Measurements
Verdict: the diagnostics work in the game; the gate's own effect is not measured yet. In start 23 (Integration 31, which
carries this change) the device was lost with the Mac at warning pressure and 1 GB available, and the log said so in place of
the old "timed out (done 0)" line; the gate's setup line came, but no giant finished after the hand-off, so the gate was never
asked (it is asked when a worker finishes an item). In starts 24-26 the pressure stayed normal and the gate never paused. Start
23's loss came from the renderer's own GPU memory (19.7 → 53.0 GB in 14 s after one A), which #642 addresses; this PR
keeps the compiler services' 10-15 GB each out of such a window.
Baseline: main does not run on macOS yet, so both columns are the macOS port's integration builds, arm64, MoltenVK 1.4.2, the
field-dawn save, START and A at the title. Before: 09f9a171 (run #21), without this change. After: 552cc3d4 (run #23,
Integration 31, which carries it). One run each; each lost the device.
Hardware: macOS 26.7.1, Apple M4 Max (40-core GPU), MoltenVK 1.4.2, 16-core CPU, 64 GB
Scene: a start, the title, START and A, the save's load; windowless
Method: the game log's lines at the loss and the gate's lines, one run each
How to reproduce: a start of a macOS build from the field-dawn save with START and A at the title; read the
device lostanda giant compile starts only whilelinesa capture's wait for write-back 9646 timed out (done 0)a wait for recording 9290 came back VK_ERROR_DEVICE_LOSTRun reports:
reports/2026-11-14-mac-startup-21.md,-22.md,-23.md,-24.md,-25.md,-26.md; the analysis:reports/2026-11-14-device-lost-21.md,reports/2026-11-14-giant-mem-gate.md.Flags and settings
One new switch, read only with
RX_VK_COMPILER_SERVICE(MoltenVK); the diagnostics have no switch.RX_VK_GIANT_ROOM_MB0: no memory gate (the precompile's and play's)Testing
Tested: on macOS 26.7.1, Apple M4 Max, at this head (main
6c9edffeplus #650, #651, #652, #664, #665,#666, #642 and this PR):
tests/test_giant_memory.py --arch arm64and--arch x86_64(the fixtures run under Rosetta; this Mac's memory read for real):pass on both at this head (
reports/PubWave-files/PubLate1/gmg-proof-split/gmg-giant-*). On this branch's base (73e4f57c,#666 with #642 merged) with the test files copied in (sha256 in
gmg-test-files-split.sha256): fails on both (alost wait said as
timed out (done 0)anddevice_lostnot run; nogiant_room, no memory gate at the hand-off, nogiant_memory_said) (gmg-proof-split/gmg-base-giant-*).test_slow_frame_limit(arm64, and x86_64 throughclang++ -arch x86_64under Rosetta) at this head(
gmg-proof-split/); #642'stest_unified_budget(the same two) and on arm64test_loading_stall,test_retry_aloneandtest_precompile_handoffat6ddaaa71, whose code outsidevk_replay.cppthis head has (final-proof-02/gmg-*).tools/ls-suite.pyat
5a71da08(beforeaefba76a): 282 pass, 21 fail: the same 19 as at #652's head (16 also fail on main6c9edffeon macOS, 3only because named); main's
test_intel_quick_compute, fixed in #666's3cd25d0e; andtest_gpu_hang_watch("exit 3, 3.5 s" against its 3.4 s bound, at load 23-37), the known load-sensitive timing test, which also failed so at
#665's head without this PR (
giant-mem-gate-related-02/). At this head, the 88 checks that readvk_replay.cppandtest_giant_memory: 84 pass, 5 fail, the same 5 as before (andtest_gpu_hang_watchpasses;test_device_lost_exitpasses)(
giant-mem-gate-replay-03/,-compare.txt). This PR's 200 ms before the fatal line on the Mac falls inside the hang watchtest's window, so it leaves that test less margin on macOS.
gpu/vk/vk_draw.cppandgpu/vk/vk_replay.cppcompiled whole at this head for arm64 and x86_64 with main's renderer flags (with#476's and #492's macOS compile pieces in a scratch tree): 4 objects made, exit 0
(
reports/PubWave-files/PubLate1/compile/giant-mem-gate-split/).unifdef -U__APPLE__, base against head):vk_replay.cppis the same (tokens; one comment longer);precompile_handoff.handvk_draw.cppdiffer: the gate's code, compiled everywhere and reached only undercompiler_servicewith a known memory reading (none off Apple) (
reports/PubWave-files/PubLate1/offapple-giant-mem-gate-split.txt).Not tested: the gate pausing in the game (no start since had pressure while a giant was due); play's wait at critical pressure;
the game at this head (main does not run on macOS without the other macOS PRs); Windows and Linux (the gate needs
compiler_serviceandsystem_memory.hreads nothing there;vk_replay.cpp's text there is as it was): not built on Windows orLinux; its test request covers it.
Must be tested:
build behind it, expecting the gate's setup line,
giant compiles paused for memory/go on afterlines if the pressurerises, and at a loss the
came back VK_ERROR_DEVICE_LOSTanddevice lost: system memorylines (by hand: the parent's GPUslot).
aefba76a, it carries #650, #651, #652, #642, #664,#665 and #666) and main
6c9edffethe same way and start each to the title, expecting noa giant compile starts only while the system's memory pressure is normalline (the gate needscompiler_serviceand a memory reading, neitherthere), and every other line to match main's (by hand: test request T-XXXX).
Risks
What could break: on MoltenVK the giants behind the game build later when memory is short, one at a time on a Mac whose game
takes most of its memory; a dispatch that needs one meanwhile compiles it itself. Play's giant compile waits while the pressure is
critical, its dispatches left out meanwhile. On the Mac a lost timeline wait ends the game at once (as a submit that sees the loss
does) after its line, the system memory and 200 ms for MoltenVK's own line. Other platforms:
vk_replay.cppcompiles the text ithad; the precompile's new code there is compiled and not reached. The every-platform line for a lost wait is #670;
ending the game at such a wait on every platform, the pause before the exit and memory lines on Linux and Windows are left to the
owner (not in this PR).
Turning it off:
RX_VK_GIANT_ROOM_MB=0(no memory gate),RX_VK_COMPILER_SERVICE=0(none of #652's path), or revert the PR.Compatibility: Nothing changes for players: no shader stamp, cache format or
MKWVK_API_VERSIONchange.Reviewer notes
523c51e4..aefba76a(3 commits; in git's notation73e4f57c..aefba76a): the gate, play's wait, the lost-device linesand the tests; the switch's docs row; and the lost wait's line and end kept to the Mac (
aefba76a).play_beginand the alone slot this gate also holds), and through it on#665, #664, #652, #650 and #651; and on #642 for
gpu/vk/system_memory.h. The branch is#666's head
3cd25d0ewith #642's head263e462dmerged (73e4f57c, no conflict), then this PR's commits.-xfrommacos/giant-mem-gate(230dde4c), with no conflict; its change equalsthe topic's line for line. The topic's first commit
41b04c19(system_memory.halone) is not picked: #642 carries itas
1326bfac(cherry-pick -xof41b04c19), and the header here is byte-identical to41b04c19's.03439964adds theRX_VK_GIANT_ROOM_MBrow to
docs/development.md, which the topic did not have;aefba76akeeps the topic's lost-wait change to the Mac (the owner'srule: every-platform changes in their own PRs):
wait_lostand its end are Apple-only, the recordings' wait result and the earlyreturn it fed are gone (on the Mac
device_lostnever returns there),system_memory.h's include,device_lost's report andthe slow frame line are Apple-only, and every other platform compiles
vk_replay.cppas it was (unifdef -U__APPLE__: thesame tokens; one comment longer).
git merge-treeconflicts ingpu/vk/vk_replay.cpp, bothchanging the two waits. Whichever lands second keeps that PR's
wait_rec_resultand line on every platform and adds this PR'sdevice_lost()on the Mac after the line, as the topic first had it (macos/giant-mem-gate230dde4c).Board::giants()), which this PRleaves as it was; a probe in Integration 30 showed the saver and this gate cannot wait on each other (the gate defers a giant's
start, never one in flight, and
play_beginwaits before it counts).git merge-treeof this head:gpu/vk/vk_draw.cppwith#658 at the top of
dispatch(#666's), and #652's own (vk_main.cppwith #428, #498 and #653; #646's four files)(
reports/PubWave-files/PubLate1/merge-check-giant-mem-gate*.txt).Issues
See #420
Checklist
Everyone
type(scope): description) of the status line's type.main(maine090cc48).python -I -B tests/run_all.pypasses (any failure that also fails onmainis named). (On the wave's mergebfe5c752of this PR, then at03439964, and metal-cache2: 544 passed, 32 failed, 17 skipped against main 6c9edffe's 531/32/17 on this Mac; the 32 failures are main's own, nothing that passes on main fails, 13 new tests pass;reports/PubWave-files/waveC1/. The last commit,aefba76a, Apple-gates the lost-wait line: test_giant_memory, test_slow_frame_limit, test_device_lost_exit and test_gpu_hang_watch pass at the head, and the 88 checks that readvk_replay.cppfail only as before.)third_party/untouched (a new folder with aPROVENANCE.mdinstead).When it applies
Assisted-by:/Co-Authored-By:trailers.mainand passing here.tests/that reads changed source updated in this PR, not weakened.7707c634) under the precompile hand-off f9f166f7cb263e462d) under the giant compiles' memory gate 73e4f57c59aefba76a9c4d16d199ec4d16d199ec143345156dFolded into #849 (fix(macos): MoltenVK's precompile and pipelines on Metal)
Pull request closed