1package vulkan
2
3import (
4 "fmt"
5 "image"
6 "runtime/debug"
7 "unsafe"
8
9 "github.com/bnema/neferwl/internal/adapters/hintlabel"
10 "github.com/bnema/neferwl/internal/ports"
11 vk "github.com/bnema/purego-vulkan/vulkan"
12 "golang.org/x/sys/unix"
13)
14
15// wl_shm buffers: each client pool is mapped once, read only. A surface's
16// pixels are copied once per new content into a persistently mapped,
17// host-visible GPU buffer (device-local when the GPU exposes it, ReBAR)
18// that the fragment shader reads directly: one memcpy per row, no staging
19// copy, no resample, no alpha fix-up on the CPU. Each surface has two GPU
20// buffers used in turn, so the CPU never writes one a frame in flight
21// reads. Mappings and buffers are dropped after importTTL frames unused,
22// or idleTTL of wall-clock time (Trim).
23
24// mapping is a client pool mapped read-only.
25type mapping struct {
26 data []byte
27 last uint64
28}
29
30// shmPixels returns the pool bytes of a buffer, mapping the pool once and
31// again when it grew past the mapping.
32func (r *Renderer) shmPixels(b *ports.SHMBuffer, end int) ([]byte, error) {
33 if m := r.pools[b.Pool]; m != nil {
34 if end <= len(m.data) {
35 m.last = r.frame
36 return m.data, nil
37 }
38 // Draws collected earlier this frame may still read the old
39 // mapping: it is unmapped once the frame is recorded.
40 r.retired = append(r.retired, m.data)
41 delete(r.pools, b.Pool)
42 }
43 fd, err := dupFile(b.File)
44 if err != nil {
45 return nil, err
46 }
47 defer unix.Close(fd)
48 var st unix.Stat_t
49 if err := unix.Fstat(fd, &st); err != nil {
50 return nil, err
51 }
52 if int64(end) > st.Size {
53 return nil, fmt.Errorf("shm pool of %d bytes, buffer ends at %d", st.Size, end)
54 }
55 data, err := unix.Mmap(fd, 0, int(st.Size), unix.PROT_READ, unix.MAP_SHARED)
56 if err != nil {
57 return nil, err
58 }
59 r.pools[b.Pool] = &mapping{data: data, last: r.frame}
60 return data, nil
61}
62
63// dropPools unmaps replaced mappings and pools not drawn for importTTL
64// frames. The GPU never reads a mapping: this runs once the CPU copies of
65// a frame are done.
66func (r *Renderer) dropPools() {
67 for _, data := range r.retired {
68 _ = unix.Munmap(data)
69 }
70 r.retired = r.retired[:0]
71 for id, m := range r.pools {
72 if r.frame-m.last > importTTL {
73 _ = unix.Munmap(m.data)
74 delete(r.pools, id)
75 }
76 }
77}
78
79// copyGuarded runs copy with page faults turned into a recoverable panic:
80// a client may shrink its pool file under the mapping (SIGBUS). It reports
81// false when the client broke its buffer.
82func copyGuarded(copy func()) (ok bool) {
83 defer debug.SetPanicOnFault(debug.SetPanicOnFault(true))
84 defer func() {
85 if recover() != nil {
86 ok = false
87 }
88 }()
89 copy()
90 return true
91}
92
93// gpuBuffer is a host-visible storage buffer, mapped for its whole life.
94type gpuBuffer struct {
95 buffer vk.Buffer
96 memory vk.DeviceMemory
97 mapped unsafe.Pointer
98 size int
99 // last is the frame that last read it: it is written or freed only
100 // once that frame completed.
101 last uint64
102}
103
104func (r *Renderer) newGPUBuffer(b *gpuBuffer, size int) error {
105 d := r.dd
106 ok := false
107 defer func() {
108 if !ok {
109 r.freeGPUBuffer(b)
110 }
111 }()
112 info := vk.BufferCreateInfo{SType: vk.StructureTypeBufferCreateInfo, Size: vk.DeviceSize(size), Usage: vk.BufferUsageStorageBufferBit, SharingMode: vk.SharingModeExclusive}
113 if err := checked("vkCreateBuffer(shm)", d.CreateBuffer(r.device, &info, nil, &b.buffer)); err != nil {
114 return err
115 }
116 var req vk.MemoryRequirements
117 d.GetBufferMemoryRequirements(r.device, b.buffer, &req)
118 host := vk.MemoryPropertyFlags(vk.MemoryPropertyHostVisibleBit | vk.MemoryPropertyHostCoherentBit)
119 kind, err := r.findMemoryType(req.MemoryTypeBits, host|vk.MemoryPropertyDeviceLocalBit)
120 if err != nil {
121 kind, err = r.findMemoryType(req.MemoryTypeBits, host)
122 }
123 if err != nil {
124 return err
125 }
126 alloc := vk.MemoryAllocateInfo{SType: vk.StructureTypeMemoryAllocateInfo, AllocationSize: req.Size, MemoryTypeIndex: kind}
127 if err := checked("vkAllocateMemory(shm)", d.AllocateMemory(r.device, &alloc, nil, &b.memory)); err != nil {
128 return err
129 }
130 if err := checked("vkBindBufferMemory(shm)", d.BindBufferMemory(r.device, b.buffer, b.memory, 0)); err != nil {
131 return err
132 }
133 if err := checked("vkMapMemory(shm)", d.MapMemory(r.device, b.memory, 0, vk.DeviceSize(size), 0, &b.mapped)); err != nil {
134 return err
135 }
136 b.size, ok = size, true
137 return nil
138}
139
140func (r *Renderer) freeGPUBuffer(b *gpuBuffer) {
141 d := r.dd
142 if b.mapped != nil {
143 d.UnmapMemory(r.device, b.memory)
144 }
145 if b.buffer != 0 {
146 d.DestroyBuffer(r.device, b.buffer, nil)
147 }
148 if b.memory != 0 {
149 d.FreeMemory(r.device, b.memory, nil)
150 }
151 *b = gpuBuffer{}
152}
153
154// shmKey identifies a surface independently of its position in a tree.
155type shmKey struct {
156 win ports.WindowID
157 surface uint64
158 // label, typed and scale name a compositor-drawn hint label (hint_labels.go)
159 // instead of a client surface: win and surface are 0 and label is never
160 // empty, so such a key cannot collide with a client's.
161 label string
162 typed int
163 scale float64
164 style hintlabel.Style
165}
166
167// shmState is what a GPU buffer holds: a w×h client buffer at a content
168// Seq.
169type shmState struct {
170 w, h int
171 seq uint64
172 windowSeq uint64 // root damage history uses window publications
173}
174
175// shmCopy is one of a surface's two GPU buffers.
176type shmCopy struct {
177 gpu gpuBuffer
178 holds shmState
179 valid bool
180}
181
182// shmSurface is the GPU copy of one surface's wl_shm content.
183type shmSurface struct {
184 bufs [2]*shmCopy
185 last uint64 // frame that drew it
186}
187
188// shmCached returns the GPU buffer already holding a surface's content st,
189// or nil. It needs no client pixels: a kept window whose pool is closed
190// still draws from it.
191func (r *Renderer) shmCached(key shmKey, st shmState) *shmCopy {
192 s := r.shm[key]
193 if s == nil {
194 return nil
195 }
196 s.last = r.frame
197 for _, c := range s.bufs {
198 if c != nil && c.valid && c.holds.w == st.w && c.holds.h == st.h && c.holds.seq == st.seq {
199 c.gpu.last = r.frame
200 return c
201 }
202 }
203 return nil
204}
205
206// shmCopyFor copies a surface's content st from the client pixels (stride
207// bytes per row from offset) into a GPU buffer; the caller found none
208// holding it (shmCached). A buffer holding an older content of the same
209// size gets only what changed since (damage, in buffer pixels; false:
210// everything).
211func (r *Renderer) shmCopyFor(key shmKey, st shmState, pixels []byte, offset, stride int, damage func(since uint64) ([]ports.Rect, bool)) (*shmCopy, error) {
212 s := r.shm[key]
213 if s == nil {
214 s = &shmSurface{}
215 r.shm[key] = s
216 }
217 s.last = r.frame
218 // The older buffer takes the new content.
219 i := 0
220 if s.bufs[0] != nil && (s.bufs[1] == nil || s.bufs[1].gpu.last < s.bufs[0].gpu.last) {
221 i = 1
222 }
223 size := st.w * st.h * 4
224 if size > r.maxRange {
225 return nil, fmt.Errorf("shm buffer of %d bytes past the storage buffer range", size)
226 }
227 c := s.bufs[i]
228 if c != nil && c.gpu.size != size {
229 r.retire(c.gpu.last, func() { r.freeShmCopy(c) })
230 s.bufs[i], c = nil, nil
231 }
232 if c == nil {
233 c = &shmCopy{}
234 if err := r.newGPUBuffer(&c.gpu, size); err != nil {
235 return nil, err
236 }
237 s.bufs[i] = c
238 }
239 if err := r.waitFrame(c.gpu.last); err != nil {
240 return nil, err
241 }
242 dst := unsafe.Slice((*byte)(c.gpu.mapped), size)
243 row := st.w * 4
244 rects := []ports.Rect{{W: st.w, H: st.h}}
245 if c.valid && c.holds.w == st.w && c.holds.h == st.h && damage != nil {
246 if d, ok := damage(c.holds.windowSeq); ok {
247 rects = d
248 }
249 }
250 // The client may shrink its pool under the mapping: its window then
251 // shows garbage for this frame, never a crash, and is copied again.
252 // Damage of the frames since this buffer was written can overlap: each
253 // pixel is copied once.
254 r.shmRects = disjointRects(r.shmRects[:0], rects, st.w, st.h)
255 c.valid = copyGuarded(func() {
256 for _, rc := range r.shmRects {
257 for y := rc.Min.Y; y < rc.Max.Y; y++ {
258 d := y*row + rc.Min.X*4
259 s := offset + y*stride + rc.Min.X*4
260 n := rc.Dx() * 4
261 copy(dst[d:d+n], pixels[s:s+n])
262 r.copied += n
263 }
264 }
265 })
266 c.holds, c.gpu.last = st, r.frame
267 return c, nil
268}
269
270func (r *Renderer) freeShmCopy(c *shmCopy) {
271 r.invalidateCompose(resourceKey{shm: c})
272 r.freeGPUBuffer(&c.gpu)
273}
274
275// dropShm frees the GPU buffers of surfaces not drawn for importTTL frames.
276func (r *Renderer) dropShm() {
277 for key, s := range r.shm {
278 if r.frame-s.last > importTTL {
279 r.retireShm(s)
280 delete(r.shm, key)
281 }
282 }
283}
284
285// retireShm frees a surface's GPU buffers once the frames reading them
286// completed.
287func (r *Renderer) retireShm(s *shmSurface) {
288 for _, c := range s.bufs {
289 if c != nil {
290 r.retire(c.gpu.last, func() { r.freeShmCopy(c) })
291 }
292 }
293}
294
295// maxDisjointRects bounds the pieces disjointRects cuts the damage into: the
296// rects come from the client, and each cut can add three pieces.
297const maxDisjointRects = 64
298
299// disjointRects appends to dst the parts of rects inside a w×h buffer, with
300// no pixel in two of them. Past maxDisjointRects pieces it appends their
301// bounding box instead: some undamaged pixels are copied again. It reuses
302// dst's capacity.
303func disjointRects(dst []image.Rectangle, rects []ports.Rect, w, h int) []image.Rectangle {
304 bounds := image.Rect(0, 0, w, h)
305 start := len(dst)
306 for _, rc := range rects {
307 if len(dst)-start > maxDisjointRects {
308 var box image.Rectangle
309 for _, rc := range rects {
310 box = box.Union(image.Rect(rc.X, rc.Y, rc.X+rc.W, rc.Y+rc.H).Intersect(bounds))
311 }
312 return append(dst[:start], box)
313 }
314 pending := len(dst)
315 dst = append(dst, image.Rect(rc.X, rc.Y, rc.X+rc.W, rc.Y+rc.H).Intersect(bounds))
316 // Cut the new rect by each earlier one; the pieces it leaves are
317 // appended after it and cut in turn.
318 for i := pending; i < len(dst); i++ {
319 if dst[i].Empty() {
320 dst[i] = dst[len(dst)-1]
321 dst = dst[:len(dst)-1]
322 i--
323 continue
324 }
325 for _, o := range dst[:pending] {
326 in := dst[i].Intersect(o)
327 if in.Empty() {
328 continue
329 }
330 r := dst[i]
331 dst[i] = image.Rect(r.Min.X, r.Min.Y, r.Max.X, in.Min.Y)
332 dst = append(dst,
333 image.Rect(r.Min.X, in.Max.Y, r.Max.X, r.Max.Y),
334 image.Rect(r.Min.X, in.Min.Y, in.Min.X, in.Max.Y),
335 image.Rect(in.Max.X, in.Min.Y, r.Max.X, in.Max.Y))
336 i--
337 break
338 }
339 }
340 }
341 return dst
342}