perf(vk): the next frame's CPU work overlaps the GPU's end of the current frame (presenter thread, fixed and on by default on Linux) #2622
Open
hckrman101 wants to merge 11 commits from
hckrman101/perf/present-overlap into main
pull from: hckrman101/perf/present-overlap
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:fix/play-cache-files
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!2622
Loading…
Reference in a new issue
No description provided.
Delete branch "hckrman101/perf/present-overlap"
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?
Parked. Not for testing or merging. The gain turned out small and step 3 is not safe: see "Why it is parked". The branch stays at
e59c8c80; the work continues as a different design (the game running a frame ahead with readback results delivered in order), not as fixes to this PR.The game's frame is strictly serial today: the next frame's CPU work does not start until the GPU has finished the current one. This PR lets the two overlap.
Cause
In Hyrule Field with #2602 (RTX 3090, Ryzen 9 7900X, Linux, 2560x1440, uncapped) a frame is 12.6 ms:
The 4 ms is
vkQueuePresentKHR. NVIDIA's driver on Wayland waits there on the CPU until the frame's copy into the window is done on the GPU, which is after all of the frame's GPU work. The game's main thread waits for that present to return before it builds the next frame, so the GPU's tail of one frame and the CPU's start of the next never overlap. It is not V-Sync: the swapchain is mailbox, and the wait does not follow the refresh.What is in it
RX_VK_PRESENT_THREAD=1.RX_VK_CAPTURE_HOLD=0turns it off. It has no platform check, so it is on everywhere, and it has only run on Linux with NVIDIA.Measured
Hyrule Field, RTX 3090, Linux/Wayland, 2560x1440, uncapped, Reflex and frame generation off, 15 s runs against #2602's build:
e59c8c80(paired run)e59c8c80(a second run)e59c8c80withRX_VK_PRESENT_THREAD=0Why it is parked
RX_VK_PRESENT_THREAD=0on the same build the median is 11.93 ms against 11.64 ms for #2602, so there is no gain without a present that blocks on the game's thread (NVIDIA's driver on Wayland here). A tester on an RTX 4090 whose present does not block saw 93-94 fps on and off.54d1ab82)e59c8c80)RX_VK_CAPTURE_HOLD=0: presenter + part wait)54d1ab82), unpushed.Never tested
Off switches
RX_VK_PRESENT_THREAD=0: the present back on the game's thread. The log says which:vk: presenter thread: ....RX_VK_CAPTURE_HOLD=0: the capture waits for its late page as before.Tests
tests/test_present_thread.py: the held submits are sent and the presenter idle before the acquire and before the fence's reset; the drain has no limit; no present is skipped; only the game's own present is handed over, never while Reflex numbers the presents; onevkDeviceWaitIdle, under every queue's lock; the lost-surface reset; the default per platform.tests/presenter_queue_test.cpp: the queue choice on NVIDIA's and RADV's layouts.tests/test_fps_stats.pyandtests/test_present_semaphores.pyupdated for the changed functions.First measurements of
8dceed53(the presenter thread on its own, beforea9a2e746/5ae50a53). RTX 3090, Ryzen 9 7900X, Linux/Wayland, 2560x1440, uncapped, default options (Reflex and frame generation off), Hyrule Field, 25 s each,RX_VK_PRESENT_THREADoff vs on in the same build.Next on this PR:
dev.present_mureleased (behind a switch, measured). With Reflex/frame generation on, this step does nothing yet; that case should gain the most.Testers: Linux with Reflex and frame generation off for now. Compare against main in the same spot, or toggle
RX_VK_PRESENT_THREAD=0. The most useful report is about stutter (frames over 20-33 ms), and also resizing, alt-tab, the fullscreen toggle and quitting.Testers: please hold off on this build for now. As pushed (
5ae50a53), the presenter thread adds hitches with default options. It's not only swap noise.Hyrule Field, 5 runs with it on against 3 off, same session:
Cause: once the game runs a frame ahead, a capture finds pages still to be written back (the exposure meter's) and waits for that submission's value. That value is reached only with the submission's last part. When the submission is stopped at an event, the presentation thread has to finish it, while the main thread is the one waiting, so the wait runs tens of ms, up to the 250 ms limit. Before this PR the present had already waited for the whole frame, so this never happened (0 of 600 takes). The same wait already happens on main with frame generation on, where the present is already off the game's thread, so it is probably part of the stutter frame-generation users see today.
Fix, committed locally, not yet run in the game: a capture's late pages wait for the part that writes them back (#2602's page-to-part table), not the submission's last part. Only pages written in place keep the old wait.
RX_VK_CAPTURE_PART=0restores the old wait. Timing pairs are running; I'll push it once they're clean.A second machine for the hitch question on
5ae50a53, before your fix, with memory to spare. Linux 7.2.15-1-omarchy, Hyprland 0.56.2 on Wayland, RTX 4090 (NVIDIA 615.78.08), i9-13900K, one 1920x1080 monitor at 60 Hz (scale 1.25). A real window at the monitor's size, not fullscreen; the log saysswapchain 1920x1080, 4 images, mailbox (V-Sync Auto), so mailbox as you assume. Hyrule Field from a save, with scripted camera turns,HOS_FPS_MAX=240, the game's own upscaler, Reflex and frame generation off.One hold with nothing else on the GPU and no builds running, the same build four times in turn. Memory available 66.9 GB before and 75.7 GB after; pages swapped in 5632 and out 0 during the hold.
Runs of 105 s; the numbers are from the window that starts 15 s after the save has loaded (55 s, judged in two halves: a half counts when everything else on the PC used under 3 cores on average over it and no 5 s sample of that was over 6). Frame times are the swapchain's present intervals (
RX_VK_FRAME_LOG, written when each present call returned, on whichever thread made it); the 1 % low is 1000 over the mean of the slowest 1 % of them.vk: slowlines in the window (longest)RX_VK_PRESENT_THREAD=0)RX_VK_PRESENT_THREAD=0)Thread on against off, from the parts that count: frames over 33 ms 0 and 0 against 0 and 0; over 20 ms 1 and 1 against 2 and 2; longest frame 32.7 and 20.5 ms against 20.7 and 22.6 ms; median 10.48 and 10.37 ms against 10.37 and 10.45 ms; fps 93.4 and 94.3 against 94.0 and 93.5; main-thread memory-sync
slow submitlines 0 and 0 against 0 and 0.So the hitches do not show on this PC: no frame over 33 ms in any of the four windows (110 s and about 10,300 frames a side). The main thread's memory-sync
slow submitlines are in every run, on and off alike, but only at the save's load (two or three a run, the longest 122 to 148 ms, within about 240 presents of the load) and none in play. The capture's late-page waits do not grow with the thread on either. The gain does not show here either: the median is the same, and only the tail is a little tighter with the thread on (p99 14.7 and 14.9 ms against 15.5 and 15.6 ms, about half as many frames over 16.7 ms). My guess at why both are missing: the GPU is not the limit in a 1920x1080 window on this card (the presenter's line has the copy's fence at 1.7 ms where yours waits 3.5 to 4.3 ms), so the game is rarely a frame ahead of it. The windows are 55 s each, so this says the hitches are not general; it does not say they cannot happen here.Quitting, as far as a script reaches: each run ends with the window being closed by SIGTERM at its time limit. 4 of 4 runs (2 with the thread on) ended 2.9 to 3.2 s after the signal with
display: window closed, exitingas the log's last line; no driver line in the kernel log (0).No line in any of the logs that names a swapchain error, a lost surface, an out-of-date or suboptimal swapchain, or a wait that gave up.
The build is the exact commit plus one local counting patch (it logs each buffer readback). The
vk: slowlines are the renderer's own (over 15 ms, at most 80 a run), placed in the window by the 120-presents lines around them. I did not use the log's per-frame breakdown.Not tested: resizing, alt-tab and the fullscreen toggle (the test window takes no input and its fullscreen requests are refused), V-Sync On, Reflex or frame generation on, Kokiri Forest and the 60 cap on this head (waiting for the new head), other GPUs, X11.
WIP: perf(vk): the next frame's CPU work overlaps the GPU's end of the current frame (presenter thread, fixed and on by default on Linux)to perf(vk): the next frame's CPU work overlaps the GPU's end of the current frame (presenter thread, fixed and on by default on Linux)Tested in the game on Linux + NVIDIA: the hold works as described and the capture's wait is gone, but the frame rate did not change here, because this machine is GPU-limited in this scene, so the wait moves to the hold's flush.
Hardware: RTX 3060 12 GB, NVIDIA 615.71.09, Manjaro (kernel 6.12), Core Ultra 7 265KF, Wayland. Built on main
51da0464with this PR merged onto it locally (the merge conflicted invk_memory.cppandvk_replay.cpp, mostly additions on both sides; resolved by keeping both, a test build only, not pushed), onlyhookshot_vk.sobuilt (RX_MARCH=x86-64-v3 RX_RELEASE=1) and swapped into the same game folder; main's built the same way as the baseline.Settings: picture 2576x1080 (Aspect ratio: monitor's), Render resolution 100%, frame interpolation 2x, Reflex on, V-Sync off, no cap. Scene: Hyrule Field from the same save, standing, no input; only the 5 s windows at 3,370-3,730 draws counted.
held submits for a capture's late page: 600 holds, 4.75 ms each from the capture to the page's copy; 596 ended in a flush that waited for the GPU, 4.26 ms each.RX_VK_PASS_TIMING=1on main in the same spot measured about 20 ms of GPU time a frame (GPU 84-90 % busy innvidia-smi), so on this card the GPU, not the replay, sets the pace; the replay's 9 ms saved shows up as the flush waiting instead.GPU-bound waitin thevk: N presentsline read 0.0 ms in every run, also on main where the capture waited ~9 ms a frame for the GPU, so that counter does not see this wait.