v0.2.0 13c6a90

damage_test.go RawHistoryFolder

1package vulkan
2
3import (
4	"bytes"
5	"fmt"
6	"image/color"
7	"os"
8	"testing"
9
10	"github.com/bnema/neferwl/internal/ports"
11)
12
13// damagedContent writes px into the content's pool (the client drawing
14// into the same buffer) and records the damage of the new Seq.
15func damagedContent(t *testing.T, f *os.File, base ports.SurfaceContent, seq uint64, px []byte, hist []ports.SeqDamage) ports.SurfaceContent {
16	t.Helper()
17	if _, err := f.WriteAt(px, 0); err != nil {
18		t.Fatal(err)
19	}
20	c := base
21	c.Seq, c.DamageHistory = seq, hist
22	return c
23}
24
25// Partial redraws are pixel-identical to full ones: with both targets
26// ages (A then B, then one render) and with shm double buffers
27// (render, skip, render).
28func TestRendererDamageMatchesFullRedraw(t *testing.T) {
29	for _, tr := range []ports.BufferTransform{0, 1, 6} {
30		t.Run(fmt.Sprintf("transform %d", tr), func(t *testing.T) { damageMatchesFullRedraw(t, tr) })
31	}
32}
33
34// damageMatchesFullRedraw checks partial frames of a window whose buffer
35// carries transform tr against full redraws.
36func damageMatchesFullRedraw(t *testing.T, tr ports.BufferTransform) {
37	const w, h = 32, 32
38	newR := func() *Renderer {
39		r, err := New(64, 48)
40		if err != nil {
41			t.Skipf("Vulkan unavailable: %v", err)
42		}
43		t.Cleanup(r.Close)
44		return r
45	}
46	damaged, full := newR(), newR()
47	bufsD, err := damaged.ExportTargets(2, nil)
48	if err != nil {
49		t.Skipf("no exportable targets: %v", err)
50	}
51	for _, b := range bufsD {
52		b.Planes[0].File.Close()
53	}
54	px := func(c color.RGBA, x0, y0, x1, y1 int, into []byte) []byte {
55		for y := y0; y < y1; y++ {
56			for x := x0; x < x1; x++ {
57				copy(into[(y*w+x)*4:], []byte{c.B, c.G, c.R, 255})
58			}
59		}
60		return into
61	}
62	pixels := px(color.RGBA{10, 20, 30, 255}, 0, 0, w, h, make([]byte, w*h*4))
63	base := shmContent(t, w, h, w*4, pixels)
64	base.ID, base.Transform = 1, tr
65	f := base.SHM.File
66	scene := ports.Scene{Seq: 5, Background: "#000000", Windows: []ports.SceneWindow{{ID: 1, Rect: ports.Rect{X: 4, Y: 4, W: w, H: h}}}}
67	hist := []ports.SeqDamage{{Seq: 1, Full: true}}
68	c := damagedContent(t, f, *base, 1, pixels, hist)
69	step := func(i int, c ports.SurfaceContent) {
70		t.Helper()
71		damaged.UseTarget(i % 2)
72		contents := map[ports.WindowID]ports.SurfaceContent{1: c}
73		if err := render(damaged, scene, contents); err != nil {
74			t.Fatal(err)
75		}
76		if err := render(full, ports.Scene{Background: scene.Background, Windows: scene.Windows}, contents); err != nil {
77			t.Fatal(err)
78		}
79		if a, b := readPixels(t, damaged), readPixels(t, full); !bytes.Equal(a.Pix, b.Pix) {
80			t.Fatalf("frame %d differs from a full redraw", i)
81		}
82	}
83	step(0, c)
84	step(1, c) // target 1 drawn whole once
85	before := damaged.redrawn
86	// A: a red square; B: a green one elsewhere. Target 0 holds Seq 1,
87	// so its next frame redraws the union of both.
88	for i, d := range []struct {
89		col  color.RGBA
90		rect ports.Rect
91	}{{color.RGBA{255, 0, 0, 255}, ports.Rect{X: 2, Y: 2, W: 4, H: 4}}, {color.RGBA{0, 255, 0, 255}, ports.Rect{X: 20, Y: 10, W: 3, H: 5}}} {
92		seq := uint64(2 + i)
93		px(d.col, d.rect.X, d.rect.Y, d.rect.X+d.rect.W, d.rect.Y+d.rect.H, pixels)
94		hist = append(hist, ports.SeqDamage{Seq: seq, Rects: []ports.Rect{d.rect}})
95		c = damagedContent(t, f, *base, seq, pixels, hist)
96	}
97	step(2, c)
98	if got := damaged.redrawn - before; tr == 0 && got >= 64*48 {
99		t.Fatalf("partial frame redrew %d pixels", got)
100	}
101	// Render, skip (no frame), render: target 1 is two contents behind.
102	px(color.RGBA{0, 0, 255, 255}, 0, 30, 2, 32, pixels)
103	hist = append(hist, ports.SeqDamage{Seq: 4, Rects: []ports.Rect{{Y: 30, W: 2, H: 2}}})
104	c = damagedContent(t, f, *base, 4, pixels, hist)
105	step(3, c)
106	step(4, c)
107}
108
109// A window left out of a frame (on an overlay plane) and drawn again in
110// the same scene is redrawn whole; one held but no longer drawn is
111// cleared.
112func TestRendererDamageWindowLeftOutAndBack(t *testing.T) {
113	newR := func() *Renderer {
114		r, err := New(64, 48)
115		if err != nil {
116			t.Skipf("Vulkan unavailable: %v", err)
117		}
118		t.Cleanup(r.Close)
119		return r
120	}
121	damaged, full := newR(), newR()
122	c := solidContent(t, 16, 16, color.RGBA{200, 10, 10, 255})
123	c.ID, c.Seq = 1, 1
124	contents := map[ports.WindowID]ports.SurfaceContent{1: c}
125	with := ports.Scene{Seq: 9, Background: "#000000", Windows: []ports.SceneWindow{{ID: 1, Rect: ports.Rect{X: 4, Y: 4, W: 16, H: 16}}}}
126	without := with
127	without.Windows = nil
128	check := func(s ports.Scene, what string) {
129		t.Helper()
130		if err := render(damaged, s, contents); err != nil {
131			t.Fatal(err)
132		}
133		plain := s
134		plain.Seq = 0
135		if err := render(full, plain, contents); err != nil {
136			t.Fatal(err)
137		}
138		if !bytes.Equal(readPixels(t, damaged).Pix, readPixels(t, full).Pix) {
139			t.Fatalf("%s differs from a full redraw", what)
140		}
141	}
142	check(without, "overlay frame")
143	check(with, "overlay refused, window composed")
144	check(without, "window on the overlay again")
145}

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