WIP: fix(vk): report GPU resource waits beyond recording-slot reuse #60
No reviewers
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
needs measurements
question
testing wanted
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!60
Loading…
Reference in a new issue
No description provided.
Delete branch "mooki/Longshot:fix/vk-gpu-wait-accounting"
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
The old “GPU-bound wait” log could read zero while the renderer waited for other GPU resources. It counted recording-slot reuse only, so it could hide useful evidence in a performance report. This fixes the reporting, not the game's scheduling or frame rate.
Draft for one open question: in both ordinary pairs below the branch's run was slower than main's (37.9 against 44.0 fps and 42.3 against 49.5 fps at a 60 cap; the two overlay pictures show the same direction). The machine was under shared load and all four are far below the cap, so these runs cannot tell load from a cost of the added timing. A quiet paired measurement of that cost is owed before this is ready; it was never made for the original pull request either.
What changed
log_frame_stats()ingpu/vk/vk_replay.cppkeeps the old number, explicitly labelled “recording slots only”. Add host time in GPU resource waits, split into fences, timelines, idle calls and blocking query reads.gpu/vk/gpu_wait_stats.h, the macros ingpu/vk/vk_work.handreflex_sleep()ingpu/vk/vk_reflex.cpptime the same five native calls after the existing recorder boundary. Nested waits count once; zero-timeout polls and Reflex pacing are excluded. No wait, rendering setting or synchronization rule is removed.GPU waitis a presenting-thread frame partition, including the host's GPU-poll sleeps; the new log sums explicit native-call host time across threads. Different scopes and reporting windows mean the numbers need not match. Do not add either to the other or to containing replay/capture phases.tests/test_gpu_wait_stats.pyandtests/gpu_wait_stats_test.cppexecute the actual wrappers; the recorder and Reflex fixtures keep their existing assertions with the new header dependency.Flags and settings
No runtime flag, menu row, ini key or default is added or changed: the new counter is always collected at the existing native wait calls. The new authored test driver exposes these command-line options; none affects the running game. Existing runtime switches used in How to reproduce are unchanged.
--cxxintests/test_gpu_wait_stats.pyg++directly; the suite supplies its compiler--sourcein that test--outin that test--syntax-onlyin that testMeasurements
Branch state: on main
d725b3631982ed0035ea3ac9cbe1677e9fad260c; heade8a9913748b3e7f4d2338259e07da4bf318f10ca; whole game-free suite on this head:508 passed, 4 failed, 20 skipped(maind725b3631982ed0035ea3ac9cbe1677e9fad260c:510 passed, 1 failed, 20 skipped); the commits are unchanged by rebases apart from the results table.main:
d4a69456c9(2026-11-12)this PR: fix/vk-gpu-wait-accounting @
0ab0541ebaHardware: RTX 4090 24 GB, NVIDIA 615.78.08, Core i9-13900K, 96,233 MiB physical RAM, Arch Linux 7.2.15-1-omarchy
Scene: child Link in Hyrule Field by day, the same copied save and stationary view; game output requested 2048×1152, offscreen target 1920×1080, captures 1280×720; game's upscaler at 100%, frame generation off, cap 60, precompile/prediction off
Method: one warm-up per build, then before/after/before/after. After stable play and 15 seconds settling, each run records 14 seconds at Breakdown and 14 seconds at Dev, then quits. Take the last complete 120-present log report within each Dev phase, selected by its observed frame bounds, and report the median of the two logged values per build. Ordinary shared runs: this is diagnostic coverage, not a speed, overhead or GPU-utilization measurement; host descheduling contributes to the new counter.
How to reproduce: load the same copied child Hyrule Field save, stand at its spawn facing the castle, and use the scene settings above. For windowless captures set
HOS_HEADLESS=1,HOS_OUTPUT_RES=2048x1152,HOS_DYNRES=0,HOS_FPS_MAX=60,RX_VK_DLSS=0,RX_VK_FRAMEGEN=0,RX_VK_PRECOMPILE=0,RX_VK_NO_PREDICT=1,RX_VK_SHOT_MENU=1andRX_VK_SHOT_EVERY=120. Require 15 continuously stable seconds, set FPS counter to Breakdown for 14 seconds and then Dev for 14 seconds, and take the last whole 120-present report within Dev fromlongshot_log.txt; repeat main/this PR/main/this PR after a warm-up each. These reproduce counter coverage, not the still-owed quiet overhead comparison. Game-free:python -I -B tests/test_gpu_wait_stats.py; use--source ../mainfrom this PR for main's actual macros, with../mainthe named main checkout.The previously published
12e0d807added only evidence rows after measured runtime0ab0541e; the accounting and fixture bytes were unchanged by the following rebase. The PR48 policy change after measuredd4a69456affected only scripts, docs and tests, with no game code, so the existing game observations still hold. No new game run or quiet overhead measurement is claimed.d4a694560ab0541eAll seven games quit with exit 0; fatal, guest-crash, device-loss and kernel-fault counts are zero, with complete journal coverage. Virtual-core scheduler escape counts remain visible: warm-base=2, warm-branch=0, before-1=0, after-1=0, before-2=2, before-2b=2, after-2=0. These are not process core dumps. The incomplete
before-2attempt lost stable play during settling: the log records a 1,395.8-ms slow present,play -> loadingat frame 1688 andloading -> playat frame 1700 before the requested quit. It has no accepted measurement phase and remains in the table. Only that missing pair was completed withbefore-2bandafter-2, requiring 15 continuously stable seconds before sampling. The completed warm-ups and first pair were not repeated. None of these shared starts is used to claim faster play.On the previously published
12e0d807, the whole suite was rerun on that exact committed head, documentation rows included, with the repository's default compiler. Its sole failure istests/test_updater_asset_digest.py: the same local .NET missing-reference-packNU1100as main. The shared main suite additionally hit two timing-dependenttests/test_appimage_package_retry.pycases under heavy load; an isolated follow-up passed all nine cases, without erasing the original full-suite failure. Thatd4a69456suite remains the shared measured-base record. The new rebased source-check comparison follows below; no configured Forgejo runner replaces the local whole suite.Historical platform evidence: the Hookshot fork run 43282975989 passed both Windows and both Ubuntu source-check parts at
ec274e33a514950348948b8b933233a950ba0aaa. The published GitHub headbfa7e18d52f857b24280abc8c45bf936be006d96carries the same topic patches; the Longshot topic differs only in names and new evidence rows, while retaining newer main. This is not a fresh Longshot Windows run.Retained source-check history
The whole suite was rerun after rebase, including all documentation rows, with the repository's default compiler and a fresh test home; its current pinned result appears only in the branch-state block above. The shared baseline still has the local missing ASP.NET reference pack failure in
tests/test_updater_asset_digest.py(NU1100).That initial r2 run also failed
nx2/test_unsigned_image_types.pyandnx2/test_texture_phi_dominance.pybecause compiler processes were killed with SIGKILL, andtests/test_bake_mimo.pybecause shaderbatch compilation failed without a diagnostic. These three passed the shared main run. A separate failed-only run on the same source, with one test worker, then passed all three. The full-suite failure is retained, not relabelled as a full-suite pass; no additional whole-suite rerun is claimed.Cause not established: the two IR fixtures and the bake compiler calls have no per-compile timeout, and the outer 600-second suite timeout was not the reported failure. Do not infer a timeout, memory kill or product regression merely from the shared load or the successful isolated follow-up. Passing those tests does not answer the quiet instrumentation-overhead question.
The isolated follow-up ran only those three checks, with one test worker:
Its child and descendants exited with code 0. This separate follow-up does not erase the whole-suite failure.
Testing
Tested: on the Arch Linux / RTX 4090 system named above, the whole game-free suite with the default compiler, GCC and Clang accounting fixtures (the main macros fail 9 of 50 checks; this PR passes 50), and the isolated three-test follow-up recorded above. Three changed C++ fixtures pass Windows-target syntax checks; the older Hookshot Windows/Ubuntu run is historical platform evidence, not current Longshot Windows execution. In the game, the copied child Hyrule Field save reached stable play, the Breakdown and Dev levels were inspected, and two accepted interleaved pairs supplied the log observations; the incomplete attempt and warm-ups remain recorded. Tested but not measured: the shared gameplay and overlay appearance; instrumentation overhead, a speed gain or regression and GPU utilization were not isolated. The live cap/descriptor/sampler controls were retained in source, not toggled during these runs.
Not tested: Windows gameplay and native execution of the current Longshot C++ fixtures, AMD/Intel gameplay, live Reflex/frame generation, deliberately failing drivers, long sessions and isolated instrumentation overhead. The available run environment was Linux/NVIDIA; this task did not exercise the other drivers or obtain a quiet paired interval, so no portability or overhead result is inferred. No new hardware test request has been posted. The new count excludes recorder/CPU locks, display acquire/pacing, startup cooperative-matrix probing and blocking hidden inside submit/present. A small count does not prove an idle GPU, and a sum across threads can exceed one frame.
Needs other testers:
Screenshots
Nothing on screen is changed by this log-only counter; the images check integration with the existing overlay.
Both inspected before/after scenes show the same stationary Hyrule Field view. The inherited
GPU waitremains on both levels; Dev adds VRAM, texture, draw/dispatch/pass and pipeline rows. That does not contradict the separate log-only sum. The frame cap is reported as 60 with no restart required; the source of the live cap, descriptor reuse and sampler-cache controls is unchanged from main. No live cap/descriptor/sampler toggle is claimed here.main @
d4a69456(left); this PR @0ab0541e(right): Breakdown.main @
d4a69456(left); this PR @0ab0541e(right): Dev.Historical context: main @
51da0464and this PR @ 9d786cf1; the third image is the separate 75% profile, not a new Longshot comparison.Risk and rollback
Two clock reads and an atomic accumulation around nonpolling waits can add CPU cost. Both shared pairs were slower on the branch, but the load prevents assigning a cause; the requested quiet paired cost measurement is still missing, so this remains WIP. The sum also overlaps across threads and can be misleading if read as frame time or GPU utilization. There is no runtime opt-out switch: reverting this PR removes the timing wrappers and extra log line while restoring the original native calls and recorder boundaries. No synchronization or safety wait should be disabled as a workaround.
Reviewer notes
Start with
gpu_wait_stats::Scope,Excludeandtimed()ingpu/vk/gpu_wait_stats.h, then confirm each of the fivevk_work.hwrappers still drains the recorder before timing its own driver call. Check Reflex pacing exclusion and the fixture's nested/poll/error/concurrency cases. The old slot timer, the FPS presenting-thread partition and the new all-thread sum are different instruments, not additive costs. Current main's Auto tuning and live cap callback are preserved; overlay wording, Windows gameplay and the quiet overhead question remain explicit follow-up/verification boundaries.Historical runtime evidence, Hookshot on 2026-11-12, not repeated as a new timing claim: main
51da0464logged 0.010 ms in old slots; counter9d786cf1logged 0.000 ms in slots and 2.927 ms in native resource waits at 5120×2880. Those ordinary runs had different competing CPU loads. The original PR made no speed-gain claim; the deferred paired overhead measurement remains unperformed.Baseline for that historical context: Hookshot main @
51da0464; the changed build was Hookshot this PR @ 9d786cf1. It is retained provenance, not a replacement for the new Longshot comparison or the still-owed quiet overhead run.Issues
See https://github.com/NotRexed/Hookshot/issues/995#issuecomment-6828404093. Original pull request: https://github.com/NotRexed/Hookshot/issues/2897. No matching GPU-wait-accounting issue was present in the checked Longshot inventory; these links do not refer to forge issue numbers.
Checklist
For everyone
type(scope): description).main. — The tested base is frozen in branch state; a later mechanical refresh is deferred until readiness.python -I -B tests/run_all.pyrun and passing (any failure that also fails onmainis named above). — The retained full run had additional compiler failures; those three pass separately, not in a new whole-suite pass.third_party/untouched (a new folder with aPROVENANCE.mdinstead).If applicable
Assisted-by:/Co-Authored-By:trailers.docs/tested-configurations.mdfor each measured run.mainand passing here.tests/that reads changed source updated in this PR, not weakened.Moved from NotRexed/Hookshot#2897.
Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra [email protected]
Updated the description to the newest template, including Testing and one branch-state block, while preserving the uploaded pictures, earlier measurements and the open quiet-overhead question. The rebase onto
d725b363ise8a99137, with all five patches unchanged; the game observations retain their measured commits. Its whole suite reports 508 passed, 4 failed and 20 skipped, and the three compiler-related failures pass in a separate one-worker follow-up without erasing the original full-suite result or the shared local .NET failure. This stays WIP for the quiet paired cost measurement and pending test requests; the branch is refreshed onto the newest main when it is marked ready.12e0d807bbtoe8a9913748View 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.