Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 3cdb6de

Browse files
authored
[canvaskit] reuse canvases when window resizes (#22966)
1 parent 54aaac8 commit 3cdb6de

File tree

2 files changed

+74
-3
lines changed

2 files changed

+74
-3
lines changed

lib/web_ui/lib/src/engine/canvaskit/surface.dart

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,19 +96,31 @@ class Surface {
9696
throw CanvasKitError('Cannot create surfaces of empty size.');
9797
}
9898

99-
if (size == _currentSize) {
99+
// Check if the window is shrinking in size, and if so, don't allocate a
100+
// new canvas as the previous canvas is big enough to fit everything.
101+
final ui.Size? previousSize = _currentSize;
102+
if (previousSize != null &&
103+
size.width <= previousSize.width &&
104+
size.height <= previousSize.height) {
100105
// The existing surface is still reusable.
101106
return;
102107
}
103108

104-
_currentSize = size;
109+
_currentSize = _currentSize == null
110+
// First frame. Allocate a canvas of the exact size as the window. The
111+
// window is frequently never resized, particularly on mobile, so using
112+
// the exact size is most optimal.
113+
? size
114+
// The window is growing. Overallocate to prevent frequent reallocations.
115+
: size * 1.4;
116+
105117
_surface?.dispose();
106118
_surface = null;
107119
htmlElement?.remove();
108120
htmlElement = null;
109121
_addedToScene = false;
110122

111-
_surface = _wrapHtmlCanvas(size);
123+
_surface = _wrapHtmlCanvas(_currentSize!);
112124
}
113125

114126
CkSurface _wrapHtmlCanvas(ui.Size physicalSize) {
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// @dart = 2.12
6+
import 'package:test/bootstrap/browser.dart';
7+
import 'package:test/test.dart';
8+
import 'package:ui/src/engine.dart';
9+
import 'package:ui/ui.dart' as ui;
10+
11+
import 'common.dart';
12+
13+
void main() {
14+
internalBootstrapBrowserTest(() => testMain);
15+
}
16+
17+
void testMain() {
18+
group('CanvasKit', () {
19+
setUpCanvasKitTest();
20+
21+
test('Surface allocates canvases efficiently', () {
22+
final Surface surface = Surface(HtmlViewEmbedder());
23+
final CkSurface original = surface.acquireRenderSurface(ui.Size(9, 19));
24+
25+
// Expect exact requested dimensions.
26+
expect(original.width(), 9);
27+
expect(original.height(), 19);
28+
29+
// Shrinking reuses the existing surface straight-up.
30+
final CkSurface shrunk = surface.acquireRenderSurface(ui.Size(5, 15));
31+
expect(shrunk, same(original));
32+
33+
// The first increase will allocate a new surface, but will overallocate
34+
// by 40% to accommodate future increases.
35+
final CkSurface firstIncrease = surface.acquireRenderSurface(ui.Size(10, 20));
36+
expect(firstIncrease, isNot(same(original)));
37+
38+
// Expect overallocated dimensions
39+
expect(firstIncrease.width(), 14);
40+
expect(firstIncrease.height(), 28);
41+
42+
// Subsequent increases within 40% reuse the old surface.
43+
final CkSurface secondIncrease = surface.acquireRenderSurface(ui.Size(11, 22));
44+
expect(secondIncrease, same(firstIncrease));
45+
46+
// Increases beyond the 40% limit will cause a new allocation.
47+
final CkSurface huge = surface.acquireRenderSurface(ui.Size(20, 40));
48+
expect(huge, isNot(same(firstIncrease)));
49+
50+
// Also over-allocated
51+
expect(huge.width(), 28);
52+
expect(huge.height(), 56);
53+
54+
// Shrink again. Reuse the last allocated surface.
55+
final CkSurface shrunk2 = surface.acquireRenderSurface(ui.Size(5, 15));
56+
expect(shrunk2, same(huge));
57+
});
58+
}, skip: isIosSafari);
59+
}

0 commit comments

Comments
 (0)