flip_stats.go RawHistoryFolder
1package drm
2
3import "time"
4
5// flipStats are the flip timing counters of one output, reported and
6// reset by the periodic stats entry. They are plain fields owned by the
7// output goroutine. All times are CLOCK_MONOTONIC (kernel flip
8// timestamps and userspace reads), so they tell a late composition from a
9// late read of the event (see accountFlip).
10type flipStats struct {
11 // missedVblanks counts due frame flips whose kernel timestamp is over
12 // 1.5 refresh periods after the previous frame flip (never under VRR,
13 // where the period is not fixed).
14 missedVblanks int
15 // maxInterval is the longest time between two frame flips, of the
16 // frames that were due.
17 maxInterval time.Duration
18 // maxCommitDelay is the longest time a due frame waited between the
19 // moment it was due (wanted, the previous flip landed, or the VRR
20 // flip gap ended) and its commit: late event read, slow composition.
21 maxCommitDelay time.Duration
22 // maxFlipToRead is the longest time between a flip's kernel timestamp
23 // and the output reading its event: the event delivery latency.
24 maxFlipToRead time.Duration
25 // lateFences counts missed vblanks whose fences signalled after the
26 // vblank the frame targeted (never under VRR). Fence times are only
27 // read while flip tracing is on.
28 lateFences int
29}
30
31// nominalPeriod is the time between two vblanks at the mode's refresh
32// rate. Under VRR it is the shortest period: the fastest the output
33// flips.
34func (o *Output) nominalPeriod() time.Duration {
35 return time.Duration(int64(time.Second) * 1000 / int64(max(1, o.mode.refreshMilli())))
36}
37
38// refreshPeriod is the time between two vblanks of the current mode (0:
39// not fixed, VRR).
40func (o *Output) refreshPeriod() time.Duration {
41 if o.vrrOn {
42 return 0
43 }
44 return o.nominalPeriod()
45}
46
47// setVRR records the VRR state. The flip interval chain restarts when it
48// changes: the period of the two states differs.
49func (o *Output) setVRR(on bool) {
50 if o.vrrOn != on {
51 o.lastFlipAt = 0
52 }
53 o.vrrOn = on
54}
55
56// accountFlip counts the completion ev of a frame commit made at
57// commitAt for a frame due at wantedAt (0: unknown, the commit time; see
58// pendingFrame.dueAt). All times are CLOCK_MONOTONIC. fenceAt is when the
59// commit's fences signalled (0: none or unknown). It updates lastFlipAt
60// for frame flips and never allocates.
61//
62// An output renders only on change, so a gap after an idle period says
63// nothing about missed vblanks. The interval to the previous flip counts
64// only for a frame that was due: wanted less than one period after the
65// previous flip, so it was meant for the very next vblank, whenever it was
66// committed. A frame that waited for the previous flip's event or for its
67// composition is due as soon as it is wanted. Then:
68//
69// - a flip over 1.5 periods after the previous one missed a vblank
70// (never counted under VRR, where the period is not fixed);
71// - if it missed one and its fences signalled after the vblank it
72// targeted (the previous flip plus one period), the frame was late: a
73// late fence. A fence before that vblank is not;
74// - the commit delay is the time from when the frame was due (wanted,
75// and not before the previous flip) to its commit. A missed vblank
76// with a long commit delay and no late fence points at the commit
77// path: late event read, slow composition on the CPU.
78func (o *Output) accountFlip(ev flipEvent, frame bool, wantedAt, commitAt, fenceAt time.Duration) {
79 if !frame {
80 return
81 }
82 if wantedAt == 0 {
83 wantedAt = commitAt
84 }
85 if prev := o.lastFlipAt; prev != 0 && wantedAt-prev < o.nominalPeriod() {
86 s := &o.flipStats
87 interval := ev.when - prev
88 s.maxInterval = max(s.maxInterval, interval)
89 s.maxCommitDelay = max(s.maxCommitDelay, commitAt-max(wantedAt, prev))
90 if period := o.refreshPeriod(); period != 0 && 2*interval > 3*period {
91 s.missedVblanks++
92 if fenceAt != 0 && fenceAt > prev+period {
93 s.lateFences++
94 }
95 }
96 }
97 o.lastFlipAt = ev.when
98}
99
100// accountRead tracks the delivery latency of the event ev handled at now.
101func (o *Output) accountRead(ev flipEvent, now time.Duration) {
102 o.flipStats.maxFlipToRead = max(o.flipStats.maxFlipToRead, now-ev.when)
103}
104
105// takeFlipStats returns the counters and starts over.
106func (o *Output) takeFlipStats() flipStats {
107 s := o.flipStats
108 o.flipStats = flipStats{}
109 return s
110}