1package vulkan
2
3import (
4 "fmt"
5 "unsafe"
6
7 "github.com/bnema/neferwl/internal/ports"
8 vk "github.com/bnema/purego-vulkan/vulkan"
9 "golang.org/x/sys/unix"
10)
11
12// Vulkan 1.1 multi-planar constants and pNext structures not yet generated by
13// purego-vulkan v0.4.1. Their layouts follow vulkan_core.h.
14const (
15 aspectPlane0 vk.ImageAspectFlags = 0x10
16 aspectPlane1 vk.ImageAspectFlags = 0x20
17 imageDisjoint vk.ImageCreateFlags = 0x200
18 structureImagePlaneRequirements vk.StructureType = 1000156003
19 structureBindImagePlane vk.StructureType = 1000156002
20 structureImageFormatList vk.StructureType = 1000147000
21 formatR10X6 vk.Format = 1000156007
22 formatR10X6G10X6 vk.Format = 1000156008
23)
24
25type imageFormatList struct {
26 sType vk.StructureType
27 next unsafe.Pointer
28 viewFormatCount uint32
29 viewFormats *vk.Format
30}
31
32// The view formats are required by VK_EXT_image_drm_format_modifier when
33// VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT is used for per-plane sampling.
34func yuvViewFormats(format vk.Format) [3]vk.Format {
35 if format == vk.Format(1000156013) {
36 return [3]vk.Format{format, formatR10X6, formatR10X6G10X6}
37 }
38 return [3]vk.Format{format, vk.FormatR8Unorm, vk.FormatR8g8Unorm}
39}
40
41type imagePlaneRequirements struct {
42 sType vk.StructureType
43 next unsafe.Pointer
44 planeAspect vk.ImageAspectFlags
45}
46type bindImagePlane struct {
47 sType vk.StructureType
48 next unsafe.Pointer
49 planeAspect vk.ImageAspectFlags
50}
51
52// importYUV uses one multi-planar VkImage. Planes sharing a DMA-BUF use one
53// memory binding; different backing objects need disjoint support. Views
54// always sample the Y and UV planes separately.
55func (r *Renderer) importYUV(b *ports.DMABuf, format vk.Format) (*imported, error) {
56 im := &imported{fd: -1, yuv: true, last: r.frame, fds: [2]int{-1, -1}}
57 disjoint, err := differentBacking(b.Planes)
58 if err != nil {
59 return nil, err
60 }
61 if disjoint {
62 // Query this modifier with DISJOINT; an advertised shared-memory
63 // import does not imply support for distinct plane objects.
64 if r.physical == 0 || !r.importable(r.physical, format, b.Modifier, true, true) {
65 return nil, fmt.Errorf("YUV buffer has distinct plane objects without Vulkan disjoint support")
66 }
67 }
68 im.disjoint = disjoint
69 ok := false
70 defer func() {
71 if !ok {
72 r.release(im)
73 }
74 }()
75 for i, pl := range b.Planes {
76 fd, err := dupFile(pl.File)
77 if err != nil {
78 return nil, err
79 }
80 im.fds[i] = fd
81 }
82 d := r.dd
83 layouts := [2]vk.SubresourceLayout{}
84 for i, pl := range b.Planes {
85 layouts[i] = vk.SubresourceLayout{Offset: vk.DeviceSize(pl.Offset), RowPitch: vk.DeviceSize(pl.Stride)}
86 }
87 views := yuvViewFormats(format)
88 formats := imageFormatList{sType: structureImageFormatList, viewFormatCount: 3, viewFormats: &views[0]}
89 explicit := vk.ImageDrmFormatModifierExplicitCreateInfoEXT{SType: vk.StructureTypeImageDRMFormatModifierExplicitCreateInfoEXT, DrmFormatModifier: b.Modifier, DrmFormatModifierPlaneCount: 2, PlaneLayouts: &layouts[0]}
90 formats.next = unsafe.Pointer(&explicit)
91 external := vk.ExternalMemoryImageCreateInfo{SType: vk.StructureTypeExternalMemoryImageCreateInfo, Next: unsafe.Pointer(&formats), HandleTypes: vk.ExternalMemoryHandleTypeDMABUFBitEXT}
92 flags := vk.ImageCreateFlags(vk.ImageCreateMutableFormatBit)
93 if disjoint {
94 flags |= imageDisjoint
95 }
96 info := vk.ImageCreateInfo{SType: vk.StructureTypeImageCreateInfo, Next: unsafe.Pointer(&external), Flags: flags, ImageType: vk.ImageType2d, Format: format, Extent: vk.Extent3D{Width: uint32(b.Width), Height: uint32(b.Height), Depth: 1}, MipLevels: 1, ArrayLayers: 1, Samples: vk.SampleCount1Bit, Tiling: vk.ImageTilingDRMFormatModifierEXT, Usage: vk.ImageUsageSampledBit, SharingMode: vk.SharingModeExclusive, InitialLayout: vk.ImageLayoutUndefined}
97 if err := checked("vkCreateImage(YUV dmabuf)", d.CreateImage(r.device, &info, nil, &im.image)); err != nil {
98 return nil, err
99 }
100 if !disjoint {
101 if err := r.bindSharedYUV(im); err != nil {
102 return nil, err
103 }
104 } else {
105 aspects := [2]vk.ImageAspectFlags{aspectPlane0, aspectPlane1}
106 bindings := [2]vk.BindImageMemoryInfo{}
107 for i, fd := range im.fds {
108 props := vk.MemoryFdPropertiesKHR{SType: vk.StructureTypeMemoryFDPropertiesKHR}
109 if err := checked("vkGetMemoryFdPropertiesKHR(YUV)", d.GetMemoryFdPropertiesKHR(r.device, vk.ExternalMemoryHandleTypeDMABUFBitEXT, int32(fd), &props)); err != nil {
110 return nil, err
111 }
112 plane := imagePlaneRequirements{sType: structureImagePlaneRequirements, planeAspect: aspects[i]}
113 request := vk.ImageMemoryRequirementsInfo2{SType: vk.StructureTypeImageMemoryRequirementsInfo2, Next: unsafe.Pointer(&plane), Image: im.image}
114 req := vk.MemoryRequirements2{SType: vk.StructureTypeMemoryRequirements2}
115 d.GetImageMemoryRequirements2(r.device, &request, &req)
116 kind, err := r.findMemoryType(req.MemoryRequirements.MemoryTypeBits&props.MemoryTypeBits, 0)
117 if err != nil {
118 return nil, err
119 }
120 memFD, err := unix.FcntlInt(uintptr(fd), unix.F_DUPFD_CLOEXEC, 0)
121 if err != nil {
122 return nil, err
123 }
124 dedicated := vk.MemoryDedicatedAllocateInfo{SType: vk.StructureTypeMemoryDedicatedAllocateInfo, Image: im.image}
125 imp := vk.ImportMemoryFdInfoKHR{SType: vk.StructureTypeImportMemoryFDInfoKHR, Next: unsafe.Pointer(&dedicated), HandleType: vk.ExternalMemoryHandleTypeDMABUFBitEXT, Fd: int32(memFD)}
126 alloc := vk.MemoryAllocateInfo{SType: vk.StructureTypeMemoryAllocateInfo, Next: unsafe.Pointer(&imp), AllocationSize: req.MemoryRequirements.Size, MemoryTypeIndex: kind}
127 if err := checked("vkAllocateMemory(YUV plane)", d.AllocateMemory(r.device, &alloc, nil, &im.memories[i])); err != nil {
128 unix.Close(memFD)
129 return nil, err
130 }
131 planeBind := &bindImagePlane{sType: structureBindImagePlane, planeAspect: aspects[i]}
132 bindings[i] = vk.BindImageMemoryInfo{SType: vk.StructureTypeBindImageMemoryInfo, Next: unsafe.Pointer(planeBind), Image: im.image, Memory: im.memories[i]}
133 // pNext must remain alive through vkBindImageMemory2, so bind each plane.
134 if err := checked("vkBindImageMemory2(YUV plane)", d.BindImageMemory2(r.device, 1, &bindings[i])); err != nil {
135 return nil, err
136 }
137 }
138 }
139 yFormat, uvFormat := vk.Format(vk.FormatR8Unorm), vk.Format(vk.FormatR8g8Unorm)
140 if b.Format == fourcc('P', '0', '1', '0') {
141 yFormat, uvFormat = formatR10X6, formatR10X6G10X6
142 }
143 view := func(aspect vk.ImageAspectFlags, f vk.Format) (vk.ImageView, error) {
144 vi := vk.ImageViewCreateInfo{SType: vk.StructureTypeImageViewCreateInfo, Image: im.image, ViewType: vk.ImageViewType2d, Format: f, SubresourceRange: vk.ImageSubresourceRange{AspectMask: aspect, LevelCount: 1, LayerCount: 1}}
145 var result vk.ImageView
146 err := checked("vkCreateImageView(YUV plane)", d.CreateImageView(r.device, &vi, nil, &result))
147 return result, err
148 }
149 if im.view, err = view(aspectPlane0, yFormat); err != nil {
150 return nil, err
151 }
152 if im.chromaView, err = view(aspectPlane1, uvFormat); err != nil {
153 return nil, err
154 }
155 ok = true
156 r.imports[b.ID] = im
157 return im, nil
158}
159
160// differentBacking identifies duplicate descriptors of one DMA-BUF by inode.
161// Compare the opened files themselves, not their integer descriptor values.
162func differentBacking(planes []ports.DMABufPlane) (bool, error) {
163 if len(planes) != 2 {
164 return false, fmt.Errorf("expected two DMA-BUF planes")
165 }
166 var a, b unix.Stat_t
167 for i, p := range planes {
168 if p.File == nil {
169 return false, fmt.Errorf("nil DMA-BUF plane")
170 }
171 var st *unix.Stat_t
172 if i == 0 {
173 st = &a
174 } else {
175 st = &b
176 }
177 if err := unix.Fstat(int(p.File.Fd()), st); err != nil {
178 return false, err
179 }
180 }
181 return a.Dev != b.Dev || a.Ino != b.Ino, nil
182}
183
184// bindSharedYUV imports one DMA-BUF memory for both format planes.
185func (r *Renderer) bindSharedYUV(im *imported) error {
186 d := r.dd
187 fd := im.fds[0]
188 props := vk.MemoryFdPropertiesKHR{SType: vk.StructureTypeMemoryFDPropertiesKHR}
189 if err := checked("vkGetMemoryFdPropertiesKHR(YUV shared)", d.GetMemoryFdPropertiesKHR(r.device, vk.ExternalMemoryHandleTypeDMABUFBitEXT, int32(fd), &props)); err != nil {
190 return err
191 }
192 var req vk.MemoryRequirements
193 d.GetImageMemoryRequirements(r.device, im.image, &req)
194 kind, err := r.findMemoryType(req.MemoryTypeBits&props.MemoryTypeBits, 0)
195 if err != nil {
196 return err
197 }
198 memFD, err := unix.FcntlInt(uintptr(fd), unix.F_DUPFD_CLOEXEC, 0)
199 if err != nil {
200 return err
201 }
202 dedicated := vk.MemoryDedicatedAllocateInfo{SType: vk.StructureTypeMemoryDedicatedAllocateInfo, Image: im.image}
203 imp := vk.ImportMemoryFdInfoKHR{SType: vk.StructureTypeImportMemoryFDInfoKHR, Next: unsafe.Pointer(&dedicated), HandleType: vk.ExternalMemoryHandleTypeDMABUFBitEXT, Fd: int32(memFD)}
204 alloc := vk.MemoryAllocateInfo{SType: vk.StructureTypeMemoryAllocateInfo, Next: unsafe.Pointer(&imp), AllocationSize: req.Size, MemoryTypeIndex: kind}
205 if err := checked("vkAllocateMemory(YUV shared)", d.AllocateMemory(r.device, &alloc, nil, &im.memory)); err != nil {
206 unix.Close(memFD)
207 return err
208 }
209 return checked("vkBindImageMemory(YUV shared)", d.BindImageMemory(r.device, im.image, im.memory, 0))
210}