Repository navigation
android: disable the A52 vblank time-grid fps limiter patch - #26
Merged
Merged
Conversation
The A47 patch still said `-r` was the session's vblank grid and that a `-r 1200` grid matched no mode; the A52 patch, disabled, diagnosed the cap against the same grid and its 0.83 ms lateness. Neither is true of a session any more: the launcher passes no `-r`, so the mode's rate is the only rate in the session and the grid derives from it. - `android/patches/gamescope/0034-A47-drm-mode-lookup-by-size.patch`: the header and the comment the hunk inserts state the mechanism that still matters — a preferred refresh the connector does not carry would find no mode at all, so the lookup re-finds by size and the launch line stays free to pass one. The hunk header is recounted after the comment shrank by a line; the patch still reverse-applies against the built tree, so the applied copy and the patch agree. - `android/patches/disabled/0050-A52-fps-limit-on-the-vblank-time-grid.patch`: the diagnosis says the finer the session grid the sooner a wakeup is late, and that the launcher no longer passes one, instead of naming the grid and its period. No diff body changes in either file.
utkarshdalal
pushed a commit
to utkarshdalal/GameNative
that referenced
this pull request
Nov 1, 2026
… cap and the multiplier (#2313) * gamescope: advertise a cap that is below the panel, and the panel otherwise The daemon reports the panel's own rate as the session's emulated mode. That is right for everything at or above the panel — the cap is enforced at acquire, so the compositor's grid should stay at the rate the display can latch. Below the panel it is not: a 30 cap on a 120 Hz panel had the connector advertise 120, the compositor paint at 120 and the divisor base be 120, so the session ran a display three times the rate it was asked to produce. `applyPanelRefreshForCap` decides one number per apply: the cap when it is below the panel, the panel otherwise, and it is called from the gamescope arm of the limiter whenever a target changes. `refreshFpsLimit` passes the current cap too, so a display change (and session start, which already sets the rate before `nativeStart`) re-evaluates rather than re-reporting the panel blindly. X11 sessions are untouched: they pace through the renderer and the present extension, not through the emulated display. The jniLibs daemon is rebuilt from gamenative-drm `5ecab43`, which is the code this behaviour needs: the mode is the panel's rate, the frame ring counts what was produced, and a scan-out subscription now outlives the allocator role in both directions. * gamescope: the mode follows the cap and the multiplier, live The connector advertises the rate the session is asked to produce — the cap times the frame-generation multiplier, clamped to the panel — and gamescope has to take it mid-session: at a 40 cap, x2 must run an 80 Hz mode and show 80 fps, x3 120 and 120, x4 120 and 160, on the QuickMenu's change alone. `applyPanelRefreshForCap` derives that rate from the multiplier as well as the cap, reads the multiplier from the container rather than `isLsfgAvailable` (that is a composition-time value, and the QuickMenu's switch changes the container without recomposing this screen), reports it, and asks gamescope to re-read the connector when it changes — only then, because a nudge forces a modeset. `applyLsfgMultiplier` reports the rate before the LSFG settings, so the config the shim opens the generator from carries the rate in force instead of the one from before the change. - `display/GamescopeConVar.kt`: `BACKEND_SET_DIRTY`, gamescope's own re-poll. - `display/GamescopeCommandQueue.kt`: `nudgeDisplayMode()`, a one-shot action. - `ui/screen/xserver/XServerScreen.kt`: the produced rate in `applyPanelRefreshForCap`, the change-only nudge, the multiplier documented, and the rate applied before the LSFG settings. - `display/GamescopePayload.kt`: payload 20261031.6. - `jniLibs/arm64-v8a/libgamenative_drm.so`: rebuilt from the daemon commit that serves the rate live (`69f03ef6`). Measured on the Retroid Pocket Nova (120 Hz panel), Hades II, one session, cap 40, changing only the multiplier: 80 Hz mode / 80.2 fps at x2, 120 / 120.0 at x3, 120 / 160.0 at x4, back to 80 / 80.0 at x2. * gamescope: the mode is the display's, not the session's `applyPanelRefresh` reports the display's own rate and nothing else. The cap and the frame-generation multiplier never enter it: the mode is what the compositor runs at, so it changes when the display changes and at no other time. At a 40 cap the mode stays at the panel's rate while x2 produces 80 frames a second, x3 120 and x4 160, which is what the frame counter reports. The mode nudge stays, and is now asked for only by the display listener: a display change reaches the running session through it (gamescope re-reads the connector and re-selects the mode), while a QuickMenu change moves nothing. The first report does not nudge — it is the rate the session starts on anyway. - `ui/screen/xserver/XServerScreen.kt`: `applyPanelRefreshForCap` becomes `applyPanelRefresh`, reports the display's rate, and nudges only on a change after the first report. - `display/GamescopePayload.kt`: payload 20261031.7, whose shim no longer halves the source rate on a slow panel. - `jniLibs/arm64-v8a/libgamenative_drm.so`: rebuilt (`aa33a6a4`). Measured on the Retroid Pocket Nova, Hades II, cap 40, one session per display: 120 Hz display 80.0 / 120.0 / 160.0 at x2 / x3 / x4 with a single `selecting mode 1280x960@120Hz` and no nudges; 60 Hz display 160.0 at x4, 120.0 at x3, 79.8 at x2 with 40.3 acquires a second; forcing the display to 60 Hz mid-session re-selected the mode once. * gamescope: payload 20261101.1, the shim leaves the source rate at the cap The payload carries the gamescope build with the A52 vblank time-grid limiter patch disabled (GameNative/gamescope#26) and the shim built from the daemon change in GameNative/gamenative-drm#41, where `source_cap` is the user's cap as asked: the display no longer halves it on a slow panel, and generation runs at `multiplier - 1` frames per source frame whatever the mode. `GamescopePayload.VERSION` must name the archive that ships with the build, so it moves with it. * update drm library * Gamescope payload 20261101.4: Xwayland shared-memory attach via the sysvshm server, on the fps-limiter stack Payload = shipped 20261031.3 + bin/gamescope (gamescope#26, A52 disabled) + bin/Xwayland (GameNative/xserver#1 + #2) + lib/libadrenotools_gamescope.so (gamenative-drm#41 shim, e0f9c23). Daemon in jniLibs is JT's from #2313. * Gamescope payload 20261101.5: merged fps-limiter daemon and shim, Xwayland shared-memory attach Payload = shipped 20261031.3 + bin/gamescope and bin/Xwayland from the gamescope gamenative CI (A52 disabled, #26; Xwayland from GameNative/xserver gamenative with #1 + #2) + lib/libadrenotools_gamescope.so from gamenative-drm master (#41 + #42). Daemon in jniLibs from the same master. * Gamescope payload 20261101.6: Xwayland linked against the payload's libxcvt.so 20261101.5's Xwayland was built by the gamenative workflow without the unversioned-libxcvt fix, so it needed libxcvt.so.0 and never loaded ("Xwayland server 0 did not signal ready"). 20261101.6 is the same payload with the Xwayland built from the identical xserver source (bd9c2cdd4 = merged gamenative) on the fixed workflow. --------- Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>
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.
Moves
0050-A52-fps-limit-on-the-vblank-time-grid.patchout ofandroid/patches/gamescope/intoandroid/patches/disabled/, whereandroid/build-gamescope.shdoes not apply it.With the patch gone, the non-VRR frame-rate limit counts compositor wakeups again (
steamcompmgr_should_vblank_window,vblank_idx % nVblankDivisor) instead of releasing on the vblank time grid. That equals every Nth vblank only while no wakeup is ever late:CVBlankTimer::GetNextVBlanksteps to the next vblank after now, so a wakeup landing more than one vblank late skips the vblanks in between andvblank_idxnever counts them. The finer the session grid, the sooner a wakeup is late — a sub-millisecond scheduling delay already cost a whole vblank — so each late wakeup held the next release a vblank longer and the cap read below its target: the 18-21 ms frames with the CPU and the GPU idle that #22 measured at 54 of 60. The launcher no longer passes a fine grid, which is why the patch is disabled rather than merely unapplied.One file, a rename, so re-enabling is the reverse move. The VRR path is untouched either way, and no other patch depends on it.
Paired changes
20261101.1.With A52 out of the payload, the limiter the app drives is the compositor's own divisor-based
one; the app-side cap and frame generation are unchanged by this rename.