vrr_flip_gap.go RawHistoryFolder

1package drm
2
3import "time"
4
5// VRR flip gap: a workaround for game frames that hit the panel's slowest
6// refresh while the game renders fast.
7//
8// Symptom: fullscreen game, direct scanout, VRR on, 120-165 fps. Now and
9// then, runs of 5 to 30 consecutive flips each take ~20.8 ms: the panel's
10// maximum VRR frame time (1/48 Hz on a 48-165 Hz panel). The game's FPS
11// counter stays steady, but the screen visibly drops to 48 Hz for a
12// fraction of a second.
13//
14// Root cause, measured with --debug=drm-flip on amdgpu (DP, 48-165 Hz):
15//   - the client's acquire fence had already signalled at commit, and
16//     the atomic ioctl returned at once: neither the game nor our commit
17//     path is late;
18//   - every slow flip was committed within ~0.3 ms after the previous
19//     flip event, i.e. right at the start of the next refresh cycle;
20//   - the flip event then comes ~20.8 ms after the commit: the flip
21//     misses the early-refresh window and the display only refreshes when
22//     the VRR timeout forces it (vtotal at the minimum rate);
23//   - since Output.Run commits the next frame as soon as that late event
24//     arrives, the next commit lands at the same point of the cycle, and
25//     the output stays locked at the minimum rate until the game's timing
26//     drifts.
27//
28// Why the kernel misses these flips is not established (amdgpu DC VRR
29// flip programming around vblank is the likely place). What is measured:
30// committing the next game frame a short time after the flip event keeps
31// it out of that window. With a 1 ms gap a 3-minute run had 0 slow flips
32// in gameplay, against bursts of up to 30 without it and ~10 isolated
33// slow flips per minute with a 2 ms delay applied only after a slow flip.
34// The cost is at most the gap of added latency per frame, well under one
35// 165 Hz refresh (6 ms), and it applies only while VRR runs for a game.
36//
37// render.vrr-flip-gap sets the gap (default 1 ms, 0 turns the workaround
38// off); drop it once the driver no longer needs it.
39
40// startFlipGap is called on each completed commit f: after a game frame's
41// flip under VRR, the next frame waits vrrFlipGap before it commits. A
42// state commit (cursor, VRR toggle) completing inside the gap keeps it:
43// the next frame still follows the last frame flip.
44func (o *Output) startFlipGap(f pendingFrame) {
45	if !f.frame {
46		return
47	}
48	o.flipGapUntil = time.Time{}
49	if o.vrrFlipGap > 0 && o.vrrOn && o.vrrGame {
50		o.flipGapUntil = time.Now().Add(o.vrrFlipGap)
51	}
52}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 25

Tags 10

↑↓ moveenter openesc close