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

Commit cba6c1e

Browse files
author
Harry Terkelsen
authored
Use cached CanvasKit instance if available (#26059)
* If CanvasKit has already been initialized, don't load it again. This could be useful for hot restart or for DartPad. * Don't redownload script if it is already loaded. * trailing whitespace * Touch up comments
1 parent 01ce6a1 commit cba6c1e

File tree

6 files changed

+87
-21
lines changed

6 files changed

+87
-21
lines changed

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,15 @@ late CanvasKit canvasKit;
1616
/// static APIs.
1717
///
1818
/// See, e.g. [SkPaint].
19+
///
20+
/// This also acts as a cache of an initialized CanvasKit instance. We can use
21+
/// this, for example, to perform a hot restart without needing to redownload
22+
/// and reinitialize CanvasKit.
23+
@JS('window.flutterCanvasKit')
24+
external set windowFlutterCanvasKit(CanvasKit? value);
25+
1926
@JS('window.flutterCanvasKit')
20-
external set windowFlutterCanvasKit(CanvasKit value);
27+
external CanvasKit? get windowFlutterCanvasKit;
2128

2229
@JS()
2330
@anonymous
@@ -141,7 +148,7 @@ class ColorSpace {}
141148
@anonymous
142149
class SkWebGLContextOptions {
143150
external factory SkWebGLContextOptions({
144-
required int anitalias,
151+
required int antialias,
145152
// WebGL version: 1 or 2.
146153
required int majorVersion,
147154
});

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

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -87,20 +87,23 @@ String canvasKitWasmModuleUrl(String file) => canvasKitBuildUrl + file;
8787
/// This calls `CanvasKitInit` and assigns the global [canvasKit] object.
8888
Future<void> initializeCanvasKit() {
8989
final Completer<void> canvasKitCompleter = Completer<void>();
90-
late StreamSubscription<html.Event> loadSubscription;
91-
loadSubscription = domRenderer.canvasKitScript!.onLoad.listen((_) {
92-
loadSubscription.cancel();
93-
final CanvasKitInitPromise canvasKitInitPromise =
94-
CanvasKitInit(CanvasKitInitOptions(
95-
locateFile: js.allowInterop(
96-
(String file, String unusedBase) => canvasKitWasmModuleUrl(file)),
97-
));
98-
canvasKitInitPromise.then(js.allowInterop((CanvasKit ck) {
99-
canvasKit = ck;
100-
windowFlutterCanvasKit = canvasKit;
101-
canvasKitCompleter.complete();
102-
}));
103-
});
90+
if (windowFlutterCanvasKit != null) {
91+
canvasKit = windowFlutterCanvasKit!;
92+
canvasKitCompleter.complete();
93+
} else {
94+
domRenderer.canvasKitLoaded!.then((_) {
95+
final CanvasKitInitPromise canvasKitInitPromise =
96+
CanvasKitInit(CanvasKitInitOptions(
97+
locateFile: js.allowInterop(
98+
(String file, String unusedBase) => canvasKitWasmModuleUrl(file)),
99+
));
100+
canvasKitInitPromise.then(js.allowInterop((CanvasKit ck) {
101+
canvasKit = ck;
102+
windowFlutterCanvasKit = canvasKit;
103+
canvasKitCompleter.complete();
104+
}));
105+
});
106+
}
104107

105108
/// Add a Skia scene host.
106109
skiaSceneHost = html.Element.tag('flt-scene');

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ class Surface {
210210
SkWebGLContextOptions(
211211
// Default to no anti-aliasing. Paint commands can be explicitly
212212
// anti-aliased by setting their `Paint` object's `antialias` property.
213-
anitalias: 0,
213+
antialias: 0,
214214
majorVersion: webGLVersion,
215215
),
216216
);

lib/web_ui/lib/src/engine/dom_renderer.dart

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ class DomRenderer {
4848
html.ScriptElement? get canvasKitScript => _canvasKitScript;
4949
html.ScriptElement? _canvasKitScript;
5050

51+
Future<void>? get canvasKitLoaded => _canvasKitLoaded;
52+
Future<void>? _canvasKitLoaded;
53+
5154
/// The element that contains the [sceneElement].
5255
///
5356
/// This element is created and inserted in the HTML DOM once. It is never
@@ -441,7 +444,8 @@ flt-glass-pane * {
441444

442445
_sceneHostElement = createElement('flt-scene-host');
443446

444-
final html.Element semanticsHostElement = createElement('flt-semantics-host');
447+
final html.Element semanticsHostElement =
448+
createElement('flt-semantics-host');
445449
semanticsHostElement.style
446450
..position = 'absolute'
447451
..transformOrigin = '0 0 0';
@@ -509,11 +513,21 @@ flt-glass-pane * {
509513
});
510514
}
511515

512-
if (useCanvasKit) {
516+
// Only reset CanvasKit if it's not already available.
517+
if (useCanvasKit && windowFlutterCanvasKit == null) {
513518
_canvasKitScript?.remove();
514519
_canvasKitScript = html.ScriptElement();
515520
_canvasKitScript!.src = canvasKitJavaScriptBindingsUrl;
516521

522+
Completer<void> canvasKitLoadCompleter = Completer<void>();
523+
_canvasKitLoaded = canvasKitLoadCompleter.future;
524+
525+
late StreamSubscription<html.Event> loadSubscription;
526+
loadSubscription = _canvasKitScript!.onLoad.listen((_) {
527+
loadSubscription.cancel();
528+
canvasKitLoadCompleter.complete(true);
529+
});
530+
517531
// TODO(hterkelsen): Rather than this monkey-patch hack, we should
518532
// build CanvasKit ourselves. See:
519533
// https://github.com/flutter/flutter/issues/52588
@@ -591,7 +605,8 @@ flt-glass-pane * {
591605
/// logical pixels. To compensate, we inject an inverse scale at the root
592606
/// level.
593607
void updateSemanticsScreenProperties() {
594-
_semanticsHostElement!.style.transform = 'scale(${1 / html.window.devicePixelRatio})';
608+
_semanticsHostElement!.style.transform =
609+
'scale(${1 / html.window.devicePixelRatio})';
595610
}
596611

597612
/// Called immediately after browser window metrics change.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
import 'package:test/bootstrap/browser.dart';
6+
import 'package:test/test.dart';
7+
import 'package:ui/src/engine.dart';
8+
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('initialize', () {
19+
test(
20+
're-uses the same initialized instance if it is already set on the window',
21+
() async {
22+
expect(windowFlutterCanvasKit, isNull);
23+
24+
DomRenderer();
25+
await ui.webOnlyInitializePlatform(
26+
assetManager: WebOnlyMockAssetManager());
27+
expect(windowFlutterCanvasKit, isNotNull);
28+
29+
var firstCanvasKitInstance = windowFlutterCanvasKit;
30+
31+
// Triggers a reset of the CanvasKit script element.
32+
DomRenderer();
33+
await ui.webOnlyInitializePlatform(
34+
assetManager: WebOnlyMockAssetManager());
35+
// The instance is the same.
36+
expect(firstCanvasKitInstance, windowFlutterCanvasKit);
37+
});
38+
// TODO: https://github.com/flutter/flutter/issues/60040
39+
}, skip: isIosSafari);
40+
}

lib/web_ui/test/canvaskit/initialization_test.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ void testMain() {
2020

2121
test('populates flt-renderer and flt-build-mode', () {
2222
DomRenderer();
23-
expect(html.document.body!.attributes['flt-renderer'], 'canvaskit (requested explicitly)');
23+
expect(html.document.body!.attributes['flt-renderer'],
24+
'canvaskit (requested explicitly)');
2425
expect(html.document.body!.attributes['flt-build-mode'], 'debug');
2526
});
2627
// TODO: https://github.com/flutter/flutter/issues/60040

0 commit comments

Comments
 (0)