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 // stateCommits counts state-only commits (cursor, VRR, content type);
30 // stateCarried counts deferred state changes a frame carried instead.
31 // Many state commits while a video plays point at a lost vblank each.
32 stateCommits, stateCarried int
33}
34
35// nominalPeriod is the time between two vblanks at the mode's refresh
36// rate. Under VRR it is the shortest period: the fastest the output
37// flips.
38func (o *Output) nominalPeriod() time.Duration {
39 return time.Duration(int64(time.Second) * 1000 / int64(max(1, o.mode.refreshMilli())))
40}
41
42// refreshPeriod is the time between two vblanks of the current mode (0:
43// not fixed, VRR).
44func (o *Output) refreshPeriod() time.Duration {
45 if o.vrrOn {
46 return 0
47 }
48 return o.nominalPeriod()
49}
50
51// setVRR records the VRR state. The flip interval chain restarts when it
52// changes: the period of the two states differs.
53func (o *Output) setVRR(on bool) {
54 if o.vrrOn != on {
55 o.lastFlipAt = 0
56 }
57 o.vrrOn = on
58}
59
60// accountFlip counts the completion ev of a frame commit made at
61// commitAt for a frame due at wantedAt (0: unknown, the commit time; see
62// pendingFrame.dueAt). All times are CLOCK_MONOTONIC. fenceAt is when the
63// commit's fences signalled (0: none or unknown). It updates lastFlipAt
64// for frame flips and never allocates.
65//
66// An output renders only on change, so a gap after an idle period says
67// nothing about missed vblanks. The interval to the previous flip counts
68// only for a frame that was due: wanted less than one period after the
69// previous flip, so it was meant for the very next vblank, whenever it was
70// committed. A frame that waited for the previous flip's event or for its
71// composition is due as soon as it is wanted. Then:
72//
73// - a flip over 1.5 periods after the previous one missed a vblank
74// (never counted under VRR, where the period is not fixed);
75// - if it missed one and its fences signalled after the vblank it
76// targeted (the previous flip plus one period), the frame was late: a
77// late fence. A fence before that vblank is not;
78// - the commit delay is the time from when the frame was due (wanted,
79// and not before the previous flip) to its commit. A missed vblank
80// with a long commit delay and no late fence points at the commit
81// path: late event read, slow composition on the CPU.
82func (o *Output) accountFlip(ev flipEvent, frame bool, wantedAt, commitAt, fenceAt time.Duration) {
83 if !frame {
84 return
85 }
86 if wantedAt == 0 {
87 wantedAt = commitAt
88 }
89 if prev := o.lastFlipAt; prev != 0 && wantedAt-prev < o.nominalPeriod() {
90 s := &o.flipStats
91 interval := ev.when - prev
92 s.maxInterval = max(s.maxInterval, interval)
93 s.maxCommitDelay = max(s.maxCommitDelay, commitAt-max(wantedAt, prev))
94 if period := o.refreshPeriod(); period != 0 && 2*interval > 3*period {
95 s.missedVblanks++
96 if fenceAt != 0 && fenceAt > prev+period {
97 s.lateFences++
98 }
99 }
100 }
101 o.lastFlipAt = ev.when
102}
103
104// accountRead tracks the delivery latency of the event ev handled at now.
105func (o *Output) accountRead(ev flipEvent, now time.Duration) {
106 o.flipStats.maxFlipToRead = max(o.flipStats.maxFlipToRead, now-ev.when)
107}
108
109// takeFlipStats returns the counters and starts over.
110func (o *Output) takeFlipStats() flipStats {
111 s := o.flipStats
112 o.flipStats = flipStats{}
113 return s
114}