WIP: perf(build): a renderer header edit compiles the files that read it, not the whole back end #37
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/macos
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
ci: approved
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!37
Loading…
Reference in a new issue
No description provided.
Delete branch "dyst/Longshot:perf/renderer-depfiles"
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?
Why
Editing any renderer header recompiles the whole back end: on Windows a comment in
vk_present_rect.h, which two files include, compiled 35 files again (417 s of compiler time; a 44 s build against 26 s for a.cppedit, and minutes on a 4–8 thread PC).What changed
nx2/build.pycompiles the back end's own files (gpu/vk/*.cpp, not Eden's or sirit's) with-MD, and clang writes a depfile beside each object (vk_x.dforvk_x.o,depfile_of).depfile_pathsreads a make-style depfile (the target left out, line continuations joined,\,$$and\#).renderer_depskeys each object by the headers its depfile lists under the same folders as before (vdeps_roots:gpu/vk,gpu/render,gpu/compat, suyu's texture decoders), by name and content (_HEADER_DIGESTS). A header the file doesn't read no longer changes its key; one it reads, or one that's gone, does.tree_headers) stays as the fallback, for a file with no depfile yet or one whose depfile names nothing under those folders.flag_textleaves-MDout of the key, so no key changes and nothing compiles again for this change itself; depfiles appear as files are compiled.compile_oneneither caches nor stamps an object under a key that came from the last compile's depfile (it hashes the new source with the old include list): it stamps itDEPFILE_PENDING.rekey_depfiles, run inmainafter the compile, stamps and caches each back-end object of this build (DEPFILE_JOBS) by the key its own new depfile gives, also when that is the planned key; an object stamped otherwise is another build's and is left alone. A build that stops before re-keying leaves the object pending, and the next build compiles it again. This answers hckrman101's review (an include added, removed, the header edited, the include back used to get the first object back from the cache).tests/test_renderer_depfiles.py(new, 11 cases).tests/test_compiler_failure.py,tests/test_mem_gate_leases.pyandtests/test_compile_retry_jobs.pytakeDEPFILE_DEPSandDEPFILE_PENDINGwithcompile_one;tests/test_dlss_sdk_cache.pyandtests/test_shader_stamp_cache.pyrun the job loop with its new helpers.Flags and settings
None. (The back end's files are compiled with
-MDnow: a compiler flag inside the build, not a switch, and left out of object keys.)Measurements
Measured on the GitHub version (https://github.com/NotRexed/Hookshot/pull/2706), not yet on Longshot: this PR stays WIP until it is.
Hardware: Ryzen 9 3900X, NVMe, Windows
Scene: none (a build measurement)
Method: one build after a comment was added to
gpu/vk/vk_present_rect.h, without depfiles and with them; then the header revertedvk_present_rect.hvk_framegen.cpp,vk_menu.cpp,vk_replay.cpp, andhost/hos_main.c, through the host's own blob.In the game (a check, not a comparison): RTX 3080 (NVIDIA 621.14), Ryzen 9 3900X, 64 GB, Windows 11, this branch's
build.py(with the review fix) on GitHub'smainee29dd55. A build with depfiles, then one with a comment added tovk_present_rect.h: 5 files compiled (the four above, andvk_hangdump.cpp, which reads no repo header, so its depfile names nothing under those folders and it keeps the blob), no object left pending; with the comment taken out again, 0 files. That game, launched headless through the control bridge from a save: in play in Kokiri Forest, 27–34 fps over five samples, nothing in the log about a lost device or a crash.Testing
Tested: the runs under Measurements (on the GitHub version) and the game-free checks this description names. Checked but not measured: the game on Windows (above): the change is build-only, and nothing in play was compared with
main.Not tested: Linux. gcc and clang write the same make-style depfile.
Needs other testers:
gpu/vkheader, expecting only the renderer files that include it compiled again (request to open)Screenshots
Nothing on screen.
Risk and rollback
rekey_depfilescover a header read for the first time; the remaining limit is make's and ninja's: a newly added header that shadows another earlier in the include path isn't seen until something the file reads changes.Reviewer notes
Carried over from https://github.com/NotRexed/Hookshot/pull/2706 with the move to Longshot: the same change on Longshot's
main, with its rename applied. Its review and discussion are there (reviewed by Joey-Magnus, jordanella).run_all.py): every check that readsbuild.py(39), on Windows, passes apart fromtest_driver_cache_folder,test_fpx_stickyandtest_linux_update, which fail the same way onmainthere.tests/test_renderer_depfiles.py: the depfile read as clang 23 writes it on Windows (/and\in paths with spaces, CRLF continuations), plus$$and\#;-MDin no key; no depfile keeps the blob; an unread header or one outside the folders changes nothing, while a read header or a gone one changes the key; the re-keying, and that it leaves alone an object not of this build; the wiring; and hckrman101's four steps, with a stand-in compiler that writes the headers it read into the object and a real depfile (on the previous commit step 4 got back step 2's object, built with the oldb.h). It fails againstmain'sbuild.py.test_dlss_sdk_cache.pyandtest_shader_stamp_cache.pystill take an olderbuild.py(--build-script), andtest_shader_stamp_cachepasses againstmain's.Issues
None.
Checklist
For everyone
type(scope): description).main.python -I -B tests/run_all.pyrun and passing (any failure that also fails onmainis named above).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.🤖 Generated with Claude Code
perf(build): a renderer header edit compiles the files that read it, not the whole back endto WIP: perf(build): a renderer header edit compiles the files that read it, not the whole back endBack to WIP under #48's process: this was carried over before the new template. Its description will be brought to the template and measured on Longshot (main against this PR, at named commits) when it's wanted for review; ask here if you'd like it sooner.
Description brought to Longshot's PR template (#48). Still WIP: its numbers are from the GitHub version, and it isn't rebased on current main; it gets measured on Longshot (main against this PR, at named commits) before it is marked ready.
Description moved to the template's Testing section and whole checklist (#72): what was tested, what wasn't, and what needs other testers (their test requests to open while this is WIP).
The description follows the template after #76 and #106: If applicable keeps only the boxes that apply (one that applies and isn't done yet stays unticked while this is WIP), the Flags table has the template's seven columns (Goal is now Recommendation), and GitHub references are full URLs. Nothing else changed.
Read head
13d72239, all seven changed files, the description and four forge comments, the empty forge review list, the archived original review, and the planning, compile, cache and link callers; ran all six changed checks and five existing build checks on Linux, with GCC and Clang for the DLSS fixture: all passed. No game, full build or whole suite was run.What's good
cf605c85: each of the back end's own objects gets the same complete renderer-header digest. Narrowing that digest to the headers the compiler read addresses the cause, while a clean build or an absent depfile keeps the conservative fallback.compile_oneleaves a depfile-keyed compile pending rather than caching the new source under the previous include list. After all compiles succeed,rekey_depfilesreads the new list, publishes the correct cache entry and stamps the object before linking. The new 11-case check passes, including add include, remove include, edit header, add include again.Must change
No functional defect found in this patch. It is 117 commits behind main
cf605c85, with one commit of its own, but the actual merge is clean. Since its base82de567a, main's only changes tonx2/build.pyconcern FSR header discovery and selection of the prebuilt renderer, not these object keys. Being behind main is not the reason for a change request.Measurements and smaller points
Keep the historical result separate from a Longshot speed claim. The table's 44 s versus 27 s is one Windows build per side, with neither commit identified. There are no repeated alternating runs, median or spread, so it does not meet the stated measurement rule. The description correctly keeps this WIP. For the new comparison, name both commits and the compiler, keep the work-folder retention and cache state equal, state how depfiles were populated, and retain at least main / PR / main / PR with every build's file list, compiler time and wall time. Show the first depfile-producing build separately from the later header-edit builds.
The older Linux result is already available, but is not a test of this head. It reports 35 files versus 5, real GCC and Clang checks of the stale-cache fix, and successful starts on GitHub head
f0f345b6: https://github.com/NotRexed/Hookshot/pull/2706#issuecomment-6829816712. Link that result beside the Windows evidence rather than leaving Linux wholly untested. Keep its original commits and its explicit lack of a wall-time claim. The older four-file table and later five-file observation should remain separately labelled.Explain why the extra files rebuild. At this head,
vk_present_rect.his included byvk_framegen.cppandvk_replay.cpp.vk_menu.cppandhost/hos_main.calso receive the source fingerprint throughLONGSHOT_VERSION;nx2/version.pyhashes renderer headers, andnx2/build.py, lines 1217 and 1348, supplies that definition to those two files. They are not additional readers of this header.vk_hangdump.cppretains the whole-header fallback. This explains the five-file result without attributing the host rebuild to its header group.Extend the checks to exercise the new path together with the preserved extras. The DLSS and shader-stamp fixtures construct jobs without existing object depfiles, so their passes cover the fallback path. Seeding a depfile in those fixtures would also test that an NGX or ImGui edit still changes the key after narrowing. A case stopped after the pending stamp and restarted before re-keying would pin the interrupted-build guarantee. These are coverage improvements, not observed failures.
Overlaps
cache_put, which this PR's re-keying calls: #24. The source-hash record changeskey_ofand its saved state: #35. Both merge pairwise with this head without textual conflicts; their combined behavior was not tested.Not checked
No current-head Windows execution, real renderer build, interrupted renderer build, binary-output comparison or repeat timing here. The original GitHub API now returns 404, so its earlier discussion was read from retained copies, not a fresh complete thread. The forge status reports a passed description check and a skipped website check, not passed source checks: https://git.magicbean.dev/MagicBeanDev/Longshot/actions/runs/1181. A current-main rebase with the focused checks, a clean and incremental build comparison, and the repeated timing record would settle the remaining readiness and performance questions.
View command line instructions
Manual merge helper
Use this merge commit message when completing the merge manually.
Checkout
From your project repository, check out a new branch and test the changes.