WIP: fix(macos): put a dispatch's compute pipeline ahead of MoltenVK's giant compiles and retry it alone, not fail it for good #666
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!666
Loading…
Reference in a new issue
No description provided.
Delete branch "mooki/Longshot:mac/loading-stall"
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 goes on behind the game with a few giant compute compiles in flight (#652, #664), and
Metal's compiler service sometimes ends under them: in starts 17 and 18 it ended under 13 and 8 compiles. A dispatch whose
compute pipeline the game builds itself in that window waits behind those giants, and if its compile fails with them it is
failed for good: the program is never given out again and every dispatch of it is left out for the rest of the session. The
precompile had its own retry for a failed giant (#665) that knew nothing of play's. This PR lets a compile the game
waits for go ahead of every giant not started yet, tries one that failed beside a giant again alone instead of failing it, joins
that retry and the precompile's into one place with one bound per program, and names each dispatch left out when asked to. 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 from the source on the macOS port's
integration builds 4e472987 and 1cbb594a, runs #17 and #18)
Settings and switches: defaults (start mode)
Steps: start the game; while the precompile builds giants behind the game, load a scene whose dispatch compiles its own compute
pipeline.
Expected: the dispatch's pipeline is built, ahead of the background giants, and a failed one is tried again.
Actual: the dispatch's build starts beside the giants; if Metal's compiler service ends under it the pipeline is failed for good
and every dispatch of it is left out (
reports/2026-11-14-loading-stall.md§1-§2; runs-mac-startup-17.md,-18.md). Runs#18 and #19 also showed no way to see which dispatches were left out and why.
What changed
Under
compiler_service(MoltenVK only): a dispatch's compute build is wanted on one compile board from its push; while acompile is wanted, no precompile giant starts behind the game (deferred ones included). A dispatch's build that fails with
VK_ERROR_INITIALIZATION_FAILEDwhile another giant compile was in flight is handed to a thread of its own and tried againalone, once no giant compile is in flight and with none starting beside it, each try with a new pipeline cache; until then its
dispatches are left out and counted as still compiling, not failed. The precompile's tries alone and play's share one queue on
the board (play's first) and one bound,
RX_VK_ALONE_TRIES(2) per program on both paths together; used up, the program issaid once and failed as before.
RX_VK_SKIP_LOG=1names each dispatch left out, once per program key and reason, on everydriver. Every other driver's builds and skips are unchanged.
gpu/vk/precompile_handoff.h(no Vulkan):Board(play's wants and its giants in flight;play_begin/play_end/overlapped;the one alone slot:
alone_ask/alone_go/alone_again/alone_cancel/alone_begin;alone_left/alone_spend/alone_used_upper program),retry_alone,alone_tries;Giantsshares the board (share,held_back), and itssolo_*calls go to the board's slot.
gpu/vk/skip_log.h(new):Seen(each key and reason once, at most 512 lines).gpu/vk/vk_draw.cpp:compile_board(); indispatchthe build's want (BuildWant, let go by its own scope on every return orthrow), the compile counted on the board,
compute_retry_alone/compute_alone_one(a thread of its own);alone_asked,alone_tried,alone_used_up(one wording for both paths);precompile_runshares the board and itscompute_alonetakes theboard's slot and bound; every
SKIP_HERE()indispatchisSKIP_DISPATCH(key, why), which asksskip_log_on()first.docs/development.md: rows forRX_VK_ALONE_TRIESandRX_VK_SKIP_LOG; theRX_VK_COMPILER_SERVICErow points theprecompile's retry to
RX_VK_ALONE_TRIES.tests/test_loading_stall.py+tests/loading_stall_test.cpp(the board, play first, the retry's bound, start 18's shape,the skip log, and the wiring);
tests/test_retry_alone.py+tests/retry_alone_test.cpp(one slot, play first, the hold andplay's wants holding only the precompile's tries, the per-program total said once, and the wiring).
Measurements
Verdict: nothing to compare yet: no compile failed beside a giant in the starts that carry this change (21-24), so the retry
alone never ran; the skip log named every dispatch left out (9 keys, all
its program did not translate: the movie upscaler'snetworks, left out on purpose off NVIDIA; start 24 also 9
its compute pipeline is still compilingduring the load into play).What the change does is shown by the game-free tests below.
Baseline: main does not run on macOS yet, so both columns are the macOS port's integration builds, arm64, MoltenVK 1.4.2. Before:
1cbb594a (run #18), without this change. After: 09f9a171 (run #21, Integration 28, which carries it), with
RX_VK_SKIP_LOG=1.One run each.
Hardware: macOS 26.7.1, Apple M4 Max (40-core GPU), MoltenVK 1.4.2, 16-core CPU, 64 GB
Scene: #18 a start to the title and the menu; #21 a start, the title and the field-dawn save's load; windowless
Method: the game log's skip and retry lines, one run each
How to reproduce: a start of a macOS build with
RX_VK_SKIP_LOG=1; readvk: dispatch of … left outandtried again aloneRun reports:
reports/2026-11-14-mac-startup-18.md,-19.md,-21.md,-22.md,-23.md,-24.md; the analysis:reports/2026-11-14-loading-stall.md; the reconciliation:reports/2026-11-14-integration-2.md, Integration 28.Flags and settings
Two new switches: one for the retry's bound (with the compiler service), one log switch, off by default, on every driver. The
lines this PR touches also read
RX_VK_GIANT_WORDS(the board's giant size, #664's switch) andRX_VK_SKIP_PROG(main's), both unchanged.
RX_VK_ALONE_TRIES0: none, a failed compile is failed at onceRX_VK_COMPILER_SERVICERX_VK_SKIP_LOG=1: each dispatch left out named once per key and reasonTesting
Tested: on macOS 26.7.1, Apple M4 Max, at this head (main
6c9edffeplus #650, #651, #652, #664, #665 andthis PR):
tests/test_loading_stall.pyandtests/test_retry_alone.py, each--arch arm64and--arch x86_64(the fixtures run underRosetta): pass on both;
tests/test_precompile_handoff.py(both arches) andtests/test_warm_handoff.py(arm64) still pass(
reports/PubWave-files/PubLate1/final-proof-02/ls-*atfedbfd1e; the first two again at this head,final-proof-04/ls-*).On this branch's base (#665
d429f0f0) with the test filescopied in (sha256 in
ls-test-files.sha256): both fail on both arches (loading_stall_test.cppdoes not build withoutskip_log.h; the source checks name the missing board, want, retry and named skips;test_retry_alone: no one place or countfor the tries alone) (
tsls-proof-01/ls-base-*). Before the adaptation commit (6f62edda), with this head'stest_loading_stall.py: fails on the one new check,BuildWantnot first (final-proof-02/ls-pre-loading-arm64).ones (301 names), through
tools/ls-suite.pyatfedbfd1e: 281 pass, 20 fail: the same 19 as at #652's head (16 also fail onmain
6c9edffeon macOS; 3 are skipped on macOS in a full run and fail only because named), and main'stest_intel_quick_compute, which found the dispatch's build by its old capture list;3cd25d0eupdates that one check, and itpasses at this head (
loading-stall-related-02/,-compare.txt;final-proof-04/ls-iqc).gpu/vk/vk_draw.cppcompiled whole for arm64 and x86_64 with main's renderer flags (with #476's and #492's macOS compile piecesin a scratch tree): both objects made, exit 0 (
reports/PubWave-files/PubLate1/compile/loading-stall/).unifdef -U__APPLE__, base against head):precompile_handoff.handvk_draw.cppdiffer andskip_log.his newand included there: the board and the retry are compiled on every platform and reached only under
compiler_service; thenamed skips (
SKIP_DISPATCH,skip_log_on()) run on every driver, saying nothing unlessRX_VK_SKIP_LOG=1(
reports/PubWave-files/PubLate1/offapple-loading-stall.txt).Not tested: the game at this head (main does not run on macOS without the other macOS PRs); a compile that fails beside a giant
and is made alone (none in starts 21-24); Windows and Linux (the board runs only under
compiler_service, off there; thenamed skips and the skip log are compiled and reached there, with the log off by default): not built on Windows or Linux; its
test request covers it.
Must be tested:
RX_VK_SKIP_LOG=1, expectingthe skip log's lines, and any
tried again alone (k of 2 …)line endingmade(by hand: the parent's GPU slot).3cd25d0e, it carries #650, #651, #652, #664 and #665)and main
6c9edffethe same way and start each to the title, once as it ships and once withRX_VK_SKIP_LOG=1, expecting nonew line as it ships and, with the switch,
vk: dispatch of … left out: … (RX_VK_SKIP_LOG)lines once per key andreason, and every other line to match main's (by hand: test request T-XXXX).
Risks
What could break: on MoltenVK a giant the precompile defers while play wants a compile builds later; a dispatch whose build
waits for its try alone stays left out until then (at most two tries a program); one queue thread is started at the first retry.
On every driver
dispatch's left-out paths askskip_log_on()(a static bool) before working out a key or reason; off, theycost what they did. The want on the board is let go by the build's own scope, so a build that throws or returns early lets it go.
Turning it off:
RX_VK_ALONE_TRIES=0(no retry: a failed compile is failed at once, as before);RX_VK_COMPILER_SERVICE=0(no board at all); or revert the PR.
Compatibility: Nothing changes for players: no shader stamp, cache format or
MKWVK_API_VERSIONchange; the log switch is offby default.
Reviewer notes
7a8ede63..3cd25d0e(7 commits; in git's notationd429f0f0..3cd25d0e): the board, the retry alone and the skip log;a test fix; the skip log costing nothing when off; the build's want by scope; the reconciliation with #665's retry
alone (one place, one count, one wording,
tests/test_retry_alone.py); and the adaptation to main (fedbfd1e,3cd25d0e).Giants' hold), and through it on #664, #652,#650 and #651. The branch is #665's head
d429f0f0plus this PR's commits.-xfrommacos/loading-stall(830c1a02 40683ae5 8885591d 9e1a0402), made onthe integration branch, then the integration branch's reconciliation
d8951420(Integration 28: two retries alone, theprecompile's and play's, counted apart until then). Conflicts in the pick:
gpu/vk/precompile_handoff.h: taken as Integration 28 merged this topic onto #665 (2c4a9231), whose header beforethe merge is byte-identical to this branch's base.
gpu/vk/vk_draw.cppbehind_worker's deferral line: the board, then #665's hold, then the bound (as2c4a9231).gpu/vk/vk_draw.cppdispatch: main's code (#252's Intel worker pathon_worker/made_how, the quick first build andcompute_optimize,jobs_push_serial's third argument; the program namedp, which the integration branch's #663 renamesplain) with this PR's want, retry and named skips. The integration branch's segmented binding (#663-#663) and memoryadmission (#658) lines in
dispatchare not on main; their skips are not here.tests/seg_gathers_test.cpp(one stub line,SKIP_DISPATCH): the file is not on main (#663-#663's); that PR's fixture needsthe line once both are in.
docs/development.md: both topics' rows; main'scache/longshot_pipelines.bin.slow(#321).tests/test_loading_stall.pyatd8951420:9e1a0402'sBuildWantneedles kept with the reconciliation'salone_leftbound,as Integration 28 resolved it.
fedbfd1e:BuildWantopens the build, so main's Intel worker path's early returnslet play's want go too (on the Mac
on_workeris never set; the scope makes the rule hold by construction); andtest_loading_stall's checks follow main's build (capture list, the serial push,p, the order check against the build's ownlast store, 8 named skips in main's dispatch).
3cd25d0e: main'stest_intel_quick_compute(#252) finds the dispatch's buildby its capture list, which now also has
board_on; what it checks inside is unchanged.git merge-treeof this head:gpu/vk/vk_draw.cppwith #658 (
mac/renderer-core): its memory admission lines at the top ofdispatchagainst this PR's named skips; keep #658'sadmission block with this PR's
SKIP_DISPATCHlines. Otherwise the same conflicts as #652's own head (vk_main.cppwith #428,#498 and #653; #646's four files), none from this PR's lines (
reports/PubWave-files/PubLate1/merge-check-loading-stall*.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 merge of the macOS PRs this one is built with,bfe5c752(metal-cache2 + giant-mem-gate, which carry this PR): 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/.)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 f9f166f7cb3cd25d0ead9260653dc89260653dc891b67ad468Folded into #849 (fix(macos): MoltenVK's precompile and pipelines on Metal)
Pull request closed