1package vulkan
2
3import (
4 "time"
5
6 vk "github.com/bnema/purego-vulkan/vulkan"
7 "golang.org/x/sys/unix"
8)
9
10// idleTTL is how long a client buffer cache survives undrawn in wall-clock
11// time. An idle output renders no frame, so importTTL alone never frees
12// the pools, GPU copies and imports of windows that left the screen.
13const idleTTL = 20 * time.Second
14
15// trimMark is the last frame recorded when Trim ran at a time.
16type trimMark struct {
17 frame uint64
18 at time.Time
19}
20
21// Trim frees the pool mappings, GPU copies and dmabuf imports not drawn
22// for idleTTL, and the objects of frames the GPU finished, without a new
23// frame. What the last frame drew stays: it is on screen and a redraw
24// would copy it again. The output calls it periodically on its goroutine.
25func (r *Renderer) Trim(now time.Time) error {
26 // An idle output keeps its oldest mark: the frame is the same.
27 if n := len(r.marks); n == 0 || r.marks[n-1].frame != r.frame {
28 r.marks = append(r.marks, trimMark{frame: r.frame, at: now})
29 }
30 // cutoff is the newest mark at least idleTTL old: everything last
31 // drawn at or before it has been undrawn for idleTTL.
32 i := -1
33 for j, m := range r.marks {
34 if now.Sub(m.at) >= idleTTL {
35 i = j
36 }
37 }
38 if i >= 0 {
39 cutoff := r.marks[i].frame
40 r.marks = append(r.marks[:0], r.marks[i:]...)
41 stale := func(last uint64) bool { return last <= cutoff && last < r.frame }
42 // The GPU reads the copies, never a mapping, and no frame is being
43 // recorded: unmapping now is safe.
44 for id, m := range r.pools {
45 if stale(m.last) {
46 _ = unix.Munmap(m.data)
47 delete(r.pools, id)
48 }
49 }
50 for key, s := range r.shm {
51 if stale(s.last) {
52 r.retireShm(s)
53 delete(r.shm, key)
54 }
55 }
56 for id, im := range r.imports {
57 if stale(im.last) {
58 r.retire(im.last, func() { r.release(im) })
59 delete(r.imports, id)
60 }
61 }
62 }
63 return r.poll()
64}
65
66// poll records the frames the GPU finished, without waiting, and frees
67// what they held.
68func (r *Renderer) poll() error {
69 var pending uint64 // oldest frame still running, 0: none
70 for i := range r.slots {
71 s := &r.slots[i]
72 if !s.busy {
73 continue
74 }
75 res := r.dd.WaitForFences(r.device, 1, &s.fence, 1, 0)
76 if res == vk.Timeout {
77 if pending == 0 || s.frame < pending {
78 pending = s.frame
79 }
80 continue
81 }
82 if err := checked("vkWaitForFences", res); err != nil {
83 return err
84 }
85 if err := checked("vkResetFences", r.dd.ResetFences(r.device, 1, &s.fence)); err != nil {
86 return err
87 }
88 s.busy = false
89 }
90 if pending == 0 {
91 // Nothing runs: frames recorded but never submitted have no GPU
92 // work either (like idle).
93 r.completed = r.frame
94 } else {
95 r.completed = max(r.completed, pending-1)
96 }
97 r.runDeferred()
98 return nil
99}