test(vulkan): pin the sceneDelta-seeded partial redraw at zero allocations
TestRenderSteadyStateAllocations replicates Render's partial sequence for a new scene Seq with one of two windows moved, asserts the redraw is partial and that building and clipping its draws allocates nothing.
- commit
- 8967242492da9807176f169f767457566f09db25
- author
- brice · · 2026-10-04 20:34 UTC
- committer
- brice · · 2026-10-04 20:57 UTC
- parent
- 185b931
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
1 file · +40 -1
M internal/adapters/vulkan/scene_perf_test.go +40 -1
| @@ -12,7 +12,8 @@ import ( | |||
| 12 | 12 | // on a normal and on a rotated output (orient works in place), and that | |
| 13 | 13 | // building the draws of a scene with a moving window (a new Seq and window | |
| 14 | 14 | // slice every frame, as an animation publishes) allocates nothing. | |
| 15 | - | // TODO(A2): add the partial redraw seeded by sceneDelta once it is merged. | |
| 15 | + | // A new scene Seq with one of two windows moved is a partial redraw seeded by | |
| 16 | + | // sceneDelta and allocates nothing either. | |
| 16 | 17 | func TestRenderSteadyStateAllocations(t *testing.T) { | |
| 17 | 18 | for _, tc := range []struct { | |
| 18 | 19 | name string | |
| @@ -93,6 +94,44 @@ func TestRenderSteadyStateAllocations(t *testing.T) { | |||
| 93 | 94 | if allocs := testing.AllocsPerRun(50, partial); allocs != 0 { | |
| 94 | 95 | t.Errorf("partial redraw draws: %.1f allocs/frame, want 0", allocs) | |
| 95 | 96 | } | |
| 97 | + | // A new scene Seq with one window moved: the delta against the | |
| 98 | + | // scene the target holds seeds the partial redraw (A2). The | |
| 99 | + | // stand-in target holds the old scene and is never updated. | |
| 100 | + | half := tc.sceneW / 2 | |
| 101 | + | content2 := solidContent(t, 8, 8, color.RGBA{G: 255, A: 255}) | |
| 102 | + | content2.ID, content2.Surface, content2.Seq, content2.Version = 2, 2, 1, 1 | |
| 103 | + | dcontents := map[ports.WindowID]ports.SurfaceContent{1: content, 2: content2} | |
| 104 | + | before := ports.Scene{Seq: 200, Transform: tc.transform, Scale: 1, Windows: []ports.SceneWindow{ | |
| 105 | + | {ID: 1, Rect: ports.Rect{W: half, H: tc.sceneH}}, | |
| 106 | + | {ID: 2, Rect: ports.Rect{X: half, W: tc.sceneW - half, H: tc.sceneH}}, | |
| 107 | + | }} | |
| 108 | + | moved := before | |
| 109 | + | moved.Seq = 201 | |
| 110 | + | moved.Windows = []ports.SceneWindow{before.Windows[0], {ID: 2, Rect: ports.Rect{X: half + 1, W: tc.sceneW - half - 1, H: tc.sceneH}}} | |
| 111 | + | full := image.Rect(0, 0, r.width, r.height) | |
| 112 | + | held = &target{valid: true, sceneSeq: before.Seq, scene: before, windows: map[ports.WindowID]heldWindow{ | |
| 113 | + | 1: {seq: 1, rect: image.Rect(0, 0, half, tc.sceneH)}, | |
| 114 | + | 2: {seq: 1, rect: image.Rect(half, 0, tc.sceneW, tc.sceneH)}, | |
| 115 | + | }} | |
| 116 | + | seeded := func() { | |
| 117 | + | dmg := r.frameDamage(held, moved, bounds) | |
| 118 | + | ds := r.draws(moved, dcontents, dmg) | |
| 119 | + | dmg.finish() | |
| 120 | + | ds = append(ds, draw{}) | |
| 121 | + | copy(ds[1:], ds[:len(ds)-1]) | |
| 122 | + | ds[0] = r.fillDraw(dmg.area, parseColor(moved.Background)) | |
| 123 | + | ds = dmg.clip(ds) | |
| 124 | + | r.scratchDraws = ds | |
| 125 | + | r.orient(ds, moved.Transform) | |
| 126 | + | area, whole = dmg.area, dmg.all() | |
| 127 | + | } | |
| 128 | + | seeded() | |
| 129 | + | if whole || area.Empty() || area.Dx()*area.Dy() >= full.Dx()*full.Dy() { | |
| 130 | + | t.Fatalf("sceneDelta did not seed a partial redraw: all %v, area %v of %v", whole, area, bounds) | |
| 131 | + | } | |
| 132 | + | if allocs := testing.AllocsPerRun(50, seeded); allocs != 0 { | |
| 133 | + | t.Errorf("sceneDelta-seeded partial redraw draws: %.1f allocs/frame, want 0", allocs) | |
| 134 | + | } | |
| 96 | 135 | }) | |
| 97 | 136 | } | |
| 98 | 137 | } | |
| @@ -12,7 +12,8 @@ import ( | |||||
| 12 | // on a normal and on a rotated output (orient works in place), and that | 12 | // on a normal and on a rotated output (orient works in place), and that | ||
| 13 | // building the draws of a scene with a moving window (a new Seq and window | 13 | // building the draws of a scene with a moving window (a new Seq and window | ||
| 14 | // slice every frame, as an animation publishes) allocates nothing. | 14 | // slice every frame, as an animation publishes) allocates nothing. | ||
| 15 | - | // TODO(A2): add the partial redraw seeded by sceneDelta once it is merged. | 15 | + | // A new scene Seq with one of two windows moved is a partial redraw seeded by |
| 16 | + | // sceneDelta and allocates nothing either. | |||
| 16 | func TestRenderSteadyStateAllocations(t *testing.T) { | 17 | func TestRenderSteadyStateAllocations(t *testing.T) { | ||
| 17 | for _, tc := range []struct { | 18 | for _, tc := range []struct { | ||
| 18 | name string | 19 | name string | ||
| @@ -93,6 +94,44 @@ func TestRenderSteadyStateAllocations(t *testing.T) { | |||||
| 93 | if allocs := testing.AllocsPerRun(50, partial); allocs != 0 { | 94 | if allocs := testing.AllocsPerRun(50, partial); allocs != 0 { | ||
| 94 | t.Errorf("partial redraw draws: %.1f allocs/frame, want 0", allocs) | 95 | t.Errorf("partial redraw draws: %.1f allocs/frame, want 0", allocs) | ||
| 95 | } | 96 | } | ||
| 97 | + | // A new scene Seq with one window moved: the delta against the | |||
| 98 | + | // scene the target holds seeds the partial redraw (A2). The | |||
| 99 | + | // stand-in target holds the old scene and is never updated. | |||
| 100 | + | half := tc.sceneW / 2 | |||
| 101 | + | content2 := solidContent(t, 8, 8, color.RGBA{G: 255, A: 255}) | |||
| 102 | + | content2.ID, content2.Surface, content2.Seq, content2.Version = 2, 2, 1, 1 | |||
| 103 | + | dcontents := map[ports.WindowID]ports.SurfaceContent{1: content, 2: content2} | |||
| 104 | + | before := ports.Scene{Seq: 200, Transform: tc.transform, Scale: 1, Windows: []ports.SceneWindow{ | |||
| 105 | + | {ID: 1, Rect: ports.Rect{W: half, H: tc.sceneH}}, | |||
| 106 | + | {ID: 2, Rect: ports.Rect{X: half, W: tc.sceneW - half, H: tc.sceneH}}, | |||
| 107 | + | }} | |||
| 108 | + | moved := before | |||
| 109 | + | moved.Seq = 201 | |||
| 110 | + | moved.Windows = []ports.SceneWindow{before.Windows[0], {ID: 2, Rect: ports.Rect{X: half + 1, W: tc.sceneW - half - 1, H: tc.sceneH}}} | |||
| 111 | + | full := image.Rect(0, 0, r.width, r.height) | |||
| 112 | + | held = &target{valid: true, sceneSeq: before.Seq, scene: before, windows: map[ports.WindowID]heldWindow{ | |||
| 113 | + | 1: {seq: 1, rect: image.Rect(0, 0, half, tc.sceneH)}, | |||
| 114 | + | 2: {seq: 1, rect: image.Rect(half, 0, tc.sceneW, tc.sceneH)}, | |||
| 115 | + | }} | |||
| 116 | + | seeded := func() { | |||
| 117 | + | dmg := r.frameDamage(held, moved, bounds) | |||
| 118 | + | ds := r.draws(moved, dcontents, dmg) | |||
| 119 | + | dmg.finish() | |||
| 120 | + | ds = append(ds, draw{}) | |||
| 121 | + | copy(ds[1:], ds[:len(ds)-1]) | |||
| 122 | + | ds[0] = r.fillDraw(dmg.area, parseColor(moved.Background)) | |||
| 123 | + | ds = dmg.clip(ds) | |||
| 124 | + | r.scratchDraws = ds | |||
| 125 | + | r.orient(ds, moved.Transform) | |||
| 126 | + | area, whole = dmg.area, dmg.all() | |||
| 127 | + | } | |||
| 128 | + | seeded() | |||
| 129 | + | if whole || area.Empty() || area.Dx()*area.Dy() >= full.Dx()*full.Dy() { | |||
| 130 | + | t.Fatalf("sceneDelta did not seed a partial redraw: all %v, area %v of %v", whole, area, bounds) | |||
| 131 | + | } | |||
| 132 | + | if allocs := testing.AllocsPerRun(50, seeded); allocs != 0 { | |||
| 133 | + | t.Errorf("sceneDelta-seeded partial redraw draws: %.1f allocs/frame, want 0", allocs) | |||
| 134 | + | } | |||
| 96 | }) | 135 | }) | ||
| 97 | } | 136 | } | ||
| 98 | } | 137 | } | ||