Skip to content

shim: no vblank-grid gate under the cap-rate FIFO declaration; a late start is released at the next grid instant - #47

Merged
utkarshdalal merged 1 commit into
masterfrom
shim-cap-rate-gate-off
Nov 3, 2026
Merged

utkarshdalal merged 1 commit into
masterfrom
shim-cap-rate-gate-off

Conversation

@utkarshdalal

Copy link
Copy Markdown
Contributor

Follow-up to #46, which merged at c9b7c31 before this commit reached the branch. Master therefore carries the regression @joshuatam's capture found (see #46 (comment)); this PR is that fix, cherry-picked unchanged from the #46 branch (2567925).

Problem

Under a cap equal to the display's rate, #46 declares FIFO and also kept the vblank-grid acquire gate running. Once a frame takes longer than a period from acquire to commit, every start reaches the gate late and the schedule books every other release a whole vblank later: the game settles at 40 fps on a 60 Hz panel (1,2,1,2 vblank intervals). The capture: at 120 Hz declared=mailbox source=env gate=vsync, 60 fps; after a live switch to 60 Hz declared=fifo source=cap-rate gate=vsync, shim commits 400 per 10 s = daemon publishes 400/600 with late=0, gate_late 400/400, skipped +200 per 10 s, acquire-to-commit 24 ms (7-16 ms at 120 Hz), GPU 65 % against 80 % at 120 Hz where the same scene runs 60.

Change

Tests

cargo test -p adrenotools_gamescope --lib: 88 passed. Clippy on the shim, daemon and android crates with -D warnings: clean. New cases: 60 on 59.97 Hz is FIFO/cap-rate/gate off; 60 on 120 and 30 on 60 stay mailbox with the gate every second vblank; a guest with 16 ms of work on a 59.97 Hz grid gets a start every vblank; a late 30-on-60 frame is followed at the next grid instant.

Verified on a Retroid Pocket 6

Dave the Diver in game (diving), cap 60, in-game VSync on, panel switched live from 120 to 60 Hz, shim 2567925 via payload 20261103.4: census declared=fifo source=cap-rate cap=60 gate=off; SurfaceFlinger 374 of 377 intervals a single vblank, median 16.71 ms; app fps 59.9 / 60.1 / 60.0 at GPU 51-53 %; daemon 596-598 publishes per 600 vsyncs, late 0-2.

Delivery

Payload gamescope-gamenative-20261103.4.tzst (sha256 5126c4ee8f042a69e287a3bde16e3be5a79de0cc052aaeadff5554b6a09fe7b9) already carries this shim (sha256 6355ee9e…9035, build string 2026110307122567925); the app side is utkarshdalal/GameNative#2327. The daemon is unchanged.

… start is released at the next grid instant

@joshuatam's capture (payload 20261103.3, shim c9b7c31, a 1280x960 device,
DTD in game, cap 60, env mailbox, in-game VSync on) found a regression. At
120 Hz the session was declared mailbox with the gate and held 60 fps. After
a live switch to 60 Hz it became declared=fifo source=cap-rate gate=vsync and
settled at 40 fps within ~10 s: shim commits 400 per 10 s, equal to the
daemon's 400/600 publishes with late=0; gate_holds 0, gate_late 400/400,
skipped +200 per 10 s; acquire-to-commit 24 ms against 7-16 ms at 120 Hz.
Back at 120 Hz it recovered within a second. The RP6 runs of this cell did
not show it because their scene's acquire-to-commit stayed near 12 ms.

FIFO's back-pressure already holds the guest at the display's rate, and the
gate stacked on it: once a frame took longer than a period from acquire to
commit, every start was late, and the schedule booked releases on the grid
the guest could no longer meet.

- The cap-rate cell (a non-FIFO mode under a cap at the display's rate)
  still declares FIFO, but with the gate off: gamescope's limiter (divisor
  1) paces it, which is master's behaviour in that cell. The census reads
  source=cap-rate gate=off.
- The gate never books the release after a late start more than one grid
  instant after it: under a cap below the display's rate (30 on 60, 60 on
  120) a late frame's successor no longer waits a whole slot. On-time
  guests are held as before. Lateness is measured against the instant the
  acquire was released for, so the first grid booking after a clock booking
  is not mistaken for a late one.
- The slot count follows the guest's own request whenever the swapchain is
  declared FIFO at creation for another reason than its own mode (no stamp
  lead, cap-rate, a guest's FIFO request with no cap), as on master.

- icd_shim/src/proxy.rs: gamescope_route_pacing turns the gate off for every
  FIFO declaration; the slot count from choose_live_mode at creation;
  comments; tests: 60 on 59.97 Hz is FIFO, cap-rate, gate off; 60 on 120
  and 30 on 60 stay mailbox with the gate every second vblank.
- icd_shim/src/vsync_gate.rs: the late-start release rule; tests: a late
  frame on a 30-on-60 cap is followed at the next grid instant while an
  on-time one is held two vblanks; a guest with 16 ms of work (late every
  seventh frame) on a 59.97 Hz grid gets a start every vblank.
- docs/10, docs/15: the rule table and the reason, with the capture's
  counters.

(cherry picked from commit 2567925)
@utkarshdalal
utkarshdalal merged commit bf2757b into master Nov 3, 2026
@utkarshdalal
utkarshdalal deleted the shim-cap-rate-gate-off branch November 3, 2026 07:28
utkarshdalal pushed a commit that referenced this pull request Nov 3, 2026
…ant release after a late start

Reported on app master with payload 20261103.6 (shim 762e559): the FPS
limiter set to 20 under gamescope on a 120 Hz panel left MGS V: The
Phantom Pain at 40-50 fps; at 60 Hz the cap held. Reproduced on the
Retroid Pocket 6 at 120 Hz with DTD at 1920x1080, cap 20, env mailbox:
census `declared=mailbox requested=immediate source=env cap=20
gate=vsync`, `gate_holds` frozen at 188 for the rest of the session while
`gate_late` grew by one per frame, frame spacing 17.2-20 ms (DTD's own
~53 fps). The cap reached the shim; the gate stopped holding.

Cause: #47 (1cc98e1) added, next to the fix for
the cap-rate FIFO cell (gate off under that declaration, kept here), a
rule that the release after a late start is never more than one grid
instant after it. A cap below the display's rate holds the guest for a
whole slot (six vblanks at 20 on 120 Hz); after that rule fired once the
next release was one vblank after the late start, a guest whose frame
outlasts one period plus ON_TIME_NS (0.5 ms) was late for that release
too, and so for every release after: the gate never held again. At 120 Hz
(8.3 ms period) that is every game; at 60 Hz a guest faster than ~58 fps
recovers, which is why the 60 Hz panel hid it.

- icd_shim/src/vsync_gate.rs: Schedule::book: a start late by less than a
  period is booked to its release point as before; a start late by a
  period or more but still inside its slot (before `ideal + interval`) is
  booked to the grid instant it is in and keeps the slot's ideal timeline,
  so the release after it is where the cap puts it, at most the rest of
  the slot away; a start past the whole slot restarts the timeline from
  the instant it is in, so a stall carries no debt. The cap-rate cell is
  unchanged (slot == period). Tests: the late-frame-on-a-divisor-cap test
  now expects the slot's release (two vblanks at 30 on 60) and covers late
  by a whole vblank and past the slot; a new test runs a guest with 18 ms
  frames, 1 ms late wake-ups and one 80 ms hitch under cap 20 on 120 Hz
  (and 22 ms frames under 20 on 60 Hz) and requires the 6- (3-) vblank
  cadence everywhere but the three releases after the hitch, which catch
  the slot's timeline up, and never more releases than the cap over the
  run. The existing 16 ms guest on a 59.97 Hz grid under cap 60 still
  gets every vblank.
- docs/15: the gate paragraph states the rule and why the next-grid-instant
  release is gone.
utkarshdalal added a commit to utkarshdalal/GameNative that referenced this pull request Nov 3, 2026
…panel rate again (#2336)

Ships GameNative/gamenative-drm#49: the shim's vsync-grid gate no longer
releases the frame after a late start at the next grid instant, a rule
from GameNative/gamenative-drm#47 that made every cap below the panel's
rate stop holding once a frame outlasted one vblank (MGS V at cap 20 on a
120 Hz panel ran at 40-50 fps; DTD at 53). Only GamescopePayload.VERSION
changes.

Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant