Repository navigation
android: A52 — release limited frames on the vblank time grid, not on every Nth wakeup - #21
Closed
utkarshdalal wants to merge 5 commits into
Closed
utkarshdalal wants to merge 5 commits into
utkarshdalal wants to merge 5 commits into
Conversation
This was referenced Oct 30, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this fixes
The non-VRR frame-rate limit counts compositor wakeups: a window gets its frame callback on every
refresh / targetth wakeup (steamcompmgr_should_vblank_window,vblank_idx % nVblankDivisor). That equals every Nth vblank only while no wakeup is ever late.CVBlankTimer::GetNextVBlanksteps to the next vblank after now, so a wakeup that lands more than one vblank late skips the vblanks in between, andvblank_idxnever counts them.A session runs on the 1200 Hz grid (
-r 1200, the nested refresh:GetRefresh()returns it, and so does everyg_nNestedRefresh ? g_nNestedRefresh : g_nOutputRefreshread in the compositor loop), where a wakeup is late by a vblank after 0.83 ms of scheduling delay. Every late wakeup holds the next release a whole vblank longer, so the cap lands below the target in proportion to the wakeups lost, and it sinks further as the SoC throttles and the compositor thread is scheduled later still. That is the shape of the reports: a 60 cap reading 45-57 and a 30 cap reading ~25 on the same device (both about 83 % of the target), flat 18-21 ms frames with the CPU and the GPU idle, gamescope at the X11 rate for the first minutes and lower afterwards.The change
A52keeps the release grid in time instead of in wakeups:g_ulFPSLimitNextReleaseis the previous release plus the target period; a frame is released on the first vblank whose nominal time (VBlankTime::schedule.ulTargetVBlank) reaches it, with half a vblank of tolerance so a moving anchor cannot push a release to the next vblank.should_vblank_windowcall in that iteration agrees.The grid is the nested refresh and the target fps alone. The panel's rate plays no part in the limiter, as before:
g_nOutputRefreshkeeps its uses (mode, Xwayland re-advertise, mangoapp, feedback atom). The VRR branch is untouched (it already schedules by time), andg_SteamCompMgrLimitedAppRefreshCycle(the cycle hinted to clients) is unchanged.Patch
0050-A52-fps-limit-on-the-vblank-time-grid.patchapplies after A51; the full train applies cleanly on this base.Verification
Stacked on #20 (base branch
fix/gamescope-resolution-scanout) so it ships in the same payload.