v0.2.0 c616855

image.go RawHistoryFolder

1package gpu
2
3import (
4	"fmt"
5	"math"
6	"unsafe"
7
8	"github.com/bnema/purego-vulkan/vulkan"
9	"golang.org/x/sys/unix"
10)
11
12// Image is a render target backed by exportable DMA-BUF memory. FD stays owned
13// by the Image; the Wayland layer duplicates it on import.
14type Image struct {
15	dev    *Device
16	image  vulkan.Image
17	memory vulkan.DeviceMemory
18	view   vulkan.ImageView
19
20	Width, Height  int32
21	FD             int
22	Modifier       uint64
23	Offset, Stride uint32
24
25	// used is false until the first render: the first transition starts from
26	// an undefined layout.
27	used bool
28}
29
30// NewImage allocates a Width x Height B8G8R8A8 image with the given modifier.
31func (d *Device) NewImage(width, height int32, mod Modifier) (img *Image, err error) {
32	if width <= 0 || height <= 0 {
33		return nil, fmt.Errorf("gpu: invalid image size %dx%d", width, height)
34	}
35	img = &Image{dev: d, Width: width, Height: height, FD: -1}
36	defer func() {
37		if err != nil {
38			img.Close()
39			img = nil
40		}
41	}()
42	modifier := uint64(mod)
43	list := vulkan.ImageDrmFormatModifierListCreateInfoEXT{SType: vulkan.StructureTypeImageDRMFormatModifierListCreateInfoEXT, DrmFormatModifierCount: 1, DrmFormatModifiers: &modifier}
44	ext := vulkan.ExternalMemoryImageCreateInfo{SType: vulkan.StructureTypeExternalMemoryImageCreateInfo, Next: unsafe.Pointer(&list), HandleTypes: vulkan.ExternalMemoryHandleTypeDMABUFBitEXT}
45	ci := vulkan.ImageCreateInfo{
46		SType: vulkan.StructureTypeImageCreateInfo, Next: unsafe.Pointer(&ext),
47		ImageType: vulkan.ImageType2d, Format: vulkan.FormatB8g8r8a8Unorm,
48		Extent:    vulkan.Extent3D{Width: uint32(width), Height: uint32(height), Depth: 1},
49		MipLevels: 1, ArrayLayers: 1, Samples: vulkan.SampleCount1Bit,
50		Tiling: vulkan.ImageTilingDRMFormatModifierEXT, Usage: vulkan.ImageUsageColorAttachmentBit,
51		SharingMode: vulkan.SharingModeExclusive, InitialLayout: vulkan.ImageLayoutUndefined,
52	}
53	if err = vulkan.Check(d.disp.CreateImage(d.logical, &ci, nil, &img.image)); err != nil {
54		return nil, fmt.Errorf("gpu: create image: %w", err)
55	}
56	props := vulkan.ImageDrmFormatModifierPropertiesEXT{SType: vulkan.StructureTypeImageDRMFormatModifierPropertiesEXT}
57	if err = vulkan.Check(d.disp.GetImageDrmFormatModifierPropertiesEXT(d.logical, img.image, &props)); err != nil {
58		return nil, err
59	}
60	if props.DrmFormatModifier != modifier {
61		return nil, fmt.Errorf("gpu: asked for modifier %#x, driver chose %#x", modifier, props.DrmFormatModifier)
62	}
63	img.Modifier = modifier
64	sub := vulkan.ImageSubresource{AspectMask: vulkan.ImageAspectMemoryPlane0BitEXT}
65	var layout vulkan.SubresourceLayout
66	d.disp.GetImageSubresourceLayout(d.logical, img.image, &sub, &layout)
67	if layout.RowPitch == 0 || layout.RowPitch > math.MaxUint32 || layout.Offset > math.MaxUint32 {
68		return nil, fmt.Errorf("gpu: unusable plane layout offset %d stride %d", layout.Offset, layout.RowPitch)
69	}
70	img.Offset, img.Stride = uint32(layout.Offset), uint32(layout.RowPitch)
71
72	reqInfo := vulkan.ImageMemoryRequirementsInfo2{SType: vulkan.StructureTypeImageMemoryRequirementsInfo2, Image: img.image}
73	reqs := vulkan.MemoryRequirements2{SType: vulkan.StructureTypeMemoryRequirements2}
74	d.disp.GetImageMemoryRequirements2(d.logical, &reqInfo, &reqs)
75	idx, err := d.memoryType(reqs.MemoryRequirements.MemoryTypeBits, vulkan.MemoryPropertyDeviceLocalBit)
76	if err != nil {
77		return nil, err
78	}
79	ded := vulkan.MemoryDedicatedAllocateInfo{SType: vulkan.StructureTypeMemoryDedicatedAllocateInfo, Image: img.image}
80	exp := vulkan.ExportMemoryAllocateInfo{SType: vulkan.StructureTypeExportMemoryAllocateInfo, Next: unsafe.Pointer(&ded), HandleTypes: vulkan.ExternalMemoryHandleTypeDMABUFBitEXT}
81	ai := vulkan.MemoryAllocateInfo{SType: vulkan.StructureTypeMemoryAllocateInfo, Next: unsafe.Pointer(&exp), AllocationSize: reqs.MemoryRequirements.Size, MemoryTypeIndex: idx}
82	if err = vulkan.Check(d.disp.AllocateMemory(d.logical, &ai, nil, &img.memory)); err != nil {
83		return nil, fmt.Errorf("gpu: allocate image memory: %w", err)
84	}
85	bind := vulkan.BindImageMemoryInfo{SType: vulkan.StructureTypeBindImageMemoryInfo, Image: img.image, Memory: img.memory}
86	if err = vulkan.Check(d.disp.BindImageMemory2(d.logical, 1, &bind)); err != nil {
87		return nil, fmt.Errorf("gpu: bind image memory: %w", err)
88	}
89	fdi := vulkan.MemoryGetFdInfoKHR{SType: vulkan.StructureTypeMemoryGetFDInfoKHR, Memory: img.memory, HandleType: vulkan.ExternalMemoryHandleTypeDMABUFBitEXT}
90	fd := int32(-1)
91	if err = vulkan.Check(d.disp.GetMemoryFdKHR(d.logical, &fdi, &fd)); err != nil {
92		return nil, fmt.Errorf("gpu: export DMA-BUF: %w", err)
93	}
94	img.FD = int(fd)
95	vi := vulkan.ImageViewCreateInfo{
96		SType: vulkan.StructureTypeImageViewCreateInfo, Image: img.image, ViewType: vulkan.ImageViewType2d,
97		Format:           vulkan.FormatB8g8r8a8Unorm,
98		SubresourceRange: vulkan.ImageSubresourceRange{AspectMask: vulkan.ImageAspectColorBit, LevelCount: 1, LayerCount: 1},
99	}
100	if err = vulkan.Check(d.disp.CreateImageView(d.logical, &vi, nil, &img.view)); err != nil {
101		return nil, err
102	}
103	return img, nil
104}
105
106// Close frees the image. The GPU must be done with it; the compositor's import
107// holds its own kernel reference.
108func (i *Image) Close() {
109	if i == nil || i.dev == nil {
110		return
111	}
112	d := i.dev
113	if i.FD >= 0 {
114		_ = unix.Close(i.FD)
115		i.FD = -1
116	}
117	if i.view != 0 {
118		d.disp.DestroyImageView(d.logical, i.view, nil)
119		i.view = 0
120	}
121	if i.image != 0 {
122		d.disp.DestroyImage(d.logical, i.image, nil)
123		i.image = 0
124	}
125	if i.memory != 0 {
126		d.disp.FreeMemory(d.logical, i.memory, nil)
127		i.memory = 0
128	}
129}

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 5

Tags 4

↑↓ moveenter openesc close