Skip to content

Commit

Permalink
[canvaskit] reuse canvases when window resizes (flutter#22966)
Browse files Browse the repository at this point in the history
  • Loading branch information
yjbanov committed Dec 11, 2020
1 parent 54aaac8 commit 3cdb6de
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 3 deletions.
18 changes: 15 additions & 3 deletions lib/web_ui/lib/src/engine/canvaskit/surface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,19 +96,31 @@ class Surface {
throw CanvasKitError('Cannot create surfaces of empty size.');
}

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

_currentSize = size;
_currentSize = _currentSize == null
// First frame. Allocate a canvas of the exact size as the window. The
// window is frequently never resized, particularly on mobile, so using
// the exact size is most optimal.
? size
// The window is growing. Overallocate to prevent frequent reallocations.
: size * 1.4;

_surface?.dispose();
_surface = null;
htmlElement?.remove();
htmlElement = null;
_addedToScene = false;

_surface = _wrapHtmlCanvas(size);
_surface = _wrapHtmlCanvas(_currentSize!);
}

CkSurface _wrapHtmlCanvas(ui.Size physicalSize) {
Expand Down
59 changes: 59 additions & 0 deletions lib/web_ui/test/canvaskit/surface_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// @dart = 2.12
import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
import 'package:ui/src/engine.dart';
import 'package:ui/ui.dart' as ui;

import 'common.dart';

void main() {
internalBootstrapBrowserTest(() => testMain);
}

void testMain() {
group('CanvasKit', () {
setUpCanvasKitTest();

test('Surface allocates canvases efficiently', () {
final Surface surface = Surface(HtmlViewEmbedder());
final CkSurface original = surface.acquireRenderSurface(ui.Size(9, 19));

// Expect exact requested dimensions.
expect(original.width(), 9);
expect(original.height(), 19);

// Shrinking reuses the existing surface straight-up.
final CkSurface shrunk = surface.acquireRenderSurface(ui.Size(5, 15));
expect(shrunk, same(original));

// The first increase will allocate a new surface, but will overallocate
// by 40% to accommodate future increases.
final CkSurface firstIncrease = surface.acquireRenderSurface(ui.Size(10, 20));
expect(firstIncrease, isNot(same(original)));

// Expect overallocated dimensions
expect(firstIncrease.width(), 14);
expect(firstIncrease.height(), 28);

// Subsequent increases within 40% reuse the old surface.
final CkSurface secondIncrease = surface.acquireRenderSurface(ui.Size(11, 22));
expect(secondIncrease, same(firstIncrease));

// Increases beyond the 40% limit will cause a new allocation.
final CkSurface huge = surface.acquireRenderSurface(ui.Size(20, 40));
expect(huge, isNot(same(firstIncrease)));

// Also over-allocated
expect(huge.width(), 28);
expect(huge.height(), 56);

// Shrink again. Reuse the last allocated surface.
final CkSurface shrunk2 = surface.acquireRenderSurface(ui.Size(5, 15));
expect(shrunk2, same(huge));
});
}, skip: isIosSafari);
}

0 comments on commit 3cdb6de

Please sign in to comment.