fix(vk): one pipeline play file a session, and the next start puts them together #2625
Open
NotRexed wants to merge 1 commit from
fix/play-cache-files into main
pull from: fix/play-cache-files
merge into: MagicBeanDev:main
MagicBeanDev:main
MagicBeanDev:github/pr/2893
MagicBeanDev:github/pr/2890
MagicBeanDev:github/pr/2887
MagicBeanDev:github/pr/2878
MagicBeanDev:github/pr/2875
MagicBeanDev:github/pr/2874
MagicBeanDev:github/pr/2873
MagicBeanDev:github/pr/2871
MagicBeanDev:github/pr/2756
MagicBeanDev:github/pr/2897
MagicBeanDev:github/pr/2896
MagicBeanDev:github/pr/2718
MagicBeanDev:github/pr/2842
MagicBeanDev:github/pr/2691
MagicBeanDev:github/pr/2895
MagicBeanDev:github/pr/2743
MagicBeanDev:github/pr/2765
MagicBeanDev:github/pr/2708
MagicBeanDev:github/pr/2732
MagicBeanDev:github/pr/2755
MagicBeanDev:github/pr/2817
MagicBeanDev:github/pr/2876
MagicBeanDev:release-please--branches--main
MagicBeanDev:release-please--branches--main--release-notes
MagicBeanDev:github/pr/2778
MagicBeanDev:github/pr/2888
MagicBeanDev:github/pr/2866
MagicBeanDev:github/pr/2877
MagicBeanDev:github/pr/2800
MagicBeanDev:github/pr/2829
MagicBeanDev:github/pr/2850
MagicBeanDev:github/pr/2889
MagicBeanDev:github/pr/2882
MagicBeanDev:github/pr/2860
MagicBeanDev:github/pr/2849
MagicBeanDev:github/pr/2886
MagicBeanDev:github/pr/2794
MagicBeanDev:github/pr/2840
MagicBeanDev:github/pr/2830
MagicBeanDev:github/pr/2834
MagicBeanDev:github/pr/2719
MagicBeanDev:github/pr/2742
MagicBeanDev:github/pr/2741
MagicBeanDev:github/pr/2820
MagicBeanDev:github/pr/2771
MagicBeanDev:github/pr/2859
MagicBeanDev:github/pr/2858
MagicBeanDev:github/pr/2823
MagicBeanDev:github/pr/2757
MagicBeanDev:github/pr/2769
MagicBeanDev:github/pr/2844
MagicBeanDev:github/pr/2843
MagicBeanDev:github/pr/2673
MagicBeanDev:github/pr/2855
MagicBeanDev:github/pr/2739
MagicBeanDev:github/pr/2807
MagicBeanDev:github/pr/2851
MagicBeanDev:github/pr/2789
MagicBeanDev:github/pr/2814
MagicBeanDev:github/pr/2818
MagicBeanDev:github/pr/2774
MagicBeanDev:github/pr/2772
MagicBeanDev:github/pr/2750
MagicBeanDev:github/pr/2786
MagicBeanDev:github/pr/2785
MagicBeanDev:github/pr/2782
MagicBeanDev:github/pr/2779
MagicBeanDev:github/pr/2775
MagicBeanDev:github/pr/2706
MagicBeanDev:github/pr/2766
MagicBeanDev:github/pr/2747
MagicBeanDev:github/pr/2759
MagicBeanDev:github/pr/2745
MagicBeanDev:github/pr/2740
MagicBeanDev:github/pr/2733
MagicBeanDev:github/pr/2725
MagicBeanDev:github/pr/2727
MagicBeanDev:github/pr/2728
MagicBeanDev:github/pr/2729
MagicBeanDev:github/pr/2721
MagicBeanDev:github/pr/2722
MagicBeanDev:github/pr/2715
MagicBeanDev:github/pr/2714
MagicBeanDev:github/pr/2707
MagicBeanDev:github/pr/2703
MagicBeanDev:github/pr/2705
MagicBeanDev:github/pr/2697
MagicBeanDev:github/pr/2679
MagicBeanDev:github/pr/2676
MagicBeanDev:github/pr/2669
MagicBeanDev:docs/what-hookshot-connects-to
MagicBeanDev:fix/hdr-encode-test
MagicBeanDev:fix/main-tests
MagicBeanDev:perf/game-barrier-order
MagicBeanDev:feat/heap-lazy
MagicBeanDev:feat/texture-detail
MagicBeanDev:perf/astc-opaque-bc1
MagicBeanDev:fix/fs-dir-lock
MagicBeanDev:fix/framegen-native-kept
MagicBeanDev:fix/update-no-downgrade
MagicBeanDev:port/k10-gcn-f16-mirrors
MagicBeanDev:fix/framegen-mult-cap
MagicBeanDev:chore/release-as-2.1.1
MagicBeanDev:perf/pcache-load-switch
MagicBeanDev:fix/prepare-progress-log
MagicBeanDev:fix/update-closes-stale-game
MagicBeanDev:fix/hdr-scaled-sampled
MagicBeanDev:perf/first-start-preset
MagicBeanDev:perf/zero-upload
MagicBeanDev:fix/vcore-escape-2s
MagicBeanDev:test/fake-gh-atomic
MagicBeanDev:perf/batch-idle-flush
MagicBeanDev:test/finish-stub-a
MagicBeanDev:chore/record-2.1.0
MagicBeanDev:fix/main-red-tests
MagicBeanDev:fix/test-display-startup-driver-cache
MagicBeanDev:feat/mesh-shader-mode
MagicBeanDev:fix/menu-probes-at-startup
MagicBeanDev:fix/fps-counter-classic
MagicBeanDev:fix/test-log-batch-flake
MagicBeanDev:fix/narrow-screen-hud
MagicBeanDev:ci/prereleases
MagicBeanDev:release/v2.0.15
MagicBeanDev:hotfix/v2.0.15
MagicBeanDev:fix/present-ahead-replay
MagicBeanDev:release/v2.0.13
MagicBeanDev:fix/bracket-paths-2013
MagicBeanDev:fix/hdr-overlays-2013
MagicBeanDev:fix/fps-counter-scale-2013
MagicBeanDev:fix/stick-seen-at-rest
MagicBeanDev:hotfix/menu-fits-screen
MagicBeanDev:fix/f1-always-opens-menu
MagicBeanDev:fix/ini-keep-lines
MagicBeanDev:claude/cranky-pike-7cf247
MagicBeanDev:perf/texture-answer-flat
MagicBeanDev:fix/stamp-test-modules
MagicBeanDev:fix/appimage-fonts
MagicBeanDev:fix/display-awake
MagicBeanDev:release/v2.0.12
MagicBeanDev:fix/discord-windows
MagicBeanDev:rc/v2.0.12-tests
MagicBeanDev:fix/dlss-keep-feature
MagicBeanDev:rc/v2.0.12
MagicBeanDev:button-icons
No reviewers
Labels
Clear labels
accessibility
Barrier affecting people with disabilities
autorelease: pending
autorelease: tagged
bug
Something isn't working
dependencies
Pull requests that update a dependency file
documentation
Improvements or additions to documentation
duplicate
This issue or pull request already exists
enhancement
New feature or request
github_actions
Pull requests that update GitHub Actions code
good first issue
Good for newcomers
help wanted
Extra attention is needed
invalid
This doesn't seem right
question
Further information is requested
testing wanted
wontfix
This will not be worked on
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
Milestone
Clear milestone
No items
No milestone
Assignees
Clear assignees
No assignees
2 participants
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!2625
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/play-cache-files"
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?
The bug (#2579). Every save in play (every 32 new pipelines) wrote its own
mkw_vk_pipelines_play_<n>.bin. The files are folded into the main pipeline cache only once a start finds 16 of them, so a long session can leave more than that: the reporter had 22, and the owner's test folder had 13.The fix (
gpu/vk/vk_draw.cpp):So the folder holds at most the last start's combined file plus the current session's. The 2.5 GB main cache is still not rewritten during play.
Tests:
tests/test_pipeline_play_cache.pyand its C++ model are updated:All 9 cases pass, and
test_pipeline_cache_savepasses.Fixes #2579
🤖 Generated with Claude Code
I am handling the separate file-backed I/O scope in #2623: remove whole-cache temporary heap vectors from base/play reads and writes, preserving the current policy. Your one-file-per-session and startup-consolidation behavior stays in this PR.
I reviewed
1b644a20: it still goes throughpcache_file_readandplay_file_write, so the storage helper can remain underneath your changes. The extraction fixtures overlap too; whichever lands second should retain both the session/retry assertions here and the native I/O failure/actual-length checks there.The file-backed candidate has passed a local three-process load/save/reload cycle on Windows, including a forced base save and a fresh read of that output. Its PR is not open yet; I will link it when the current full suites and final overlap audit finish.
I merged this into current main (it merges cleanly) and ran tests/run_all.py on Linux with the Vulkan headers. 457 checks pass. The only failures are test_lift_cache, which fails on main here, and a test_recorder_sync timeout under load that passes when run alone. test_pipeline_play_cache and test_pipeline_cache_save pass. The red CI is main's own breakage from that morning, so a re-run should clear it. Three things to know, none blocking:
This overlaps #2649, which rewrites play_file_write and pipeline_play_cache_test.cpp, and #2456. Whichever lands second keeps both sets of assertions, as Arkex-ai said.
With this branch, a start finds at most two play files (the last combined file and the last session's), so the
>= kPlayFilesKeptbranch above can't be reached any more, and the play files never go intomkw_vk_pipelines.bin. The combined file then grows with every session and is rewritten whole here at every start. Could the fold trigger on size instead (e.g.merged_kbpast 64 MB, like the in-play threshold), so it still fires?Small, and the old per-save numbers had the same issue: a second instance started from the same folder takes the same
play_seq + 1, so the two sessions overwrite each other's file (and share its.tmp). If it's cheap, picking the first number whose file doesn't exist yet (or creating the.tmpexclusively) would keep two sessions apart. A nit, not a blocker.Thanks, this goes at the right thing: one file per session, rewritten through the same
.tmp+ rename, is a clear improvement over a new file per save, and the tests follow the new behavior well.Crash safety looks fine. Every write still goes through
.tmpand a rename, so killing the game mid-session leaves the session file as of its last good save (only the generation since then is lost, as before). A kill at start between writing the combined file and removing the originals just leaves duplicates, which the next start merges again. Two instances at once: both takeplay_seq + 1for their session and share one.tmpname, so they overwrite each other's session file. That was already true of the per-save numbers before this PR, so I wouldn't block on it, but see the inline note for a cheap guard.One real problem: the fold into
mkw_vk_pipelines.bincan no longer happen (inline at the start merge). After this change the folder holds at most two play files at any start, somerged.size() >= kPlayFilesKeptis never reached again, except for folders that already have 16 or more. The combined play file then keeps every session's pipelines forever. It is read, merged, held in memory and rewritten whole at every start, and it grows without limit. That is the same pattern of rewriting a big file again and again that the play files were added to avoid, only moved to startup. It also puts all play data in one file, so a single bad write loses all of it. The 16-file trigger needs a replacement that still fires. I'd go by size: fold into the main cache when the play files merged at a start total more than some threshold, for example the 64 MB thatRX_VK_PLAY_SAVE_START_MBalready uses in play. A test that goes through several sessions and still ends with the main cache written would pin this down.Joey-Magnus's three points still stand and I agree with all of them: the session file is rewritten whole at each save (writes grow with the square of the session), the session cache stays in memory until exit, and a generation whose merge into the session cache fails is never written because the per-file fallback only runs without a session cache. The last one is a small fix: on a failed merge, write that generation to a file of its own as before.
Evidence: so far there are only the model tests. For a fix, our rule is that it has to be shown gone in the game. Please add the log lines and a folder listing from three or more starts in a row, showing the play file count staying at one or two and the fold into the main cache happening. Because of the rewrite-per-save cost, please also add the session file's size and save times over a long first visit (from the existing
play file %u savedlines), with the GPU named.CI: all five failures come from main, not this PR. The base commit's own push run failed the same way: the AppImage job fails on a
std::mintype mismatch invk_texture.cpp, and the source checks fail on unrelated tests (audio, dlss, env_on, wayland, etc.).test_pipeline_play_cacheandtest_pipeline_cache_savepass on both OSes. A merge of current main should turn it green. It merges cleanly, and main hasn't touched these files since.Overlaps: #2649 (pipeline cache persistence via mappings) and #2456 (Intel saves:
play_file_writesignature, the drain wait inplay_cache_save) both rewrite the same functions andpipeline_play_cache_test.cpp, so expect textual conflicts. #2649 is still a draft with open Linux issues, so this one should land first and #2649 rebase onto it. One thing to watch when #2649 rebases: with its unique temp names, each interrupted rewrite of a growing session file leaves a full-size orphan, so its temp sweep at start matters more after this PR.