1// Package surfaces keeps the latest content of every window for an output's
2// renderer, and the last content of a window that closed while the scene
3// still draws it.
4package surfaces
5
6import (
7 "github.com/bnema/neferwl/internal/adapters/outputkit/capture"
8 "github.com/bnema/neferwl/internal/ports"
9)
10
11// Table is what an output passes to Renderer.Render: the latest content per
12// window. Its owner goroutine is the only user.
13//
14// A window that unmaps sends an empty content right after the core learns
15// of it; the scene that follows may keep drawing the window while it fades
16// out (ports.SceneWindow of a leaving window). The table then keeps the
17// window's last content until a scene no longer lists it, so the renderer
18// draws it from the buffer it already imported or copied: no new upload,
19// no copy. Its Acquire fence is dropped: the first frame that drew the
20// content waited on it, and wayland closes it once the buffer is released.
21//
22// Buffer lifetime: wayland releases the buffer to the client once the
23// output reports the empty content as seen, so the client may draw into,
24// destroy or reuse it during the fade. A kept content is therefore only
25// ever composed by the renderer, from what it already holds: the dmabuf
26// import (its own duplicated descriptors keep the memory alive; a reused
27// buffer shows stale pixels at worst) or the GPU copy of the wl_shm buffer
28// (the GPU never reads the pool mapping, and a pool's fd close does not
29// unmap it). A kept content is never scanned out on a plane and never
30// reported as shown: outputs ask Kept. Window capture never reaches it:
31// core resolves a window session through its workspace, which the closed
32// window has left, so the session ends (CaptureReasonWindowGone) before
33// the leaving scene; an output or workspace capture composes the fading
34// window like the screen does.
35// Every Acquire fence of the kept content (root and children) is dropped:
36// the frame that first drew it waited on them, and wayland closes them
37// when the buffers are released.
38type Table struct {
39 m map[ports.WindowID]ports.SurfaceContent
40 // kept are the windows drawn from a content their client withdrew.
41 kept map[ports.WindowID]struct{}
42}
43
44// New returns an empty table.
45func New() *Table {
46 return &Table{m: make(map[ports.WindowID]ports.SurfaceContent), kept: make(map[ports.WindowID]struct{})}
47}
48
49// Map is the contents the renderer draws from. It is the table's own map:
50// read it, never keep it across an Update or Prune.
51func (t *Table) Map() map[ports.WindowID]ports.SurfaceContent { return t.m }
52
53// Update takes a window's new content. An empty one (the window unmapped)
54// keeps the previous content, without its Acquire fence, while scene draws
55// the window; it drops it otherwise.
56func (t *Table) Update(c ports.SurfaceContent, scene ports.Scene) {
57 if !c.Empty() {
58 t.m[c.ID] = c
59 delete(t.kept, c.ID)
60 return
61 }
62 prev, ok := t.m[c.ID]
63 if !ok || !capture.Shows(scene, c.ID) {
64 delete(t.m, c.ID)
65 delete(t.kept, c.ID)
66 return
67 }
68 prev.Acquire = nil
69 if len(prev.Children) > 0 {
70 // The slice is shared with wayland's publication: copy before
71 // zeroing (one allocation per unmap).
72 children := make([]ports.Subsurface, len(prev.Children))
73 copy(children, prev.Children)
74 for i := range children {
75 children[i].Acquire = nil
76 }
77 prev.Children = children
78 }
79 t.m[c.ID] = prev
80 t.kept[c.ID] = struct{}{}
81}
82
83// Prune drops the kept contents of windows scene no longer draws. Outputs
84// call it with every scene they accept.
85func (t *Table) Prune(scene ports.Scene) {
86 for id := range t.kept {
87 if !capture.Shows(scene, id) {
88 delete(t.m, id)
89 delete(t.kept, id)
90 }
91 }
92}
93
94// Kept reports whether id is drawn from a content its client withdrew: a
95// buffer the client may have got back. Outputs compose it only: no plane
96// scans it out, no capture or presentation report counts it as shown.
97func (t *Table) Kept(id ports.WindowID) bool {
98 _, ok := t.kept[id]
99 return ok
100}
101
102// Shown is the presentation rule both output backends report with: id was
103// shown to its client when scene draws it and its content is not kept. A
104// nil kept counts nothing as kept.
105func Shown(scene ports.Scene, id ports.WindowID, kept func(ports.WindowID) bool) bool {
106 return scene.Shows(id) && (kept == nil || !kept(id))
107}
108
109// FillShown clears dst and fills it with the windows of seen that scene
110// showed to their client (Shown), at their seen Seq.
111func FillShown(dst, seen map[ports.WindowID]uint64, scene ports.Scene, kept func(ports.WindowID) bool) {
112 clear(dst)
113 for id, seq := range seen {
114 if Shown(scene, id, kept) {
115 dst[id] = seq
116 }
117 }
118}