Skip to content

Commit

Permalink
[web] retain GL/Gr context on window resize (flutter#38576)
Browse files Browse the repository at this point in the history
* [web] dont dispose of context on window resize

* ++

* ++

* ++

* ++

* ++

* ++

* always re-create surface

* ++

* ++
  • Loading branch information
jonahwilliams authored Jan 11, 2023
1 parent ae9e181 commit b9a7234
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 35 deletions.
66 changes: 36 additions & 30 deletions lib/web_ui/lib/src/engine/canvaskit/surface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ import 'util.dart';

// Only supported in profile/release mode. Allows Flutter to use MSAA but
// removes the ability for disabling AA on Paint objects.
const bool _kUsingMSAA =
bool.fromEnvironment('flutter.canvaskit.msaa');
const bool _kUsingMSAA = bool.fromEnvironment('flutter.canvaskit.msaa');

typedef SubmitCallback = bool Function(SurfaceFrame, CkCanvas);

Expand Down Expand Up @@ -146,53 +145,60 @@ class Surface {
throw CanvasKitError('Cannot create surfaces of empty size.');
}

// Check if the window is the same size as before, and if so, don't allocate
// a new canvas as the previous canvas is big enough to fit everything.
final ui.Size? previousSurfaceSize = _currentSurfaceSize;
if (!_forceNewContext &&
previousSurfaceSize != null &&
size.width == previousSurfaceSize.width &&
size.height == previousSurfaceSize.height) {
// The existing surface is still reusable.
if (window.devicePixelRatio != _currentDevicePixelRatio) {
_updateLogicalHtmlCanvasSize();
_translateCanvas();
if (!_forceNewContext) {
// Check if the window is the same size as before, and if so, don't allocate
// a new canvas as the previous canvas is big enough to fit everything.
final ui.Size? previousSurfaceSize = _currentSurfaceSize;
if (previousSurfaceSize != null &&
size.width == previousSurfaceSize.width &&
size.height == previousSurfaceSize.height) {
// The existing surface is still reusable.
if (window.devicePixelRatio != _currentDevicePixelRatio) {
_updateLogicalHtmlCanvasSize();
_translateCanvas();
}
return _surface!;
}
return _surface!;
}

// If the current canvas size is smaller than the requested size then create
// a new, larger, canvas. Then update the GR context so we can create a new
// SkSurface.
final ui.Size? previousCanvasSize = _currentCanvasPhysicalSize;
if (_forceNewContext ||
previousCanvasSize == null ||
size.width > previousCanvasSize.width ||
size.height > previousCanvasSize.height) {
final ui.Size? previousCanvasSize = _currentCanvasPhysicalSize;
// Initialize a new, larger, canvas. If the size is growing, then make the
// new canvas larger than required to avoid many canvas creations.
final ui.Size newSize = previousCanvasSize == null ? size : size * 1.4;
if (previousCanvasSize != null &&
(size.width > previousCanvasSize.width ||
size.height > previousCanvasSize.height)) {
final ui.Size newSize = size * 1.4;
_surface?.dispose();
_surface = null;
htmlCanvas!.width = newSize.width;
htmlCanvas!.height = newSize.height;
_currentCanvasPhysicalSize = newSize;
_pixelWidth = newSize.width.ceil();
_pixelHeight = newSize.height.ceil();
_updateLogicalHtmlCanvasSize();
}
}

// If we have a surface, send a dummy command to its canvas to make its context
// current or else disposing the context could fail below.
_surface?.getCanvas().clear(const ui.Color(0x00000000));
// Either a new context is being forced or we've never had one.
if (_forceNewContext || _currentCanvasPhysicalSize == null) {
_surface?.dispose();
_surface = null;
_addedToScene = false;
_grContext?.releaseResourcesAndAbandonContext();
_grContext?.delete();
_grContext = null;

_createNewCanvas(newSize);
_currentCanvasPhysicalSize = newSize;
_createNewCanvas(size);
_currentCanvasPhysicalSize = size;
} else if (window.devicePixelRatio != _currentDevicePixelRatio) {
_updateLogicalHtmlCanvasSize();
}

_currentDevicePixelRatio = window.devicePixelRatio;
_currentSurfaceSize = size;
_translateCanvas();
return _surface = _createNewSurface(size);
_surface?.dispose();
_surface = _createNewSurface(size);
return _surface!;
}

/// Sets the CSS size of the canvas so that canvas pixels are 1:1 with device
Expand Down
11 changes: 6 additions & 5 deletions lib/web_ui/test/canvaskit/surface_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,24 +37,25 @@ void testMain() {
expect(originalSurface.width(), 9);
expect(originalSurface.height(), 19);

// Shrinking reuses the existing canvas but translates it so Skia renders into the visible area.
// Shrinking reuses the existing canvas but translates it so
// Skia renders into the visible area.
final CkSurface shrunkSurface =
surface.acquireFrame(const ui.Size(5, 15)).skiaSurface;
final DomCanvasElement shrunk = surface.htmlCanvas!;
expect(shrunk, same(original));
expect(shrunk.style.width, '9px');
expect(shrunk.style.height, '19px');
expect(shrunk.style.transform, _isTranslate(0, -4));
expect(shrunkSurface, isNot(same(original)));
expect(shrunkSurface, isNot(same(originalSurface)));
expect(shrunkSurface.width(), 5);
expect(shrunkSurface.height(), 15);

// The first increase will allocate a new canvas, but will overallocate
// The first increase will allocate a new surface, but will overallocate
// by 40% to accommodate future increases.
final CkSurface firstIncreaseSurface =
surface.acquireFrame(const ui.Size(10, 20)).skiaSurface;
final DomCanvasElement firstIncrease = surface.htmlCanvas!;
expect(firstIncrease, isNot(same(original)));
expect(firstIncrease, same(original));
expect(firstIncreaseSurface, isNot(same(shrunkSurface)));

// Expect overallocated dimensions
Expand All @@ -79,7 +80,7 @@ void testMain() {
// Increases beyond the 40% limit will cause a new allocation.
final CkSurface hugeSurface = surface.acquireFrame(const ui.Size(20, 40)).skiaSurface;
final DomCanvasElement huge = surface.htmlCanvas!;
expect(huge, isNot(same(secondIncrease)));
expect(huge, same(secondIncrease));
expect(hugeSurface, isNot(same(secondIncreaseSurface)));

// Also over-allocated
Expand Down

0 comments on commit b9a7234

Please sign in to comment.