v0.1.0 d9218d6

damage_test.go RawHistoryFolder

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

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 26

Tags 10

↑↓ moveenter openesc close