detached fdd661a

gbm_targets_test.go RawHistoryFolder

1package app
2
3import (
4	"image/color"
5	"os"
6	"path/filepath"
7	"testing"
8
9	"github.com/bnema/neferwl/internal/adapters/drm/gbm"
10	"github.com/bnema/neferwl/internal/adapters/vulkan"
11	"github.com/bnema/neferwl/internal/ports"
12	"golang.org/x/sys/unix"
13)
14
15// fourccXRGB8888 is DRM_FORMAT_XRGB8888.
16const fourccXRGB8888 = 'X' | 'R'<<8 | '2'<<16 | '4'<<24
17
18// renderNodeFor opens the render node of the GPU the renderer runs on.
19func renderNodeFor(t *testing.T, dev uint64) *os.File {
20	t.Helper()
21	nodes, _ := filepath.Glob("/dev/dri/renderD*")
22	for _, n := range nodes {
23		var st unix.Stat_t
24		if unix.Stat(n, &st) != nil || st.Rdev != dev {
25			continue
26		}
27		f, err := os.OpenFile(n, os.O_RDWR|unix.O_CLOEXEC, 0)
28		if err != nil {
29			t.Skipf("cannot open %s: %v", n, err)
30		}
31		return f
32	}
33	t.Skipf("no render node for device %#x", dev)
34	return nil
35}
36
37// Images allocated by GBM are imported as the renderer's targets (the path
38// the DRM output takes) and frames drawn into them read back. Skips without
39// a GPU, libgbm or an openable render node.
40func TestGBMImagesAreRenderTargets(t *testing.T) {
41	const w, h = 128, 64
42	r, err := vulkan.New(w, h)
43	if err != nil {
44		t.Skipf("Vulkan unavailable: %v", err)
45	}
46	defer r.Close()
47	sup := r.DMABuf()
48	if sup.Device == 0 || len(sup.Formats) == 0 {
49		t.Skip("device does not import dmabufs")
50	}
51	mods := r.TargetModifiers(nil, false)
52	if len(mods) == 0 {
53		t.Skip("no importable target modifier")
54	}
55	t.Logf("renderer target modifiers: %#x", mods)
56	node := renderNodeFor(t, sup.Device)
57	defer node.Close()
58	dev, err := gbm.Open(int(node.Fd()))
59	if err != nil {
60		t.Skipf("GBM unavailable: %v", err)
61	}
62	defer dev.Close()
63
64	// A render node cannot always allocate scanout buffers.
65	var bufs [2]*gbm.Buffer
66	for _, usage := range []gbm.Usage{gbm.UseScanout | gbm.UseRendering, gbm.UseRendering} {
67		var cerr error
68		for i := range bufs {
69			if bufs[i], cerr = dev.Create(w, h, fourccXRGB8888, mods, usage); cerr != nil {
70				break
71			}
72		}
73		if cerr == nil {
74			t.Logf("GBM usage %#x", usage)
75			break
76		}
77		t.Logf("GBM usage %#x: %v", usage, cerr)
78		for i := range bufs {
79			bufs[i].Close()
80			bufs[i] = nil
81		}
82	}
83	if bufs[0] == nil || bufs[1] == nil {
84		t.Skip("GBM allocated no image with the renderer's modifiers")
85	}
86	defer bufs[0].Close()
87	defer bufs[1].Close()
88
89	var dmas []ports.DMABuf
90	for _, b := range bufs {
91		d, err := b.DMABuf()
92		if err != nil {
93			t.Fatal(err)
94		}
95		dmas = append(dmas, d)
96	}
97	defer func() {
98		for _, d := range dmas {
99			for _, p := range d.Planes {
100				p.File.Close()
101			}
102		}
103	}()
104	t.Logf("GBM picked modifier %#x, %d plane(s), stride %d", dmas[0].Modifier, len(dmas[0].Planes), dmas[0].Planes[0].Stride)
105	if err := r.ImportTargets(dmas); err != nil {
106		t.Fatalf("ImportTargets: %v", err)
107	}
108	t.Log("Vulkan import of the GBM images succeeded")
109
110	// Target 0: red background with a blue window; target 1: green.
111	scenes := []ports.Scene{
112		{Background: "#ff0000", Windows: []ports.SceneWindow{{ID: 1, Rect: ports.Rect{X: 32, Y: 16, W: 64, H: 32}}}},
113		{Background: "#00ff00"},
114	}
115	contents := map[ports.WindowID]ports.SurfaceContent{1: {ID: 1, Width: 1, Height: 1, LogicalW: 64, LogicalH: 32, Opaque: true, Solid: &ports.SolidColor{B: 1, A: 1}}}
116	type px struct {
117		x, y int
118		want color.RGBA
119	}
120	checks := [][]px{
121		{{4, 4, color.RGBA{255, 0, 0, 255}}, {w - 4, h - 4, color.RGBA{255, 0, 0, 255}}, {64, 32, color.RGBA{0, 0, 255, 255}}, {36, 20, color.RGBA{0, 0, 255, 255}}},
122		{{4, 4, color.RGBA{0, 255, 0, 255}}, {64, 32, color.RGBA{0, 255, 0, 255}}},
123	}
124	// Twice around, so each image is drawn into again after the other one.
125	for round := range 2 {
126		for i := range scenes {
127			r.UseTarget(i)
128			s := scenes[i]
129			s.Seq = uint64(round*2 + i + 1)
130			done, err := r.Render(s, contents)
131			if err != nil {
132				t.Fatalf("render %d: %v", i, err)
133			}
134			if done != nil {
135				done.Close()
136			}
137			img := r.Pixels()
138			if img == nil {
139				t.Fatalf("target %d: no pixels", i)
140			}
141			for _, c := range checks[i] {
142				if got := img.At(c.x, c.y); got != c.want {
143					t.Errorf("round %d target %d pixel (%d,%d) = %v, want %v", round, i, c.x, c.y, got, c.want)
144				}
145			}
146		}
147	}
148	if err := r.ImportTargets(nil); err != nil {
149		t.Fatal(err)
150	}
151}

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 23

Tags 10

↑↓ moveenter openesc close