feat/hints a471fa4

tiled_playback_test.go RawHistoryFolder

1package wayland
2
3import (
4	"testing"
5	"time"
6
7	"github.com/bnema/go-wayland-bindings/server/wayland"
8	"github.com/bnema/neferwl/internal/adapters/vulkan"
9	"github.com/bnema/neferwl/internal/ports"
10	"github.com/bnema/wlturbo"
11	"golang.org/x/sys/unix"
12)
13
14// callbackBudgetProxy observes real wl_surface.frame replies.
15type callbackBudgetProxy struct {
16	wlturbo.BaseProxy
17	done chan struct{}
18}
19
20func (p *callbackBudgetProxy) Dispatch(e *wlturbo.Event) {
21	if e.Opcode == uint16(wayland.CallbackEventDone) {
22		p.done <- struct{}{}
23	}
24}
25
26// TestHeadlessTiledSHMCallbackAndCopyBudget guards callback delivery and
27// the GPU copy budget across publications with many stable SHM children.
28func TestHeadlessTiledSHMCallbackAndCopyBudget(t *testing.T) {
29	s, events, _, contents, dir := contentServer(t)
30	c := protocolClient(t, s, dir)
31	win, root, _ := surfaceMapper(t, c, events)()
32	comp := bindProtocol(t, c, "wl_compositor")
33	subc := bindProtocol(t, c, "wl_subcompositor")
34	shm := bindProtocol(t, c, "wl_shm")
35	registerProtocol(t, c, shm)
36	fd, err := unix.MemfdCreate("tiled-playback", unix.MFD_CLOEXEC)
37	if err != nil {
38		t.Fatal(err)
39	}
40	defer unix.Close(fd)
41	const tiles = 64
42	const edge = 8
43	const stride = edge * 4
44	const size = (tiles + 1) * edge * stride
45	if err := unix.Ftruncate(fd, size); err != nil {
46		t.Fatal(err)
47	}
48	pool := c.AllocateID()
49	registerProtocol(t, c, pool)
50	if err := wireRequest(c, shm, uint16(wayland.ShmRequestCreatePool), []int{fd}, pool, int32(size)); err != nil {
51		t.Fatal(err)
52	}
53	children := make([]uint32, tiles+1)
54	buffers := make([]uint32, tiles+1)
55	for i := range children {
56		children[i] = c.AllocateID()
57		requestProtocol(t, c, comp, wayland.CompositorRequestCreateSurface, children[i])
58		registerProtocol(t, c, children[i])
59		sub := c.AllocateID()
60		requestProtocol(t, c, subc, wayland.SubcompositorRequestGetSubsurface, sub, children[i], root)
61		registerProtocol(t, c, sub)
62		if i < tiles {
63			requestProtocol(t, c, sub, wayland.SubsurfaceRequestSetPosition, int32(i%8*edge), int32(i/8*edge))
64		} else {
65			requestProtocol(t, c, sub, wayland.SubsurfaceRequestSetPosition, int32(80), int32(0))
66		}
67		buffers[i] = c.AllocateID()
68		registerProtocol(t, c, buffers[i])
69		requestProtocol(t, c, pool, wayland.ShmPoolRequestCreateBuffer, buffers[i], int32(i*edge*stride), int32(edge), int32(edge), int32(stride), uint32(0))
70		requestProtocol(t, c, children[i], wayland.SurfaceRequestAttach, buffers[i], int32(0), int32(0))
71		requestProtocol(t, c, children[i], wayland.SurfaceRequestCommit)
72	}
73	requestProtocol(t, c, root, wayland.SurfaceRequestCommit)
74	if err := c.Roundtrip(); err != nil {
75		t.Fatal(err)
76	}
77	wait := func() ports.SurfaceContent {
78		t.Helper()
79		deadline := time.After(5 * time.Second)
80		for {
81			select {
82			case got := <-contents:
83				if got.ID == win.ID && len(got.Children) == tiles+1 {
84					return got
85				}
86			case <-deadline:
87				t.Fatal("no tiled publication")
88			}
89		}
90	}
91	previous := wait()
92	r, err := vulkan.New(128, 128)
93	if err != nil {
94		t.Skipf("Vulkan unavailable: %v", err)
95	}
96	defer r.Close()
97	scene := ports.Scene{Windows: []ports.SceneWindow{{ID: win.ID, Rect: ports.Rect{W: 128, H: 128}}}}
98	render := func(content ports.SurfaceContent) {
99		t.Helper()
100		fence, err := r.Render(scene, map[ports.WindowID]ports.SurfaceContent{win.ID: content})
101		if fence != nil {
102			_ = fence.Close()
103		}
104		if err != nil {
105			t.Fatal(err)
106		}
107	}
108	render(previous)
109	copied := r.CopiedBytes()
110	for i := range 120 {
111		done := make(chan struct{}, 1)
112		cb := c.AllocateID()
113		p := &callbackBudgetProxy{done: done}
114		p.SetID(cb)
115		registerWireProxy(c, p)
116		requestProtocol(t, c, children[tiles], wayland.SurfaceRequestFrame, cb)
117		requestProtocol(t, c, children[tiles], wayland.SurfaceRequestAttach, buffers[tiles], int32(0), int32(0))
118		requestProtocol(t, c, children[tiles], wayland.SurfaceRequestCommit)
119		requestProtocol(t, c, root, wayland.SurfaceRequestCommit)
120		if err := c.Roundtrip(); err != nil {
121			t.Fatal(err)
122		}
123		deadline := time.After(3 * time.Second)
124		for {
125			select {
126			case <-done:
127				goto callbackDone
128			default:
129			}
130			result := make(chan error, 1)
131			go func() { result <- c.Dispatch() }()
132			select {
133			case err := <-result:
134				if err != nil {
135					t.Fatal(err)
136				}
137			case <-deadline:
138				_ = c.Close()
139				t.Fatalf("callback %d not delivered", i)
140			}
141		}
142	callbackDone:
143		next := wait()
144		render(next)
145		if delta := r.CopiedBytes() - copied; delta != edge*edge*4 {
146			t.Fatalf("frame %d copied %d bytes, want only the changing child's %d (root: id=%d version=%d size=%dx%d, previous root version=%d; first tile: id=%d version=%d previous=%d)", i, delta, edge*edge*4, next.Surface, next.Version, next.Width, next.Height, previous.Version, next.Children[0].Surface, next.Children[0].Version, previous.Children[0].Version)
147		}
148		copied = r.CopiedBytes()
149		for tile := range tiles {
150			if next.Children[tile].Version != previous.Children[tile].Version {
151				t.Fatalf("unchanged tile %d changed version", tile)
152			}
153		}
154		previous = next
155	}
156}

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