1package wayland
2
3import (
4 "context"
5 "path/filepath"
6 "testing"
7 "time"
8
9 "github.com/bnema/go-wayland-bindings/server/fifo"
10 "github.com/bnema/go-wayland-bindings/server/viewporter"
11 "github.com/bnema/go-wayland-bindings/server/wayland"
12 "github.com/bnema/neferwl/internal/logging"
13 "github.com/bnema/wlturbo"
14 "golang.org/x/sys/unix"
15)
16
17// BenchmarkProtocolQueuedCommit measures a real client's requests and server
18// dispatch, queue capture, and paced application. Socket roundtrips synchronize
19// each frame; this is end-to-end latency, not an allocation-free apply microbench.
20func BenchmarkProtocolQueuedCommit(b *testing.B) {
21 dir := b.TempDir()
22 s, err := New(Options{RuntimeDir: dir, Outputs: testOutputs}, Channels{}, logging.For(context.Background(), "wayland"))
23 if err != nil {
24 b.Fatal(err)
25 }
26 ctx, cancel := context.WithCancel(context.Background())
27 done := make(chan error, 1)
28 go func() { done <- s.Run(ctx) }()
29 defer func() { cancel(); <-done }()
30 c, err := wlturbo.Connect(filepath.Join(dir, s.SocketName()))
31 if err != nil {
32 b.Fatal(err)
33 }
34 defer func() {
35 _ = c.Close()
36 forgetWireClient(c)
37 }()
38 if err := c.Roundtrip(); err != nil {
39 b.Fatal(err)
40 }
41 bind := func(name string) uint32 {
42 g, ok := c.Registry().FindGlobal(name)
43 if !ok {
44 b.Fatalf("missing %s", name)
45 }
46 id, err := bindWireID(c, g.Name, g.Interface, 1)
47 if err != nil {
48 b.Fatal(err)
49 }
50 return id
51 }
52 send := func(id, op uint32, args ...any) {
53 if err := wireRequest(c, id, uint16(op), nil, args...); err != nil {
54 b.Fatal(err)
55 }
56 }
57 comp, fm, vpm := bind("wl_compositor"), bind("wp_fifo_manager_v1"), bind("wp_viewporter")
58 shm := bind("wl_shm")
59 register := func(id uint32) {
60 p := &protocolProxy{}
61 p.SetID(id)
62 registerWireProxy(c, p)
63 }
64 register(shm)
65 surf := c.AllocateID()
66 send(comp, wayland.CompositorRequestCreateSurface, surf)
67 register(surf)
68 f := c.AllocateID()
69 send(fm, fifo.WpFifoManagerV1RequestGetFifo, f, surf)
70 register(f)
71 vp := c.AllocateID()
72 send(vpm, viewporter.WpViewporterRequestGetViewport, vp, surf)
73 register(vp)
74 // One real SHM buffer, reused for every commit.
75 fd, err := unix.MemfdCreate("bench-buffer", 0)
76 if err != nil {
77 b.Fatal(err)
78 }
79 defer unix.Close(fd)
80 if err := unix.Ftruncate(fd, 4); err != nil {
81 b.Fatal(err)
82 }
83 pool, buf := c.AllocateID(), c.AllocateID()
84 register(pool)
85 register(buf)
86 if err := wireRequest(c, shm, uint16(wayland.ShmRequestCreatePool), []int{fd}, pool, int32(4)); err != nil {
87 b.Fatal(err)
88 }
89 send(pool, wayland.ShmPoolRequestCreateBuffer, buf, int32(0), int32(1), int32(1), int32(4), uint32(0))
90 if err := c.Roundtrip(); err != nil {
91 b.Fatal(err)
92 }
93 // Install a barrier so each measured commit really waits in the queue.
94 send(f, fifo.WpFifoV1RequestSetBarrier)
95 send(surf, wayland.SurfaceRequestCommit)
96 if err := c.Roundtrip(); err != nil {
97 b.Fatal(err)
98 }
99 b.ReportAllocs()
100 b.ResetTimer()
101 for range b.N {
102 send(f, fifo.WpFifoV1RequestWaitBarrier)
103 send(surf, wayland.SurfaceRequestAttach, buf, int32(0), int32(0))
104 send(surf, wayland.SurfaceRequestDamage, int32(0), int32(0), int32(1), int32(1))
105 send(vp, viewporter.WpViewportRequestSetDestination, int32(1), int32(1))
106 send(surf, wayland.SurfaceRequestCommit)
107 // Later requests must not be consumed by the captured update.
108 send(vp, viewporter.WpViewportRequestSetDestination, int32(2), int32(2))
109 if err := c.Roundtrip(); err != nil {
110 b.Fatal(err)
111 }
112 s.display.Do(func() {
113 for _, state := range s.surfaces {
114 if state.wl != nil && state.wl.ID() == surf {
115 if len(state.queue) != 1 {
116 b.Errorf("queued commits: %d", len(state.queue))
117 return
118 }
119 state.barrier = false
120 s.tickFifo(time.Now(), nil)
121 if len(state.queue) != 0 {
122 b.Error("commit still queued")
123 }
124 return
125 }
126 }
127 b.Error("missing surface")
128 })
129 // Next iteration overwrites the pending viewport destination.
130 send(f, fifo.WpFifoV1RequestSetBarrier)
131 send(surf, wayland.SurfaceRequestCommit)
132 if err := c.Roundtrip(); err != nil {
133 b.Fatal(err)
134 }
135 }
136}