WIP: perf(vk): RX_VK_READBACK_LATE, the readbacks a rule names are read without their wait (opt-in) #238
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/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
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!238
Loading…
Reference in a new issue
No description provided.
Delete branch "p0lux/Longshot:perf/readback-late"
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
On a Steam Deck the frame spends several milliseconds waiting on read-backs: a CPU read of GPU results waits for the write-back of the submission that writes them. Most of that wait is one pair of 0x3980-byte ranges that the game reads right after submitting the work that fills them. Reading those without the wait is the largest single gain I have measured on the Deck. The finding is camilobt's, from Hookshot's issue 1000.
What changed
invalidate()ingpu/vk/vk_replay.cppasksreadback_late_wanted(size, newest)before its wait. A range a rule names is read as guest memory holds it (the frame before's values); every other range waits as before.gpu/vk/readback_late.h,parseandwanted.newest: the range's writer is the newest submission handed out. A hex size: the ranges of exactly that many bytes. Either is enough.rb_range_noteandrb_ranges_loginvk_replay.cpp, the read-backs by range every 3,000 calls, behind their own switch. That log is how the two ranges were found.tests/test_readback_late.py(new) runs the rules and checks the wiring;tests/test_readback_part.pyfollowsinvalidate()'s text and runs the wait with and without a rule.docs/development.md, "Switches for testing and debugging".Flags and settings
RX_VK_READBACK_LATE=<rule>[,<rule>...]newest, or a hex size such as3980newest,3980on a Steam Deck after more testingRX_VK_LOG_READBACK_RANGES=1Measurements
Not measured as this PR yet, which is why it is WIP: this commit is the tidy version of a local patch, and the comparison the template asks for (one binary, the switch unset against set, interleaved) is still to run on the Deck. It will also separate
newest,3980andnewest,3980.For orientation only, the local patch with
RX_VK_READBACK_LATE=newest,3980against main, two binaries, runs not interleaved between the two sides:main:
000f19fb(2026-11-13)this PR: perf/readback-late @
c23f844eis not measured; the numbers are main000f19fb+ the local patchHardware: Steam Deck OLED (AMD Custom APU 0932, RADV VANGOGH, Mesa 26.2.0-devel), 16 GB, SteamOS 3.9.5, kernel 7.2.7-valve2
Scene: a save standing still on a heavy view (about 2,500 draws a frame), and one standing still in Hyrule Field (about 1,600 draws); headless, handheld profile, Minimum preset, the game's own upscaler
Method: each run starts the game, loads the save by script, waits 3 log windows, then measures exactly 720 presents: fps over that span and the log's replay time
How to reproduce: to come with the real measurement
000f19fb000f19fb+ the local patch,newest,3980On Hookshot the same patch, one binary, the switch off against on: Hyrule Field 22.49 to 27.29 fps (one run each), a heavy view 23.6 to 24.7 (two runs each).
Testing
Tested:
python -I -B tests/run_all.pyon macOS with the Vulkan headers given: 472 passed, 19 failed, 36 skipped; the 19 fail the same way on main on that machine (18 for reasons of the machine: no/proc, noposix_fallocate, the Linux launch scripts; andtest_texture_staging_publication, which fails on main since the log levels came in).test_readback_late.pyfails on main (the header is not there) and passes here;test_readback_part.py,test_readback_settle.pyandtest_readback_wait_bounded.pypass. In the game, the local patch only:newest,3980played on the Deck with the picture for two days on Hookshot and about an hour on Longshot today, with no flicker noticed by the player.Not tested: this commit in the game (not built yet: the tidy-up moved the rule to a header and put the log behind a switch); the flash detector with the switch on; any hardware but a Steam Deck OLED; Windows.
Needs other testers:
RX_VK_LOG_READBACK_RANGES=1, which ranges wait the most and for how long, then whetherRX_VK_READBACK_LATE=newestflickers or gains anything there, expecting the same two ranges and no flicker (request to open)Screenshots
Nothing on screen.
Risk and rollback
With the switch unset nothing changes but one test of a constant a read-back. With it set, a range read late holds the frame before's values: right for a range the game reads before its work can be done, wrong for one whose late read matters. The exposure meter is such a one (the white flashes the wait is there for), which is why this is a rule by range and off by default, not
RX_VK_NO_READBACK_WAIT. Unset the switch to turn it off.Reviewer notes
@dukenheimer claimed frame-ahead with in-order delivery of the read-back pages measured safe a frame late. This is the narrow, already measured case of that on a Deck (two ranges), plus the log that finds such ranges. If yours covers it, say so and I close this or reduce it to the log.
The rules are independent on purpose:
newestalone may be enough, and the measurement will say which ofnewest,3980and both carries the gain.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.docs/tested-configurations.mdfor each measured run. With the measurement.mainand passing here.tests/that reads changed source updated in this PR, not weakened.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.