1package drm
2
3import (
4 "os"
5 "runtime"
6 "time"
7 "unsafe"
8
9 "golang.org/x/sys/unix"
10)
11
12// Flip tracing (--debug=drm-flip) logs every commit completion with the
13// times needed to tell a late client fence from a late flip: all times
14// are CLOCK_MONOTONIC, the clock of flip events and fence timestamps.
15
16// SYNC_IOC_FILE_INFO = _IOWR('>', 4, struct sync_file_info)
17const ioctlSyncFileInfo = 0xc0383e04
18
19// syncFileInfo is struct sync_file_info.
20type syncFileInfo struct {
21 name [32]byte
22 status int32
23 flags uint32
24 numFences uint32
25 pad uint32
26 fences uint64
27}
28
29// syncFenceInfo is struct sync_fence_info.
30type syncFenceInfo struct {
31 objName, driverName [32]byte
32 status int32
33 flags uint32
34 timestampNs uint64
35}
36
37// fenceSignalledAt is when every fence of the sync file f signalled
38// (0: not signalled yet or unknown).
39func fenceSignalledAt(f *os.File) time.Duration {
40 info := syncFileInfo{}
41 if ioctl(int(f.Fd()), ioctlSyncFileInfo, unsafe.Pointer(&info)) != nil || info.numFences == 0 {
42 return 0
43 }
44 fences := make([]syncFenceInfo, info.numFences)
45 info.fences = uint64(uintptr(unsafe.Pointer(&fences[0])))
46 err := ioctl(int(f.Fd()), ioctlSyncFileInfo, unsafe.Pointer(&info))
47 runtime.KeepAlive(fences)
48 if err != nil {
49 return 0
50 }
51 var at uint64
52 for _, fi := range fences {
53 if fi.status != 1 {
54 return 0
55 }
56 at = max(at, fi.timestampNs)
57 }
58 return time.Duration(at)
59}
60
61// fencesSignalledAt is the latest signal time of fs (0: none, or one
62// unsignalled).
63func fencesSignalledAt(fs []*os.File) time.Duration {
64 var at time.Duration
65 for _, f := range fs {
66 t := fenceSignalledAt(f)
67 if t == 0 {
68 return 0
69 }
70 at = max(at, t)
71 }
72 return at
73}
74
75func monotonic() time.Duration {
76 var ts unix.Timespec
77 _ = unix.ClockGettime(unix.CLOCK_MONOTONIC, &ts)
78 return time.Duration(ts.Nano())
79}
80
81// traceFlip logs the completion ev of the commit f made at commitAt and
82// handled at now. prev is the previous frame flip's timestamp (0: none).
83// fenceAt is when its fences signalled (0: none or unknown).
84func (o *Output) traceFlip(ev flipEvent, f pendingFrame, commitAt, now, prev, fenceAt time.Duration) {
85 e := o.log.Debug().Str("connector", o.conn.name).Bool("frame", f.frame).Bool("async", f.async).Bool("vrr", o.vrrOn).
86 Bool("direct", f.zeroCopy != 0).Uint32("seq", ev.seq).
87 Float64("commit_to_flip_ms", ms(ev.when-commitAt)).Float64("flip_to_read_ms", ms(now-ev.when)).
88 Int("fences", len(f.fences)).Bool("fence_ready_at_commit", f.fenceReady)
89 if fenceAt != 0 {
90 e = e.Float64("commit_to_fence_ms", ms(fenceAt-commitAt)).Float64("fence_to_flip_ms", ms(ev.when-fenceAt))
91 }
92 if f.frame && prev != 0 {
93 e = e.Float64("flip_interval_ms", ms(ev.when-prev))
94 }
95 e.Msg("flip")
96}
97
98func ms(d time.Duration) float64 { return float64(d) / float64(time.Millisecond) }