1package vulkan
2
3import (
4 "image"
5 "math/rand"
6 "testing"
7
8 "github.com/bnema/neferwl/internal/ports"
9)
10
11// disjointRects keeps the union of the damage, clipped to the buffer, and
12// covers no pixel twice.
13func TestDisjointRectsCoverUnionOnce(t *testing.T) {
14 const w, h = 24, 16
15 rng := rand.New(rand.NewSource(1))
16 var scratch = disjointRects(nil, nil, w, h)
17 for range 500 {
18 rects := make([]ports.Rect, 1+rng.Intn(6))
19 var want [w * h]bool
20 for i := range rects {
21 rects[i] = ports.Rect{X: rng.Intn(w+8) - 4, Y: rng.Intn(h+8) - 4, W: rng.Intn(14), H: rng.Intn(10)}
22 for y := rects[i].Y; y < rects[i].Y+rects[i].H; y++ {
23 for x := rects[i].X; x < rects[i].X+rects[i].W; x++ {
24 if x >= 0 && x < w && y >= 0 && y < h {
25 want[y*w+x] = true
26 }
27 }
28 }
29 }
30 scratch = disjointRects(scratch[:0], rects, w, h)
31 var got [w * h]int
32 for _, r := range scratch {
33 if r.Empty() {
34 t.Fatalf("empty rect in %v for %v", scratch, rects)
35 }
36 for y := r.Min.Y; y < r.Max.Y; y++ {
37 for x := r.Min.X; x < r.Max.X; x++ {
38 got[y*w+x]++
39 }
40 }
41 }
42 for i := range got {
43 if (got[i] > 0) != want[i] || got[i] > 1 {
44 t.Fatalf("pixel %d covered %d times (want %v) for %v -> %v", i, got[i], want[i], rects, scratch)
45 }
46 }
47 }
48}
49
50// Client damage that would cut into too many pieces falls back to its
51// bounding box: the work stays bounded and every damaged pixel is copied.
52func TestDisjointRectsBoundsAdversarialDamage(t *testing.T) {
53 const w, h = 1024, 1024
54 rects := make([]ports.Rect, 128)
55 for i := range rects {
56 rects[i] = ports.Rect{X: i * 3, Y: i * 3, W: w - i*6, H: 4 + i}
57 }
58 got := disjointRects(nil, rects, w, h)
59 if len(got) != 1 {
60 t.Fatalf("%d pieces, want the bounding box", len(got))
61 }
62 for _, rc := range rects {
63 r := image.Rect(rc.X, rc.Y, rc.X+rc.W, rc.Y+rc.H)
64 if !r.In(got[0]) {
65 t.Fatalf("damage %v outside %v", r, got[0])
66 }
67 }
68}
69
70// With a reused scratch slice the helper allocates nothing.
71func TestDisjointRectsNoAllocs(t *testing.T) {
72 rects := []ports.Rect{{X: 0, Y: 0, W: 10, H: 10}, {X: 5, Y: 5, W: 10, H: 10}, {X: 2, Y: 8, W: 4, H: 4}}
73 scratch := disjointRects(nil, rects, 32, 32)
74 if n := testing.AllocsPerRun(100, func() { scratch = disjointRects(scratch[:0], rects, 32, 32) }); n != 0 {
75 t.Fatalf("%v allocs", n)
76 }
77}