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

[canvaskit] Add configuration for maximum canvases #51735

Merged
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
6 changes: 3 additions & 3 deletions lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import 'dart:math' as math;

import 'package:ui/ui.dart' as ui;

import '../../engine.dart' show PlatformViewManager, longestIncreasingSubsequence;
import '../../engine.dart' show PlatformViewManager, configuration, longestIncreasingSubsequence;
import '../display.dart';
import '../dom.dart';
import '../html/path_to_svg_clip.dart';
Expand Down Expand Up @@ -49,7 +49,7 @@ class HtmlViewEmbedder {

/// The maximum number of render canvases to create. Too many canvases can
/// cause a performance burden.
static const int maximumCanvases = 8;
static int get maximumCanvases => configuration.canvasKitMaximumSurfaces;

/// The views that need to be recomposited into the scene on the next frame.
final Set<int> _viewsToRecomposite = <int>{};
Expand Down Expand Up @@ -478,7 +478,7 @@ class HtmlViewEmbedder {
final List<RenderingEntity> modifiedEntities =
List<RenderingEntity>.from(rendering.entities);
bool sawLastCanvas = false;
for (int i = rendering.entities.length - 1; i > 0; i--) {
for (int i = rendering.entities.length - 1; i >= 0; i--) {
final RenderingEntity entity = modifiedEntities[i];
if (entity is RenderingRenderCanvas) {
if (!sawLastCanvas) {
Expand Down
17 changes: 17 additions & 0 deletions lib/web_ui/lib/src/engine/configuration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,18 @@ class FlutterConfiguration {
'FLUTTER_WEB_CANVASKIT_FORCE_CPU_ONLY',
);

/// The maximum number of canvases to use when rendering in CanvasKit.
///
/// Limits the amount of overlays that can be created.
int get canvasKitMaximumSurfaces {
final int maxSurfaces =
_configuration?.canvasKitMaximumSurfaces?.toInt() ?? 8;
if (maxSurfaces < 1) {
return 1;
}
return maxSurfaces;
}

/// Set this flag to `true` to cause the engine to visualize the semantics tree
/// on the screen for debugging.
///
Expand Down Expand Up @@ -361,6 +373,11 @@ extension JsFlutterConfigurationExtension on JsFlutterConfiguration {
external JSBoolean? get _canvasKitForceCpuOnly;
bool? get canvasKitForceCpuOnly => _canvasKitForceCpuOnly?.toDart;

@JS('canvasKitMaximumSurfaces')
external JSNumber? get _canvasKitMaximumSurfaces;
double? get canvasKitMaximumSurfaces =>
_canvasKitMaximumSurfaces?.toDartDouble;

@JS('debugShowSemanticsNodes')
external JSBoolean? get _debugShowSemanticsNodes;
bool? get debugShowSemanticsNodes => _debugShowSemanticsNodes?.toDart;
Expand Down
70 changes: 69 additions & 1 deletion lib/web_ui/test/canvaskit/embedded_views_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// found in the LICENSE file.

import 'dart:async';
import 'dart:js_interop';

import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
Expand Down Expand Up @@ -1119,7 +1120,7 @@ void testMain() {
]);
});

test('optimized overlays correctly with transforms and clips', () async {
test('optimizes overlays correctly with transforms and clips', () async {
ui_web.platformViewRegistry.registerViewFactory(
'test-view',
(int viewId) => createDomHTMLDivElement()..className = 'platform-view',
Expand Down Expand Up @@ -1152,6 +1153,73 @@ void testMain() {
_platformView,
]);
});

test('can customize amount of overlays', () async {
final CkPicture testPicture =
paintPicture(const ui.Rect.fromLTRB(0, 0, 10, 10), (CkCanvas canvas) {
canvas.drawCircle(const ui.Offset(5, 5), 5, CkPaint());
});

// Initialize all platform views to be used in the test.
final List<int> platformViewIds = <int>[];
for (int i = 0; i < 16; i++) {
ui_web.platformViewRegistry.registerViewFactory(
'test-platform-view',
(int viewId) => createDomHTMLDivElement()..id = 'view-$i',
);
await createPlatformView(i, 'test-platform-view');
platformViewIds.add(i);
}

Future<void> renderTestScene({required int viewCount}) async {
final LayerSceneBuilder sb = LayerSceneBuilder();
sb.pushOffset(0, 0);
for (int i = 0; i < viewCount; i++) {
sb.addPicture(ui.Offset.zero, testPicture);
sb.addPlatformView(i, width: 10, height: 10);
}
await renderScene(sb.build());
}

// Set maximum overlays to 4.
debugOverrideJsConfiguration(<String, Object?>{
'canvasKitMaximumSurfaces': 4,
}.jsify() as JsFlutterConfiguration?);

await renderTestScene(viewCount: 8);
_expectSceneMatches(<_EmbeddedViewMarker>[
_overlay,
_platformView,
_overlay,
_platformView,
_overlay,
_platformView,
_platformView,
_platformView,
_platformView,
_platformView,
_overlay,
_platformView,
]);

// Set maximum overlays to -1. Should default to 1.
debugOverrideJsConfiguration(<String, Object?>{
'canvasKitMaximumSurfaces': -1,
}.jsify() as JsFlutterConfiguration?);

await renderTestScene(viewCount: 8);
_expectSceneMatches(<_EmbeddedViewMarker>[
_platformView,
_platformView,
_platformView,
_platformView,
_platformView,
_platformView,
_platformView,
_overlay,
_platformView,
]);
});
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ void testMain() {
// A property under test, that we'll try to read later.
js_util.setProperty(config, 'nonce', 'some_nonce');
// A non-existing property to verify our js-interop doesn't crash.
js_util.setProperty(config, 'canvasKitMaximumSurfaces', 32.0);
js_util.setProperty(config, 'nonexistentProperty', 32.0);

// Remove window.flutterConfiguration (if it's there)
js_util.setProperty(domWindow, 'flutterConfiguration', null);
Expand Down
2 changes: 1 addition & 1 deletion lib/web_ui/test/engine/configuration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ void testMain() {
expect(() {
config.setUserConfiguration(
js_util.jsify(<String, Object?>{
'canvasKitMaximumSurfaces': 32.0,
'nonexistentProperty': 32.0,
}) as JsFlutterConfiguration);
}, returnsNormally);
});
Expand Down