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

Use H5vcc CanvasKit implementation if it is detected. #31191

Merged
merged 8 commits into from
Feb 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ import '../profiler.dart';
/// Entrypoint into the CanvasKit API.
late CanvasKit canvasKit;

/// Whether to use a CanvasKit implementation provided by a JavaScript
/// `window.h5vcc.canvasKit` object.
///
/// Cobalt may use this object to expose a native implementation of the
/// CanvasKit bindings. If this exists, use it instead of using the normal
/// downloaded CanvasKit library.
final bool useH5vccCanvasKit = h5vcc != null;

/// Sets the [CanvasKit] object on `window` so we can use `@JS()` to bind to
/// static APIs.
///
Expand All @@ -39,6 +47,18 @@ external set windowFlutterCanvasKit(CanvasKit? value);
@JS('window.flutterCanvasKit')
external CanvasKit? get windowFlutterCanvasKit;

@JS('window.h5vcc')
external H5vcc? get h5vcc;

@JS('window.h5vcc')
external set debugH5vccSetter(H5vcc? value);

@JS()
@anonymous
abstract class H5vcc {
external CanvasKit? get canvasKit;
}

@JS()
@anonymous
class CanvasKit {
Expand Down Expand Up @@ -132,6 +152,15 @@ class CanvasKit {
Object src,
SkPartialImageInfo info,
);

/// Gets a Skia surface from Cobalt's h5vcc object.
///
/// This is only applicable when running on Cobalt and when using Cobalt's
/// h5vcc CanvasKit bindings.
///
/// On Cobalt, this is the only way to get a Skia surface. Other CanvasKit
/// Make...Surface methods are not supported.
external SkSurface getH5vccSkSurface();
}

@JS('window.CanvasKitInit')
Expand Down
7 changes: 7 additions & 0 deletions lib/web_ui/lib/src/engine/canvaskit/initialization.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import '../embedder.dart';
import '../safe_browser_api.dart';
import 'canvaskit_api.dart';
import 'fonts.dart';
import 'util.dart';

/// Whether to use CanvasKit as the rendering backend.
final bool useCanvasKit = FlutterConfiguration.flutterWebAutoDetect
Expand Down Expand Up @@ -47,6 +48,12 @@ String canvasKitWasmModuleUrl(String canvasKitBase, String file) =>
Future<void> initializeCanvasKit({String? canvasKitBase}) async {
if (windowFlutterCanvasKit != null) {
canvasKit = windowFlutterCanvasKit!;
} else if (useH5vccCanvasKit) {
if (h5vcc?.canvasKit == null) {
throw CanvasKitError('H5vcc CanvasKit implementation not found.');
}
canvasKit = h5vcc!.canvasKit!;
windowFlutterCanvasKit = canvasKit;
} else {
canvasKit = await downloadCanvasKit(canvasKitBase: canvasKitBase);
windowFlutterCanvasKit = canvasKit;
Expand Down
5 changes: 5 additions & 0 deletions lib/web_ui/lib/src/engine/canvaskit/surface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ class Surface {

/// Creates a <canvas> and SkSurface for the given [size].
CkSurface createOrUpdateSurface(ui.Size size) {
if (useH5vccCanvasKit) {
_surface ??= CkSurface(canvasKit.getH5vccSkSurface(), null);
return _surface!;
}

if (size.isEmpty) {
throw CanvasKitError('Cannot create surfaces of empty size.');
}
Expand Down
73 changes: 73 additions & 0 deletions lib/web_ui/test/canvaskit/h5vcc_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// 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.

import 'dart:html' as html;
import 'dart:js' as js;

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('H5vcc patched CanvasKit', () {
int getH5vccSkSurfaceCalledCount = 0;

setUpAll(() async {
// Set `window.h5vcc` to PatchedH5vcc which uses a downloaded CanvasKit.
final CanvasKit downloadedCanvasKit = await downloadCanvasKit();
debugH5vccSetter = PatchedH5vcc(downloadedCanvasKit);

// Monkey-patch the getH5vccSkSurface function of
// `window.h5vcc.canvasKit`.
js.context['h5vcc']['canvasKit']['getH5vccSkSurface'] = () {
getH5vccSkSurfaceCalledCount++;

// Returns a fake [SkSurface] object with a minimal implementation.
return js.JsObject.jsify(<String, dynamic>{
'dispose': () {}
});
};
});

setUpCanvasKitTest();

setUp(() {
getH5vccSkSurfaceCalledCount = 0;
});

test('sets useH5vccCanvasKit', () {
expect(useH5vccCanvasKit, true);
});

test('API includes patched getH5vccSkSurface', () {
expect(canvasKit.getH5vccSkSurface, isNotNull);
});

test('Surface acquireFrame uses getH5vccSkSurface', () {
final Surface surface = SurfaceFactory.instance.getSurface();
surface.acquireFrame(ui.Size.zero);
expect(getH5vccSkSurfaceCalledCount, 1);

// No <canvas> element should be created.
expect(
flutterViewEmbedder.glassPaneElement!.querySelectorAll<html.Element>('canvas'),
isEmpty,
);
});
}, testOn: 'chrome');
}

class PatchedH5vcc implements H5vcc {
@override
final CanvasKit canvasKit;

PatchedH5vcc(this.canvasKit);
}