detached db918d9

refactor(vkdevice): drop wlturbo from DMA-BUF image allocation

AllocateImage now returns the exported DMA-BUF fd (closed by Image.Close),
ChooseModifier takes flat formats. The Wayland session imports the buffer
itself from a duplicated fd.
commit
db918d9326b07a1f0fa5b92b8ad0d0f1bc2d171e
author
brice · · 2026-10-01 16:05 UTC
parent
ed16cf7
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +84 -51
M internal/presentation/session/session.go +57 -8
@@ -24,6 +24,7 @@ import (
2424 "github.com/bnema/nefergui/internal/presentation/vkdevice"
2525 "github.com/bnema/nefergui/internal/render"
2626 "github.com/bnema/nefergui/internal/text"
27+ "github.com/bnema/wlturbo/protocol/core"
2728 "github.com/bnema/wlturbo/protocol/drmsyncobj"
2829 "golang.org/x/sys/unix"
2930 )
@@ -40,6 +41,13 @@ type Slot struct {
4041 ReadbackPending bool
4142 ReleaseHandle uint32
4243 ReleaseTimeline *drmsyncobj.WpLinuxDrmSyncobjTimeline
44+ // WlBuffer is the compositor-side wl_buffer of the Wayland-owning Session.
45+ WlBuffer *core.Buffer
46+
47+ // Used only by Target, the Wayland-free core. Descriptors are -1 when unset.
48+ ReleaseFD int // eventfd armed on each commit's release point
49+ ReleaseExport int // exported syncobj fd of the release timeline
50+ Presented bool // the caller has been told to import this buffer
4351 }
4452 type Session struct {
4553 Window *wayland.Window
@@ -259,7 +267,13 @@ func (s *Session) newGeneration() error {
259267 if err != nil {
260268 return err
261269 }
262- selected, err := vkdevice.ChooseModifier(s.Window.Tranches, s.Transparent, mods)
270+ var formats []vkdevice.Format
271+ for _, tranche := range s.Window.Tranches {
272+ for _, entry := range tranche {
273+ formats = append(formats, vkdevice.Format{FourCC: entry.Format, Modifier: entry.Modifier})
274+ }
275+ }
276+ selected, err := vkdevice.ChooseModifier(formats, s.Transparent, mods)
263277 if err != nil {
264278 return err
265279 }
@@ -286,7 +300,7 @@ func (s *Session) newGeneration() error {
286300 if b.Generation != s.Pool.Generation {
287301 continue
288302 }
289- img, e := s.Device.AllocateImage(width, height, selected, s.Window.Dmabuf)
303+ img, e := s.Device.AllocateImage(width, height, selected)
290304 if e != nil {
291305 return e
292306 }
@@ -302,25 +316,60 @@ func (s *Session) newGeneration() error {
302316 return e
303317 }
304318 slot := &Slot{Buffer: b, Image: img, Frame: frame, First: true}
319+ if slot.WlBuffer, e = s.importBuffer(img, width, height, selected.DRMFormat); e != nil {
320+ frame.Close()
321+ img.Close()
322+ return e
323+ }
305324 slot.ReleaseHandle, e = s.Node.Create()
306325 if e != nil {
326+ slot.closeImage()
307327 frame.Close()
308- img.Close()
309328 return e
310329 }
311330 slot.ReleaseTimeline, e = s.importTimeline(slot.ReleaseHandle)
312331 if e != nil {
313332 _ = s.Node.Destroy(slot.ReleaseHandle)
333+ slot.closeImage()
314334 frame.Close()
315- img.Close()
316335 return e
317336 }
318337 s.Slots[b.ID] = slot
319- img.Buffer.OnRelease(func() { s.Window.BufferReleased(b.ID) })
338+ slot.WlBuffer.OnRelease(func() { s.Window.BufferReleased(b.ID) })
320339 }
321340 return nil
322341 }
323342
343+// importBuffer wraps the image's DMA-BUF in a wl_buffer. The image keeps its
344+// own descriptor; a duplicate goes to the compositor.
345+func (s *Session) importBuffer(img *vkdevice.Image, width, height int32, format uint32) (*core.Buffer, error) {
346+ fd, err := unix.FcntlInt(uintptr(img.FD), unix.F_DUPFD_CLOEXEC, 3)
347+ if err != nil {
348+ return nil, fmt.Errorf("duplicate DMA-BUF fd: %w", err)
349+ }
350+ params, err := s.Window.Dmabuf.CreateParams()
351+ if err != nil {
352+ _ = unix.Close(fd)
353+ return nil, err
354+ }
355+ defer params.Destroy()
356+ // WLTurbo consumes fd only when its complete Add request succeeds.
357+ if err = params.Add(fd, 0, img.Offset, img.Stride, uint32(img.Modifier>>32), uint32(img.Modifier)); err != nil {
358+ _ = unix.Close(fd)
359+ return nil, err
360+ }
361+ return params.CreateImmed(width, height, format, 0)
362+}
363+
364+// closeImage destroys the wl_buffer, then frees the GPU image.
365+func (slot *Slot) closeImage() {
366+ if slot.WlBuffer != nil {
367+ _ = slot.WlBuffer.Destroy()
368+ slot.WlBuffer = nil
369+ }
370+ slot.Image.Close()
371+}
372+
324373 // beginReady does not change Pool ownership until the previous GPU submission
325374 // has completed. An unready available buffer is retried on a bounded wakeup.
326375 func beginReady(pool *buffers.Pool, slots map[uint64]*Slot, ready func(*Slot) (bool, error)) (*buffers.Buffer, *Slot, bool, error) {
@@ -455,7 +504,7 @@ func (s *Session) tick(rect *vkdevice.Rect, instances []vkdevice.Instance, uploa
455504 if err != nil {
456505 return false, err
457506 }
458- if err = s.Window.Present(slot.Image.Buffer, s.AcquireTimeline, slot.ReleaseTimeline, acquire, release); err != nil {
507+ if err = s.Window.Present(slot.WlBuffer, s.AcquireTimeline, slot.ReleaseTimeline, acquire, release); err != nil {
459508 return false, err
460509 }
461510 s.traceState(b, "committed")
@@ -670,7 +719,7 @@ func (s *Session) retire() error {
670719 if slot.Wait != nil {
671720 slot.Wait.Close()
672721 }
673- slot.Image.Close()
722+ slot.closeImage()
674723 _ = slot.ReleaseTimeline.Destroy()
675724 _ = s.Node.Destroy(slot.ReleaseHandle)
676725 s.traceState(b, "retired")
@@ -898,7 +947,7 @@ stopWaiters:
898947 }
899948 // The exported DMA-BUF remains alive in the compositor independently.
900949 if slot.Image != nil {
901- slot.Image.Close()
950+ slot.closeImage()
902951 }
903952 if s.Node != nil && slot.ReleaseHandle != 0 {
904953 _ = s.Node.Destroy(slot.ReleaseHandle)
@@ -24,6 +24,7 @@ import (
24 "github.com/bnema/nefergui/internal/presentation/vkdevice"24 "github.com/bnema/nefergui/internal/presentation/vkdevice"
25 "github.com/bnema/nefergui/internal/render"25 "github.com/bnema/nefergui/internal/render"
26 "github.com/bnema/nefergui/internal/text"26 "github.com/bnema/nefergui/internal/text"
27+ "github.com/bnema/wlturbo/protocol/core"
27 "github.com/bnema/wlturbo/protocol/drmsyncobj"28 "github.com/bnema/wlturbo/protocol/drmsyncobj"
28 "golang.org/x/sys/unix"29 "golang.org/x/sys/unix"
29 )30 )
@@ -40,6 +41,13 @@ type Slot struct {
40 ReadbackPending bool41 ReadbackPending bool
41 ReleaseHandle uint3242 ReleaseHandle uint32
42 ReleaseTimeline *drmsyncobj.WpLinuxDrmSyncobjTimeline43 ReleaseTimeline *drmsyncobj.WpLinuxDrmSyncobjTimeline
44+ // WlBuffer is the compositor-side wl_buffer of the Wayland-owning Session.
45+ WlBuffer *core.Buffer
46+
47+ // Used only by Target, the Wayland-free core. Descriptors are -1 when unset.
48+ ReleaseFD int // eventfd armed on each commit's release point
49+ ReleaseExport int // exported syncobj fd of the release timeline
50+ Presented bool // the caller has been told to import this buffer
43 }51 }
44 type Session struct {52 type Session struct {
45 Window *wayland.Window53 Window *wayland.Window
@@ -259,7 +267,13 @@ func (s *Session) newGeneration() error {
259 if err != nil {267 if err != nil {
260 return err268 return err
261 }269 }
262- selected, err := vkdevice.ChooseModifier(s.Window.Tranches, s.Transparent, mods)270+ var formats []vkdevice.Format
271+ for _, tranche := range s.Window.Tranches {
272+ for _, entry := range tranche {
273+ formats = append(formats, vkdevice.Format{FourCC: entry.Format, Modifier: entry.Modifier})
274+ }
275+ }
276+ selected, err := vkdevice.ChooseModifier(formats, s.Transparent, mods)
263 if err != nil {277 if err != nil {
264 return err278 return err
265 }279 }
@@ -286,7 +300,7 @@ func (s *Session) newGeneration() error {
286 if b.Generation != s.Pool.Generation {300 if b.Generation != s.Pool.Generation {
287 continue301 continue
288 }302 }
289- img, e := s.Device.AllocateImage(width, height, selected, s.Window.Dmabuf)303+ img, e := s.Device.AllocateImage(width, height, selected)
290 if e != nil {304 if e != nil {
291 return e305 return e
292 }306 }
@@ -302,25 +316,60 @@ func (s *Session) newGeneration() error {
302 return e316 return e
303 }317 }
304 slot := &Slot{Buffer: b, Image: img, Frame: frame, First: true}318 slot := &Slot{Buffer: b, Image: img, Frame: frame, First: true}
319+ if slot.WlBuffer, e = s.importBuffer(img, width, height, selected.DRMFormat); e != nil {
320+ frame.Close()
321+ img.Close()
322+ return e
323+ }
305 slot.ReleaseHandle, e = s.Node.Create()324 slot.ReleaseHandle, e = s.Node.Create()
306 if e != nil {325 if e != nil {
326+ slot.closeImage()
307 frame.Close()327 frame.Close()
308- img.Close()
309 return e328 return e
310 }329 }
311 slot.ReleaseTimeline, e = s.importTimeline(slot.ReleaseHandle)330 slot.ReleaseTimeline, e = s.importTimeline(slot.ReleaseHandle)
312 if e != nil {331 if e != nil {
313 _ = s.Node.Destroy(slot.ReleaseHandle)332 _ = s.Node.Destroy(slot.ReleaseHandle)
333+ slot.closeImage()
314 frame.Close()334 frame.Close()
315- img.Close()
316 return e335 return e
317 }336 }
318 s.Slots[b.ID] = slot337 s.Slots[b.ID] = slot
319- img.Buffer.OnRelease(func() { s.Window.BufferReleased(b.ID) })338+ slot.WlBuffer.OnRelease(func() { s.Window.BufferReleased(b.ID) })
320 }339 }
321 return nil340 return nil
322 }341 }
323 342
343+// importBuffer wraps the image's DMA-BUF in a wl_buffer. The image keeps its
344+// own descriptor; a duplicate goes to the compositor.
345+func (s *Session) importBuffer(img *vkdevice.Image, width, height int32, format uint32) (*core.Buffer, error) {
346+ fd, err := unix.FcntlInt(uintptr(img.FD), unix.F_DUPFD_CLOEXEC, 3)
347+ if err != nil {
348+ return nil, fmt.Errorf("duplicate DMA-BUF fd: %w", err)
349+ }
350+ params, err := s.Window.Dmabuf.CreateParams()
351+ if err != nil {
352+ _ = unix.Close(fd)
353+ return nil, err
354+ }
355+ defer params.Destroy()
356+ // WLTurbo consumes fd only when its complete Add request succeeds.
357+ if err = params.Add(fd, 0, img.Offset, img.Stride, uint32(img.Modifier>>32), uint32(img.Modifier)); err != nil {
358+ _ = unix.Close(fd)
359+ return nil, err
360+ }
361+ return params.CreateImmed(width, height, format, 0)
362+}
363+
364+// closeImage destroys the wl_buffer, then frees the GPU image.
365+func (slot *Slot) closeImage() {
366+ if slot.WlBuffer != nil {
367+ _ = slot.WlBuffer.Destroy()
368+ slot.WlBuffer = nil
369+ }
370+ slot.Image.Close()
371+}
372+
324 // beginReady does not change Pool ownership until the previous GPU submission373 // beginReady does not change Pool ownership until the previous GPU submission
325 // has completed. An unready available buffer is retried on a bounded wakeup.374 // has completed. An unready available buffer is retried on a bounded wakeup.
326 func beginReady(pool *buffers.Pool, slots map[uint64]*Slot, ready func(*Slot) (bool, error)) (*buffers.Buffer, *Slot, bool, error) {375 func beginReady(pool *buffers.Pool, slots map[uint64]*Slot, ready func(*Slot) (bool, error)) (*buffers.Buffer, *Slot, bool, error) {
@@ -455,7 +504,7 @@ func (s *Session) tick(rect *vkdevice.Rect, instances []vkdevice.Instance, uploa
455 if err != nil {504 if err != nil {
456 return false, err505 return false, err
457 }506 }
458- if err = s.Window.Present(slot.Image.Buffer, s.AcquireTimeline, slot.ReleaseTimeline, acquire, release); err != nil {507+ if err = s.Window.Present(slot.WlBuffer, s.AcquireTimeline, slot.ReleaseTimeline, acquire, release); err != nil {
459 return false, err508 return false, err
460 }509 }
461 s.traceState(b, "committed")510 s.traceState(b, "committed")
@@ -670,7 +719,7 @@ func (s *Session) retire() error {
670 if slot.Wait != nil {719 if slot.Wait != nil {
671 slot.Wait.Close()720 slot.Wait.Close()
672 }721 }
673- slot.Image.Close()722+ slot.closeImage()
674 _ = slot.ReleaseTimeline.Destroy()723 _ = slot.ReleaseTimeline.Destroy()
675 _ = s.Node.Destroy(slot.ReleaseHandle)724 _ = s.Node.Destroy(slot.ReleaseHandle)
676 s.traceState(b, "retired")725 s.traceState(b, "retired")
@@ -898,7 +947,7 @@ stopWaiters:
898 }947 }
899 // The exported DMA-BUF remains alive in the compositor independently.948 // The exported DMA-BUF remains alive in the compositor independently.
900 if slot.Image != nil {949 if slot.Image != nil {
901- slot.Image.Close()950+ slot.closeImage()
902 }951 }
903 if s.Node != nil && slot.ReleaseHandle != 0 {952 if s.Node != nil && slot.ReleaseHandle != 0 {
904 _ = s.Node.Destroy(slot.ReleaseHandle)953 _ = s.Node.Destroy(slot.ReleaseHandle)
M internal/presentation/vkdevice/image.go +14 -34
@@ -8,28 +8,28 @@ import (
88 "unsafe"
99
1010 "github.com/bnema/purego-vulkan/vulkan"
11- "github.com/bnema/wlturbo/protocol/core"
12- "github.com/bnema/wlturbo/protocol/linuxdmabuf"
1311 "golang.org/x/sys/unix"
1412 )
1513
16-// Image owns a Vulkan image, dedicated exportable memory and its Wayland buffer.
17-// Close must only be called after both wl_buffer.release and the compositor's
18-// release timeline point, or for an image never attached to a surface.
14+// Image owns a Vulkan image, dedicated exportable memory and the exported
15+// DMA-BUF file descriptor. FD stays owned by the Image and is closed by Close;
16+// a transport that consumes descriptors must be handed a dup. The compositor's
17+// import keeps its own kernel reference, so Close may run while the compositor
18+// still holds the buffer, but the GPU must be done with the image first.
1919 type Image struct {
2020 Image vulkan.Image
2121 Memory vulkan.DeviceMemory
22- Buffer *core.Buffer
22+ FD int // exported DMA-BUF, -1 when none
2323 Modifier uint64
2424 Offset, Stride uint32
2525 device *Device
2626 }
2727
28-func (d *Device) AllocateImage(width, height int32, selected Modifier, dmabuf *linuxdmabuf.LinuxDmabuf) (img *Image, err error) {
29- if width <= 0 || height <= 0 || dmabuf == nil {
30- return nil, fmt.Errorf("invalid DMA-BUF image dimensions or manager")
28+func (d *Device) AllocateImage(width, height int32, selected Modifier) (img *Image, err error) {
29+ if width <= 0 || height <= 0 {
30+ return nil, fmt.Errorf("invalid DMA-BUF image dimensions")
3131 }
32- img = &Image{device: d}
32+ img = &Image{device: d, FD: -1}
3333 defer func() {
3434 if err != nil {
3535 img.Close()
@@ -88,27 +88,7 @@ func (d *Device) AllocateImage(width, height int32, selected Modifier, dmabuf *l
8888 if err = vulkan.Check(d.Dispatch.GetMemoryFdKHR(d.Logical, &fdInfo, &fd)); err != nil {
8989 return nil, fmt.Errorf("export DMA-BUF: %w", err)
9090 }
91- // WLTurbo consumes fd only when its complete Add request succeeds. On a
92- // failed send Vulkan still owns the image and we still own the exported FD.
93- owned := true
94- defer func() {
95- if owned {
96- _ = unix.Close(int(fd))
97- }
98- }()
99- params, e := dmabuf.CreateParams()
100- if e != nil {
101- return nil, e
102- }
103- defer params.Destroy()
104- if err = params.Add(int(fd), 0, img.Offset, img.Stride, uint32(img.Modifier>>32), uint32(img.Modifier)); err != nil {
105- return nil, err
106- }
107- owned = false
108- img.Buffer, err = params.CreateImmed(width, height, selected.DRMFormat, 0)
109- if err != nil {
110- return nil, err
111- }
91+ img.FD = int(fd)
11292 return img, nil
11393 }
11494
@@ -116,9 +96,9 @@ func (img *Image) Close() {
11696 if img == nil || img.device == nil {
11797 return
11898 }
119- if img.Buffer != nil {
120- _ = img.Buffer.Destroy()
121- img.Buffer = nil
99+ if img.FD >= 0 {
100+ _ = unix.Close(img.FD)
101+ img.FD = -1
122102 }
123103 if img.Image != 0 {
124104 img.device.Dispatch.DestroyImage(img.device.Logical, img.Image, nil)
@@ -8,28 +8,28 @@ import (
8 "unsafe"8 "unsafe"
9 9
10 "github.com/bnema/purego-vulkan/vulkan"10 "github.com/bnema/purego-vulkan/vulkan"
11- "github.com/bnema/wlturbo/protocol/core"
12- "github.com/bnema/wlturbo/protocol/linuxdmabuf"
13 "golang.org/x/sys/unix"11 "golang.org/x/sys/unix"
14 )12 )
15 13
16-// Image owns a Vulkan image, dedicated exportable memory and its Wayland buffer.14+// Image owns a Vulkan image, dedicated exportable memory and the exported
17-// Close must only be called after both wl_buffer.release and the compositor's15+// DMA-BUF file descriptor. FD stays owned by the Image and is closed by Close;
18-// release timeline point, or for an image never attached to a surface.16+// a transport that consumes descriptors must be handed a dup. The compositor's
17+// import keeps its own kernel reference, so Close may run while the compositor
18+// still holds the buffer, but the GPU must be done with the image first.
19 type Image struct {19 type Image struct {
20 Image vulkan.Image20 Image vulkan.Image
21 Memory vulkan.DeviceMemory21 Memory vulkan.DeviceMemory
22- Buffer *core.Buffer22+ FD int // exported DMA-BUF, -1 when none
23 Modifier uint6423 Modifier uint64
24 Offset, Stride uint3224 Offset, Stride uint32
25 device *Device25 device *Device
26 }26 }
27 27
28-func (d *Device) AllocateImage(width, height int32, selected Modifier, dmabuf *linuxdmabuf.LinuxDmabuf) (img *Image, err error) {28+func (d *Device) AllocateImage(width, height int32, selected Modifier) (img *Image, err error) {
29- if width <= 0 || height <= 0 || dmabuf == nil {29+ if width <= 0 || height <= 0 {
30- return nil, fmt.Errorf("invalid DMA-BUF image dimensions or manager")30+ return nil, fmt.Errorf("invalid DMA-BUF image dimensions")
31 }31 }
32- img = &Image{device: d}32+ img = &Image{device: d, FD: -1}
33 defer func() {33 defer func() {
34 if err != nil {34 if err != nil {
35 img.Close()35 img.Close()
@@ -88,27 +88,7 @@ func (d *Device) AllocateImage(width, height int32, selected Modifier, dmabuf *l
88 if err = vulkan.Check(d.Dispatch.GetMemoryFdKHR(d.Logical, &fdInfo, &fd)); err != nil {88 if err = vulkan.Check(d.Dispatch.GetMemoryFdKHR(d.Logical, &fdInfo, &fd)); err != nil {
89 return nil, fmt.Errorf("export DMA-BUF: %w", err)89 return nil, fmt.Errorf("export DMA-BUF: %w", err)
90 }90 }
91- // WLTurbo consumes fd only when its complete Add request succeeds. On a91+ img.FD = int(fd)
92- // failed send Vulkan still owns the image and we still own the exported FD.
93- owned := true
94- defer func() {
95- if owned {
96- _ = unix.Close(int(fd))
97- }
98- }()
99- params, e := dmabuf.CreateParams()
100- if e != nil {
101- return nil, e
102- }
103- defer params.Destroy()
104- if err = params.Add(int(fd), 0, img.Offset, img.Stride, uint32(img.Modifier>>32), uint32(img.Modifier)); err != nil {
105- return nil, err
106- }
107- owned = false
108- img.Buffer, err = params.CreateImmed(width, height, selected.DRMFormat, 0)
109- if err != nil {
110- return nil, err
111- }
112 return img, nil92 return img, nil
113 }93 }
114 94
@@ -116,9 +96,9 @@ func (img *Image) Close() {
116 if img == nil || img.device == nil {96 if img == nil || img.device == nil {
117 return97 return
118 }98 }
119- if img.Buffer != nil {99+ if img.FD >= 0 {
120- _ = img.Buffer.Destroy()100+ _ = unix.Close(img.FD)
121- img.Buffer = nil101+ img.FD = -1
122 }102 }
123 if img.Image != 0 {103 if img.Image != 0 {
124 img.device.Dispatch.DestroyImage(img.device.Logical, img.Image, nil)104 img.device.Dispatch.DestroyImage(img.device.Logical, img.Image, nil)
M internal/presentation/vkdevice/modifier.go +12 -7
@@ -7,7 +7,6 @@ import (
77 "unsafe"
88
99 "github.com/bnema/purego-vulkan/vulkan"
10- "github.com/bnema/wlturbo/protocol/linuxdmabuf"
1110 )
1211
1312 // Modifier is the one-plane GPU/compositor common image layout chosen in
@@ -22,16 +21,22 @@ const (
2221 ARGB8888 uint32 = 0x34325241
2322 )
2423
25-func ChooseModifier(tranches [][]linuxdmabuf.FormatEntry, transparent bool, supported map[uint64]bool) (Modifier, error) {
24+// Format is one DRM format and modifier pair the compositor accepts.
25+type Format struct {
26+ FourCC uint32
27+ Modifier uint64
28+}
29+
30+// ChooseModifier picks the first format in the compositor's preference order
31+// whose modifier the GPU can also export.
32+func ChooseModifier(formats []Format, transparent bool, supported map[uint64]bool) (Modifier, error) {
2633 format := XRGB8888
2734 if transparent {
2835 format = ARGB8888
2936 }
30- for _, tranche := range tranches {
31- for _, entry := range tranche {
32- if entry.Format == format && supported[entry.Modifier] {
33- return Modifier{format, entry.Modifier}, nil
34- }
37+ for _, f := range formats {
38+ if f.FourCC == format && supported[f.Modifier] {
39+ return Modifier{format, f.Modifier}, nil
3540 }
3641 }
3742 return Modifier{}, fmt.Errorf("no one-plane exportable B8G8R8A8 modifier shared with compositor for DRM format %#x", format)
@@ -7,7 +7,6 @@ import (
7 "unsafe"7 "unsafe"
8 8
9 "github.com/bnema/purego-vulkan/vulkan"9 "github.com/bnema/purego-vulkan/vulkan"
10- "github.com/bnema/wlturbo/protocol/linuxdmabuf"
11 )10 )
12 11
13 // Modifier is the one-plane GPU/compositor common image layout chosen in12 // Modifier is the one-plane GPU/compositor common image layout chosen in
@@ -22,16 +21,22 @@ const (
22 ARGB8888 uint32 = 0x3432524121 ARGB8888 uint32 = 0x34325241
23 )22 )
24 23
25-func ChooseModifier(tranches [][]linuxdmabuf.FormatEntry, transparent bool, supported map[uint64]bool) (Modifier, error) {24+// Format is one DRM format and modifier pair the compositor accepts.
25+type Format struct {
26+ FourCC uint32
27+ Modifier uint64
28+}
29+
30+// ChooseModifier picks the first format in the compositor's preference order
31+// whose modifier the GPU can also export.
32+func ChooseModifier(formats []Format, transparent bool, supported map[uint64]bool) (Modifier, error) {
26 format := XRGB888833 format := XRGB8888
27 if transparent {34 if transparent {
28 format = ARGB888835 format = ARGB8888
29 }36 }
30- for _, tranche := range tranches {37+ for _, f := range formats {
31- for _, entry := range tranche {38+ if f.FourCC == format && supported[f.Modifier] {
32- if entry.Format == format && supported[entry.Modifier] {39+ return Modifier{format, f.Modifier}, nil
33- return Modifier{format, entry.Modifier}, nil
34- }
35 }40 }
36 }41 }
37 return Modifier{}, fmt.Errorf("no one-plane exportable B8G8R8A8 modifier shared with compositor for DRM format %#x", format)42 return Modifier{}, fmt.Errorf("no one-plane exportable B8G8R8A8 modifier shared with compositor for DRM format %#x", format)
M internal/presentation/vkdevice/modifier_test.go +1 -2
@@ -3,14 +3,13 @@
33 package vkdevice
44
55 import (
6- "github.com/bnema/wlturbo/protocol/linuxdmabuf"
76 "golang.org/x/sys/unix"
87 "os"
98 "testing"
109 )
1110
1211 func TestModifierIntersection(t *testing.T) {
13- tranches := [][]linuxdmabuf.FormatEntry{{{Format: XRGB8888, Modifier: 11}, {Format: ARGB8888, Modifier: 22}}, {{Format: XRGB8888, Modifier: 33}, {Format: ARGB8888, Modifier: 44}}}
12+ tranches := []Format{{XRGB8888, 11}, {ARGB8888, 22}, {XRGB8888, 33}, {ARGB8888, 44}}
1413 for _, tc := range []struct {
1514 alpha bool
1615 available map[uint64]bool
@@ -3,14 +3,13 @@
3 package vkdevice3 package vkdevice
4 4
5 import (5 import (
6- "github.com/bnema/wlturbo/protocol/linuxdmabuf"
7 "golang.org/x/sys/unix"6 "golang.org/x/sys/unix"
8 "os"7 "os"
9 "testing"8 "testing"
10 )9 )
11 10
12 func TestModifierIntersection(t *testing.T) {11 func TestModifierIntersection(t *testing.T) {
13- tranches := [][]linuxdmabuf.FormatEntry{{{Format: XRGB8888, Modifier: 11}, {Format: ARGB8888, Modifier: 22}}, {{Format: XRGB8888, Modifier: 33}, {Format: ARGB8888, Modifier: 44}}}12+ tranches := []Format{{XRGB8888, 11}, {ARGB8888, 22}, {XRGB8888, 33}, {ARGB8888, 44}}
14 for _, tc := range []struct {13 for _, tc := range []struct {
15 alpha bool14 alpha bool
16 available map[uint64]bool15 available map[uint64]bool

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 12

↑↓ moveenter openesc close