feat/data-dnd e05cb15

yuv.go RawHistoryFolder

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}

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 25

Tags 10

↑↓ moveenter openesc close