WIP: fix(vk): a game ended while it closes, after a quit during the precompile, no longer counts as a crash #498
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
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/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!498
Loading…
Reference in a new issue
No description provided.
Delete branch "mooki/Longshot:fix/double-quit-precompile"
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?
Depends on #428. This branch is #428's commit (
4dd978d6) plus one commit. Review it after #428, or fold it into #428.Why
Quitting during the start's precompile and then quitting again still counted as a crash. That is the usual reaction when
the game stops answering after the first quit. Two such starts dropped the precompile's notes.
With #428, a quit during the start's pipeline steps does not count as a crash. Those steps are the precompile, the
create-mode warm, the predicted pipelines and the stage libraries. But #428 puts each step's file back only at the end of
the close. Before that, the close waits up to 10 s for the preparation's workers to stop, on the window's thread.
Meanwhile the window does not answer:
A second quit there ends the process before the files are put back, so the start counted as one that crashed in the step.
After two such starts the notes were dropped and the warm was skipped. After one, the stage libraries were left out.
What this is for: a quit by the player or the control socket never counts as a crash, however the process ends after
it. A crash, or a kill with no quit asked for, still counts. What is measured:
tests/test_quit_not_crash.py, extendedhere. It now also has starts ended by a kill and quits whose process is then killed while it closes. It fails 78 of its 101
behaviour checks on
mainand 36 on #428, and passes here. Not measured: no game run, and nothing on Windows or Linux(see Testing).
What changed
The problem (lines at #428's head
4dd978d6, and atmain9c9d69edforhost/and the worker):mkwvk_close_asked(gpu/vk/vk_main.cpp:1567-1580) runs in this order:programs_census_log,module_ids_flush;fsr4_closeandpipeline_cache_close, each of which can wait up to 10 s in play;prepare_stop()andWaitForSingleObject(prep_ended, 10000)(1573-1577);crash_markers::close()(1578).(
vk_draw.cpp:8728,8742at5e072fc8). Once stopped, the worker also saves the pipeline cache before it signals that itended (
vk_main.cpp:1191atmain). So a compile under way uses up the 10 s.WM_CLOSEin the window procedure (host/display.c:489-493). During the start-up screen, that thread is therenderer's start (
vk_main.cpp:1129-1132atmain).host/posix/display_sdl.c:348-351,426-434).seconds", and its ghost window lets the user "close the unresponsive application"
(Microsoft).
The change:
gpu/vk/vk_main.cpp:crash_markers::close()is now the first statement ofmkwvk_close_asked, before anything thattakes time. The rest of the close is unchanged and in the same order. The comment above the function says so.
gpu/vk/crash_markers.h: comments only. The header's rule is now "only a crash, or a kill that no quit asked for, maycount".
close()'s doc says it is called first, and that a worker that crashes the driver after the quit is no longercounted.
tests/test_quit_not_crash.py:kill: ended from outside while the step works, with no quit asked for.quit-killed-early: a quit whose process is then ended in the close's first part other than the files.quit-killed-waiting: the same, ended while the close waits for the workers.vk_main.cpp, in its order. The preparation's stop and thecrash_markerslines arekept as they are. Every other statement becomes a stand-in where the process can be ended.
crash_markers::close()after the wait. It now wants it as the close's first statement (thereason is under Risk).
Unchanged: the four steps, their files and thresholds,
host/, and every exit path.Flags and settings
None. No switch, menu row, ini key or default changes.
Measurements
main:
5e072fc8(#428's base, 2026-11-14), and9c9d69ed(2026-11-14) for the test against its sources#428: fix/quit-is-not-a-crash @
4dd978d6this PR: fix/double-quit-precompile @
6219fbbeHardware: Apple M4 Max (16 cores), 64 GiB, macOS 26.7.1 (25G241), Apple clang 21.0.0, Python 3.14.10. No GPU or driver is
involved: the checks are game-free.
Scene: none (game-free checks only)
Method:
tests/test_quit_not_crash.pyfrom this branch, run on its head and against the other trees' sources (--source):main, #428, main, #428, this PR, then the same again.
branch with this commit applied.
run_all.py --jobs 6).This is correctness evidence, not a timing comparison.
How to reproduce:
python -I -B tests/test_quit_not_crash.py --cxx clang++, then the same with--sourceset to a checkoutof
mainor of #428. Run it from this branch's checkout, so that the test is this PR's. Thenpython -I -B tests/run_all.py.Each check runs one step as a series of starts in a new folder, each start ending as told, then one start that gets
through. That last start shows whether the step was still tried. Every quit comes while the step runs, before the start
hands over to the game.
5e072fc84dd978d66219fbbetests/test_quit_not_crash.py5e072fc8vk_main.cppobjects, arm64 and x86_64vk_main.cppandvk_draw.cpp, with a receipt-only header declaringposix_fallocate(missing on macOS, as for #428)mainBoth runs on each tree printed the same output, byte for byte (the test is deterministic). From
main's run (7 of its 88lines):
From #428's run, the same
quit-killed-waitinglines for the precompile, the other three steps andquit-killed-early, and:and on this PR:
The suite's summary lines,
main5e072fc8first, then #428's and this PR's:The runner calls this Mac
linux; these are macOS results.Testing
Tested:
main(5e072fc8and9c9d69ed), #428, and the macOSport's integrated branch with and without this commit. The marker code and the close it runs are cut from
vk_draw.cppand
vk_main.cppas they are.mainlacks (draw_layouts_report); the test took it as a stand-in withoutchange. Its precompile hands off to the game while giant compiles go on, and a quit before that hand-off is covered there.
main5e072fc8and on #428, and so are its17 skips: this PR adds no failure and removes none. The 19 are
test_appimage_package_retry,test_collect_wide,test_control,test_framegen_resources,test_game_layout,test_low_memory_notice,test_mem_gate,test_mem_gate_children,test_mirror_trim,test_module_ids,test_oot_state_tap,test_pad_layout,test_pcache_header_read,test_pipeline_cache_file,test_pipeline_cache_save,test_pipeline_play_cache,test_prof_symbols,test_program_cache_cleanupandtest_program_cache_one_variant(not triaged here).test_quit_not_crash,test_predict_resume,test_prepare_stall,test_prepare_cache_save,test_fsr4_tune,test_startup_progressandtest_vendor_fixes.Not tested:
and changes comments.
_Exitfrom another thread while the close waits. It stands in for Force Quit or End task: an endfrom outside after which nothing more of the process runs. No real Force Quit was used.
Needs other testers:
close it again ("Close the program"). Do this at two starts in a row. Expect, at the third start, no "its notes are
dropped" line in the log, and
longshot_pipelines.binkept (request to open).request to open).
Screenshots
Nothing on screen.
Risk and rollback
let go after two crashes, with at most one extra start.
begin()andend()do nothing afterclose().#428 the same happened when the step finished after its
close().Reviewer notes
mkwvk_close_asked(one call moved), thenclose()'s doc incrash_markers.h, then the test's new ends of astart and its close.
git merge-tree): clean withmain9c9d69edand onto the integrated branch.vk_main.cpp,crash_markers.hor the test, this commit adds no conflict to#428's.
vk_draw.cpp, which this commit does nottouch.
Issues
See #420
Checklist
For everyone
type(scope): description).main. — On #428's head (main5e072fc8); it merges withmain9c9d69edwithout conflicts. It will be rebased with #428, in one sweep before ready.python -I -B tests/run_all.pyrun and passing (any failure that also fails onmainis named above).main, #428 and this PR, interleaved twice each, on the same Mac, with the same result each time (it is deterministic); no frame time applies.third_party/untouched (a new folder with aPROVENANCE.mdinstead).If applicable
Assisted-by:/Co-Authored-By:trailers.mainand passing here.tests/that reads changed source updated in this PR, not weakened.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.