fix(vk): prepare again when the shader seed changes #1387
No reviewers
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
No milestone
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!1387
Loading…
Reference in a new issue
No description provided.
Delete branch "prepare-seed-stamp"
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?
What was wrong
prepare_run(the "Preparing shaders" start-up step andhookshot --prepare-shaders) decided whether to prepare again from the translator's stamp alone:hookshot_prepared.binheldprogram_cache_stamp()and nothing else. The seed,hookshot_shaders.bin, is the list of programs to prepare, and it can change while the translator stays the same. When an update grew the seed, a folder that had already been prepared never prepared the new programs. Each one was then translated in play the first time it was used, at 200-400 ms apiece.Evidence
4221050d("nx2: the shader census reads agl's archives, networks, the movie upscaler and the particle sets") added compute programs to the seed and left the translator as it was. Folders prepared before that commit don't have those programs in the program cache, and nothing prepares them.The fix
hookshot_prepared.binnow holds five words: the translator stamp, a hash of the seed's contents, the seed's size and write time, and when the record was written.std::filesystemthere gives whole seconds. As git does for its index, the shortcut is only trusted for a seed written more than 2 s before the record. A seed written closer to it is hashed, so a same-size rewrite within the time's resolution is still caught. A record without that time (four words) gets one hash. Hashing the 514 MB seed takes about 0.1 s from the page cache and 0.3-0.6 s cold; a byte-at-a-time FNV took 0.45 s from memory.vk: the game's shader list (hookshot_shaders.bin) is not the one the shaders were prepared from: preparing again (only the programs not kept yet).seed_translateskips any key the program cache keeps, andseed_computeskips any variant it keeps. So "prepare again" only makes the programs that are new, and the driver warm-up runs only for those.prepare_is_update(RX_VK_PREPARE_BACKGROUND) still reads only the first word, so it works the same.How it was tested
tests/test_prepare_seed_stamp.py(standard library; it compiles the record helpers taken fromvk_draw.cppand runs them on files in a temporary folder):prepare_runuses all of this. Replacing the seed comparison with "never changed" makes it fail.tests/test_startup_progress.pynow looks for the new write (prepared_write) after the stop branch.python3 -I -B tests/run_all.py: everything passes excepttests/test_program_cache_cleanup.py, which fails the same way on main without this change (FAIL:115 disk_in).hookshot_vk.dllcross-builds with llvm-mingw.hookshot --prepare-shaderson a new folder (RTX 3090, 24 threads). The "older" seed was the same seed with its last 40 programs removed:prepared 38809 shader programs (40 new, ...) in 10.0 s.0 newin 10.0 s, and the record is rewritten in the new form.This landed in the v2.0.12 branch (merged there for testing; it reaches
mainwith that release), so closing. Thanks!Reopened: closing this was a mistake on my side. It was tested in the v2.0.12 branch, and it will be merged into main here, so it shows as merged. Sorry for the noise.
Windows FILETIME follow-up on the inspected PR code: both
seed_file_idandfile_clock_nowoverflow signed 64-bit arithmetic when converting today's FILETIME values to nanoseconds. The ticks fit; multiplying by 100 does not.Clang 23.1.5 with signed-overflow UBSan reports both conversions. The proposed change below keeps native 100 ns ticks on Windows and scales the two-second slack accordingly; other platforms keep nanoseconds. Actual helper/record tests then have no sanitizer diagnostics. A synthetic old five-word record hashes once, rewrites its times and skips hashing on the next check.
The optimized old code happened to preserve the comparison in my CPU test. I have not reproduced a wrong preparation decision or a game stutter caused by this overflow.
Minimal follow-up patch
This applies to the inspected PR head; the release branch mentioned above is not published in the repository's branch list. Both Windows helpers and the slack must change together.