chore(canvaskit): remove SurfaceFrame from Surface (#162825)

SurfaceFrame is no longer used. This is a cleanup in preparation for a
larger refactor of `Surface` to improve performance.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
Harry Terkelsen 2025-02-06 15:59:30 -08:00 committed by GitHub
parent a6344bf2b3
commit db7e82bdfb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 74 additions and 80 deletions

View File

@ -23,27 +23,6 @@ import 'util.dart';
// removes the ability for disabling AA on Paint objects.
const bool _kUsingMSAA = bool.fromEnvironment('flutter.canvaskit.msaa');
typedef SubmitCallback = bool Function(SurfaceFrame, CkCanvas);
/// A frame which contains a canvas to be drawn into.
class SurfaceFrame {
SurfaceFrame(this.skiaSurface, this.submitCallback) : _submitted = false;
final CkSurface skiaSurface;
final SubmitCallback submitCallback;
final bool _submitted;
/// Submit this frame to be drawn.
bool submit() {
if (_submitted) {
return false;
}
return submitCallback(this, skiaCanvas);
}
CkCanvas get skiaCanvas => skiaSurface.getCanvas();
}
/// A surface which can be drawn into by the compositor.
///
/// The underlying representation is a [CkSurface], which can be reused by
@ -55,6 +34,19 @@ class Surface extends DisplayCanvas {
CkSurface? _surface;
/// Returns the underlying CanvasKit Surface. Should only be used in tests.
CkSurface? debugGetCkSurface() {
bool assertsEnabled = false;
assert(() {
assertsEnabled = true;
return true;
}());
if (!assertsEnabled) {
throw StateError('debugGetCkSurface() can only be used in tests');
}
return _surface;
}
/// Whether or not to use an `OffscreenCanvas` to back this [Surface].
final bool useOffscreenCanvas;
@ -97,7 +89,17 @@ class Surface extends DisplayCanvas {
DomOffscreenCanvas? _offscreenCanvas;
/// Returns the underlying OffscreenCanvas. Should only be used in tests.
DomOffscreenCanvas? get debugOffscreenCanvas => _offscreenCanvas;
DomOffscreenCanvas? debugGetOffscreenCanvas() {
bool assertsEnabled = false;
assert(() {
assertsEnabled = true;
return true;
}());
if (!assertsEnabled) {
throw StateError('debugGetOffscreenCanvas() can only be used in tests');
}
return _offscreenCanvas;
}
/// The <canvas> backing this Surface in the case that OffscreenCanvas isn't
/// supported.
@ -172,20 +174,6 @@ class Surface extends DisplayCanvas {
}
}
/// Acquire a frame of the given [size] containing a drawable canvas.
///
/// The given [size] is in physical pixels.
SurfaceFrame acquireFrame(ui.Size size) {
final CkSurface surface = createOrUpdateSurface(BitmapSize.fromSize(size));
// ignore: prefer_function_declarations_over_variables
final SubmitCallback submitCallback = (SurfaceFrame surfaceFrame, CkCanvas canvas) {
return _presentSurface();
};
return SurfaceFrame(surface, submitCallback);
}
BitmapSize? _currentCanvasPhysicalSize;
/// Sets the CSS size of the canvas so that canvas pixels are 1:1 with device
@ -283,9 +271,9 @@ class Surface extends DisplayCanvas {
}
}
// If we reached here, then either we are forcing a new context, or
// the size of the surface has changed so we need to make a new one.
// If we reached here, then this is the first frame and we haven't made a
// surface yet, we are forcing a new context, or the size of the surface
// has changed and we need to make a new one.
_surface?.dispose();
_surface = null;
@ -489,11 +477,6 @@ class Surface extends DisplayCanvas {
}
}
bool _presentSurface() {
_surface!.flush();
return true;
}
@override
bool get isConnected => _canvasElement!.isConnected!;

View File

@ -25,8 +25,9 @@ void testMain() {
test('Surface allocates canvases efficiently', () {
final Surface surface = Surface();
final CkSurface originalSurface = surface.acquireFrame(const ui.Size(9, 19)).skiaSurface;
final DomOffscreenCanvas original = surface.debugOffscreenCanvas!;
surface.createOrUpdateSurface(const BitmapSize(9, 19));
final CkSurface originalSurface = surface.debugGetCkSurface()!;
final DomOffscreenCanvas original = surface.debugGetOffscreenCanvas()!;
// Expect exact requested dimensions.
expect(original.width, 9);
@ -36,8 +37,9 @@ void testMain() {
// Shrinking causes the surface to create a new canvas with the exact
// size requested.
final CkSurface shrunkSurface = surface.acquireFrame(const ui.Size(5, 15)).skiaSurface;
final DomOffscreenCanvas shrunk = surface.debugOffscreenCanvas!;
surface.createOrUpdateSurface(const BitmapSize(5, 15));
final CkSurface shrunkSurface = surface.debugGetCkSurface()!;
final DomOffscreenCanvas shrunk = surface.debugGetOffscreenCanvas()!;
expect(shrunk, same(original));
expect(shrunkSurface, isNot(same(originalSurface)));
expect(shrunkSurface.width(), 5);
@ -45,9 +47,9 @@ void testMain() {
// The first increase will allocate a new surface to exactly the
// requested size.
final CkSurface firstIncreaseSurface =
surface.acquireFrame(const ui.Size(10, 20)).skiaSurface;
final DomOffscreenCanvas firstIncrease = surface.debugOffscreenCanvas!;
surface.createOrUpdateSurface(const BitmapSize(10, 20));
final CkSurface firstIncreaseSurface = surface.debugGetCkSurface()!;
final DomOffscreenCanvas firstIncrease = surface.debugGetOffscreenCanvas()!;
expect(firstIncrease, same(original));
expect(firstIncreaseSurface, isNot(same(shrunkSurface)));
@ -58,17 +60,18 @@ void testMain() {
expect(firstIncreaseSurface.height(), 20);
// Subsequent increases within 40% will still allocate a new canvas.
final CkSurface secondIncreaseSurface =
surface.acquireFrame(const ui.Size(11, 22)).skiaSurface;
final DomOffscreenCanvas secondIncrease = surface.debugOffscreenCanvas!;
surface.createOrUpdateSurface(const BitmapSize(11, 22));
final CkSurface secondIncreaseSurface = surface.debugGetCkSurface()!;
final DomOffscreenCanvas secondIncrease = surface.debugGetOffscreenCanvas()!;
expect(secondIncrease, same(firstIncrease));
expect(secondIncreaseSurface, isNot(same(firstIncreaseSurface)));
expect(secondIncreaseSurface.width(), 11);
expect(secondIncreaseSurface.height(), 22);
// Increases beyond the 40% limit will cause a new allocation.
final CkSurface hugeSurface = surface.acquireFrame(const ui.Size(20, 40)).skiaSurface;
final DomOffscreenCanvas huge = surface.debugOffscreenCanvas!;
surface.createOrUpdateSurface(const BitmapSize(20, 40));
final CkSurface hugeSurface = surface.debugGetCkSurface()!;
final DomOffscreenCanvas huge = surface.debugGetOffscreenCanvas()!;
expect(huge, same(secondIncrease));
expect(hugeSurface, isNot(same(secondIncreaseSurface)));
@ -79,8 +82,9 @@ void testMain() {
expect(hugeSurface.height(), 40);
// Shrink again. Create a new surface.
final CkSurface shrunkSurface2 = surface.acquireFrame(const ui.Size(5, 15)).skiaSurface;
final DomOffscreenCanvas shrunk2 = surface.debugOffscreenCanvas!;
surface.createOrUpdateSurface(const BitmapSize(5, 15));
final CkSurface shrunkSurface2 = surface.debugGetCkSurface()!;
final DomOffscreenCanvas shrunk2 = surface.debugGetOffscreenCanvas()!;
expect(shrunk2, same(huge));
expect(shrunkSurface2, isNot(same(hugeSurface)));
expect(shrunkSurface2.width(), 5);
@ -89,12 +93,13 @@ void testMain() {
// Doubling the DPR should halve the CSS width, height, and translation of the canvas.
// This tests https://github.com/flutter/flutter/issues/77084
EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(2.0);
final CkSurface dpr2Surface2 = surface.acquireFrame(const ui.Size(5, 15)).skiaSurface;
final DomOffscreenCanvas dpr2Canvas = surface.debugOffscreenCanvas!;
surface.createOrUpdateSurface(const BitmapSize(5, 15));
final CkSurface dpr2Surface = surface.debugGetCkSurface()!;
final DomOffscreenCanvas dpr2Canvas = surface.debugGetOffscreenCanvas()!;
expect(dpr2Canvas, same(huge));
expect(dpr2Surface2, isNot(same(hugeSurface)));
expect(dpr2Surface2.width(), 5);
expect(dpr2Surface2.height(), 15);
expect(dpr2Surface, isNot(same(hugeSurface)));
expect(dpr2Surface.width(), 5);
expect(dpr2Surface.height(), 15);
// Skipping on Firefox for now since Firefox headless doesn't support WebGL
// This causes issues in the test since we create a Canvas-backed surface,
@ -195,17 +200,19 @@ void testMain() {
() async {
final Surface surface = Surface();
expect(surface.debugForceNewContext, isTrue);
final CkSurface before = surface.acquireFrame(const ui.Size(9, 19)).skiaSurface;
surface.createOrUpdateSurface(const BitmapSize(9, 19));
final CkSurface before = surface.debugGetCkSurface()!;
expect(surface.debugForceNewContext, isFalse);
// Pump a timer to flush any microtasks.
await Future<void>.delayed(Duration.zero);
final CkSurface afterAcquireFrame = surface.acquireFrame(const ui.Size(9, 19)).skiaSurface;
surface.createOrUpdateSurface(const BitmapSize(9, 19));
final CkSurface afterAcquireFrame = surface.debugGetCkSurface()!;
// Existing context is reused.
expect(afterAcquireFrame, same(before));
// Emulate WebGL context loss.
final DomOffscreenCanvas canvas = surface.debugOffscreenCanvas!;
final DomOffscreenCanvas canvas = surface.debugGetOffscreenCanvas()!;
final Object ctx = canvas.getContext('webgl2')!;
final Object loseContextExtension = js_util.callMethod(ctx, 'getExtension', <String>[
'WEBGL_lose_context',
@ -226,7 +233,8 @@ void testMain() {
await Future<void>.delayed(Duration.zero);
expect(surface.debugForceNewContext, isTrue);
final CkSurface afterContextLost = surface.acquireFrame(const ui.Size(9, 19)).skiaSurface;
surface.createOrUpdateSurface(const BitmapSize(9, 19));
final CkSurface afterContextLost = surface.debugGetCkSurface()!;
// A new context is created.
expect(afterContextLost, isNot(same(before)));
},
@ -239,39 +247,42 @@ void testMain() {
'updates canvas logical size when device-pixel ratio changes',
() {
final Surface surface = Surface();
final CkSurface original = surface.acquireFrame(const ui.Size(10, 16)).skiaSurface;
surface.createOrUpdateSurface(const BitmapSize(10, 16));
final CkSurface original = surface.debugGetCkSurface()!;
expect(original.width(), 10);
expect(original.height(), 16);
expect(surface.debugOffscreenCanvas!.width, 10);
expect(surface.debugOffscreenCanvas!.height, 16);
expect(surface.debugGetOffscreenCanvas()!.width, 10);
expect(surface.debugGetOffscreenCanvas()!.height, 16);
// Increase device-pixel ratio: this makes CSS pixels bigger, so we need
// fewer of them to cover the browser window.
EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(2.0);
final CkSurface highDpr = surface.acquireFrame(const ui.Size(10, 16)).skiaSurface;
surface.createOrUpdateSurface(const BitmapSize(10, 16));
final CkSurface highDpr = surface.debugGetCkSurface()!;
expect(highDpr.width(), 10);
expect(highDpr.height(), 16);
expect(surface.debugOffscreenCanvas!.width, 10);
expect(surface.debugOffscreenCanvas!.height, 16);
expect(surface.debugGetOffscreenCanvas()!.width, 10);
expect(surface.debugGetOffscreenCanvas()!.height, 16);
// Decrease device-pixel ratio: this makes CSS pixels smaller, so we need
// more of them to cover the browser window.
EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(0.5);
final CkSurface lowDpr = surface.acquireFrame(const ui.Size(10, 16)).skiaSurface;
surface.createOrUpdateSurface(const BitmapSize(10, 16));
final CkSurface lowDpr = surface.debugGetCkSurface()!;
expect(lowDpr.width(), 10);
expect(lowDpr.height(), 16);
expect(surface.debugOffscreenCanvas!.width, 10);
expect(surface.debugOffscreenCanvas!.height, 16);
expect(surface.debugGetOffscreenCanvas()!.width, 10);
expect(surface.debugGetOffscreenCanvas()!.height, 16);
// See https://github.com/flutter/flutter/issues/77084#issuecomment-1120151172
EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(2.0);
final CkSurface changeRatioAndSize =
surface.acquireFrame(const ui.Size(9.9, 15.9)).skiaSurface;
surface.createOrUpdateSurface(BitmapSize.fromSize(const ui.Size(9.9, 15.9)));
final CkSurface changeRatioAndSize = surface.debugGetCkSurface()!;
expect(changeRatioAndSize.width(), 10);
expect(changeRatioAndSize.height(), 16);
expect(surface.debugOffscreenCanvas!.width, 10);
expect(surface.debugOffscreenCanvas!.height, 16);
expect(surface.debugGetOffscreenCanvas()!.width, 10);
expect(surface.debugGetOffscreenCanvas()!.height, 16);
},
skip: !Surface.offscreenCanvasSupported,
);