Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 2 additions & 2 deletions lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart
Original file line number Diff line number Diff line change
Expand Up @@ -657,8 +657,8 @@ class HtmlViewEmbedder {
element.remove();
}

/// Clears the state of this view embedder. Used in tests.
void debugClear() {
/// Disposes the state of this view embedder.
void dispose() {
final Set<int> allViews = PlatformViewManager.instance.debugClear();
disposeViews(allViews);
_context = EmbedderFrameContext();
Expand Down
10 changes: 8 additions & 2 deletions lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ class Rasterizer {
final CkPictureRecorder pictureRecorder = CkPictureRecorder();
pictureRecorder.beginRecording(ui.Offset.zero & _currentFrameSize);
pictureRecorder.recordingCanvas!.clear(const ui.Color(0x00000000));
final Frame compositorFrame = context.acquireFrame(
pictureRecorder.recordingCanvas!, viewEmbedder);
final Frame compositorFrame =
context.acquireFrame(pictureRecorder.recordingCanvas!, viewEmbedder);

compositorFrame.raster(layerTree, ignoreRasterCache: true);

Expand All @@ -54,4 +54,10 @@ class Rasterizer {

viewEmbedder.submitFrame();
}

/// Disposes of this rasterizer.
void dispose() {
viewEmbedder.dispose();
renderCanvasFactory.dispose();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import '../../engine.dart';
class RenderCanvasFactory {
RenderCanvasFactory() {
assert(() {
registerHotRestartListener(debugClear);
registerHotRestartListener(dispose);
return true;
}());
}
Expand Down Expand Up @@ -102,8 +102,8 @@ class RenderCanvasFactory {
return false;
}

/// Dispose all canvases created by this factory. Used in tests.
void debugClear() {
/// Dispose all canvases created by this factory.
void dispose() {
for (final RenderCanvas canvas in _cache) {
canvas.dispose();
}
Expand Down
64 changes: 45 additions & 19 deletions lib/web_ui/lib/src/engine/canvaskit/renderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ class CanvasKitRenderer implements Renderer {
/// is supported.
final Surface pictureToImageSurface = Surface();

// Listens for view creation events from the view manager.
StreamSubscription<int>? _onViewCreatedListener;
// Listens for view disposal events from the view manager.
StreamSubscription<int>? _onViewDisposedListener;

@override
Future<void> initialize() async {
_initialized ??= () async {
Expand All @@ -64,6 +69,20 @@ class CanvasKitRenderer implements Renderer {
canvasKit = await downloadCanvasKit();
windowFlutterCanvasKit = canvasKit;
}
// Views may have been registered before this renderer was initialized.
// Create rasterizers for them and then start listening for new view
// creation/disposal events.
final FlutterViewManager viewManager =
EnginePlatformDispatcher.instance.viewManager;
if (_onViewCreatedListener == null) {
for (final EngineFlutterView view in viewManager.views) {
_onViewCreated(view.viewId);
}
}
_onViewCreatedListener ??=
viewManager.onViewCreated.listen(_onViewCreated);
_onViewDisposedListener ??=
viewManager.onViewDisposed.listen(_onViewDisposed);
_instance = this;
}();
return _initialized;
Expand Down Expand Up @@ -394,37 +413,44 @@ class CanvasKitRenderer implements Renderer {
frameTimingsOnBuildFinish();
frameTimingsOnRasterStart();

// TODO(harryterkelsen): Use `FlutterViewManager.onViewsChanged` to manage
// the lifecycle of Rasterizers,
// https://github.com/flutter/flutter/issues/137073.
final Rasterizer rasterizer =
_getRasterizerForView(view as EngineFlutterView);
assert(_rasterizers.containsKey(view.viewId),
"Unable to render to a view which hasn't been registered");
final Rasterizer rasterizer = _rasterizers[view.viewId]!;

rasterizer.draw((scene as LayerScene).layerTree);
frameTimingsOnRasterFinish();
}

final Map<EngineFlutterView, Rasterizer> _rasterizers =
<EngineFlutterView, Rasterizer>{};
// Map from view id to the associated Rasterizer for that view.
final Map<int, Rasterizer> _rasterizers = <int, Rasterizer>{};

Rasterizer _getRasterizerForView(EngineFlutterView view) {
return _rasterizers.putIfAbsent(view, () {
return Rasterizer(view);
});
void _onViewCreated(int viewId) {
final EngineFlutterView view =
EnginePlatformDispatcher.instance.viewManager[viewId]!;
_rasterizers[view.viewId] = Rasterizer(view);
}

void _onViewDisposed(int viewId) {
// The view has already been disposed.
if (!_rasterizers.containsKey(viewId)) {
return;
}
final Rasterizer rasterizer = _rasterizers.remove(viewId)!;
rasterizer.dispose();
}

/// Returns the [Rasterizer] that has been created for the given [view].
/// Used in tests.
Rasterizer debugGetRasterizerForView(EngineFlutterView view) {
return _getRasterizerForView(view);
Rasterizer? debugGetRasterizerForView(EngineFlutterView view) {
return _rasterizers[view.viewId];
}

/// Resets the state of the renderer. Used in tests.
void debugClear() {
/// Disposes this renderer.
void dispose() {
_onViewCreatedListener?.cancel();
_onViewDisposedListener?.cancel();
for (final Rasterizer rasterizer in _rasterizers.values) {
rasterizer.renderCanvasFactory.debugClear();
rasterizer.viewEmbedder.debugClear();
rasterizer.dispose();
}
_rasterizers.clear();
}

@override
Expand Down
28 changes: 18 additions & 10 deletions lib/web_ui/lib/src/engine/view_embedder/flutter_view_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,18 @@ class FlutterViewManager {
// A map of (optional) JsFlutterViewOptions, indexed by their viewId.
final Map<int, JsFlutterViewOptions> _jsViewOptions =
<int, JsFlutterViewOptions>{};
// The controller of the [onViewsChanged] stream.
final StreamController<void> _onViewsChangedController =
StreamController<void>.broadcast();
// The controller of the [onViewCreated] stream.
final StreamController<int> _onViewCreatedController =
StreamController<int>.broadcast(sync: true);
// The controller of the [onViewDisposed] stream.
final StreamController<int> _onViewDisposedController =
StreamController<int>.broadcast(sync: true);

/// A stream of `void` events that will fire when a view is registered/unregistered.
Stream<void> get onViewsChanged => _onViewsChangedController.stream;
/// A stream of viewIds that will fire when a view is created.
Stream<int> get onViewCreated => _onViewCreatedController.stream;

/// A stream of viewIds that will fire when a view is disposed.
Stream<int> get onViewDisposed => _onViewDisposedController.stream;
Comment thread
harryterkelsen marked this conversation as resolved.

/// Exposes all the [EngineFlutterView]s registered so far.
Iterable<EngineFlutterView> get views => _viewData.values;
Expand All @@ -33,7 +39,8 @@ class FlutterViewManager {
EngineFlutterView createAndRegisterView(
JsFlutterViewOptions jsViewOptions,
) {
final EngineFlutterView view = EngineFlutterView(_dispatcher, jsViewOptions.hostElement);
final EngineFlutterView view =
EngineFlutterView(_dispatcher, jsViewOptions.hostElement);
registerView(view, jsViewOptions: jsViewOptions);
return view;
}
Expand All @@ -53,7 +60,7 @@ class FlutterViewManager {
if (jsViewOptions != null) {
_jsViewOptions[viewId] = jsViewOptions;
}
_onViewsChangedController.add(null);
_onViewCreatedController.add(viewId);

return view;
}
Expand All @@ -74,7 +81,7 @@ class FlutterViewManager {
JsFlutterViewOptions? unregisterView(int viewId) {
_viewData.remove(viewId); // .dispose();
final JsFlutterViewOptions? jsViewOptions = _jsViewOptions.remove(viewId);
_onViewsChangedController.add(null);
_onViewDisposedController.add(viewId);
return jsViewOptions;
}

Expand All @@ -91,7 +98,8 @@ class FlutterViewManager {
// inside the loop.
_viewData.keys.toList().forEach(disposeAndUnregisterView);
// Let listeners receive the unregistration events from the loop above, then
// close the stream.
_onViewsChangedController.close();
// close the streams.
_onViewCreatedController.close();
_onViewDisposedController.close();
}
}
4 changes: 0 additions & 4 deletions lib/web_ui/test/canvaskit/common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ void setUpCanvasKitTest() {
setUpTestViewDimensions: false,
);

tearDown(() {
CanvasKitRenderer.instance.debugClear();
});

setUp(() => renderer.fontCollection.fontFallbackManager!.downloadQueue
.fallbackFontUrlPrefixOverride = 'assets/fallback_fonts/');
tearDown(() => renderer.fontCollection.fontFallbackManager!.downloadQueue
Expand Down
37 changes: 35 additions & 2 deletions lib/web_ui/test/canvaskit/embedded_views_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ void testMain() {
reason: 'The slot reenables pointer events.');
expect(contentsHost.getAttribute('slot'), slot.getAttribute('name'),
reason: 'The contents and slot are correctly related.');

await disposePlatformView(0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it makes more sense to have this here, since creating and adding of platformView 0 happened in the test body too!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change was required since I no longer dispose of the Renderer between each test

});

test('clips platform views with RRects', () async {
Expand Down Expand Up @@ -101,6 +103,8 @@ void testMain() {
sceneHost.querySelectorAll('flt-clip').single.style.height,
'100%',
);

await disposePlatformView(0);
});

test('correctly transforms platform views', () async {
Expand Down Expand Up @@ -131,6 +135,8 @@ void testMain() {
// 503 (5 * 100 + 3).
'matrix3d(5, 0, 0, 0, 0, 5, 0, 0, 0, 0, 5, 0, 515, 515, 0, 1)',
);

await disposePlatformView(0);
});

test('correctly offsets platform views', () async {
Expand All @@ -157,6 +163,8 @@ void testMain() {
expect(slotRect.top, 4);
expect(slotRect.right, 8);
expect(slotRect.bottom, 10);

await disposePlatformView(0);
});

// Returns the list of CSS transforms applied to the ancestor chain of
Expand Down Expand Up @@ -189,8 +197,7 @@ void testMain() {
CanvasKitRenderer.instance.renderScene(sb.build(), implicitView);

// Transformations happen on the slot element.
DomElement slotHost =
sceneHost.querySelector('flt-platform-view-slot')!;
DomElement slotHost = sceneHost.querySelector('flt-platform-view-slot')!;

expect(
getTransformChain(slotHost),
Expand Down Expand Up @@ -220,6 +227,8 @@ void testMain() {
'matrix(1, 0, 0, 1, 3, 3)',
],
);

await disposePlatformView(0);
});

test('converts device pixels to logical pixels (no clips)', () async {
Expand All @@ -245,6 +254,8 @@ void testMain() {
getTransformChain(slotHost),
<String>['matrix(0.25, 0, 0, 0.25, 1.5, 1.5)'],
);

await disposePlatformView(0);
});

test('converts device pixels to logical pixels (with clips)', () async {
Expand Down Expand Up @@ -276,6 +287,8 @@ void testMain() {
'matrix(0.25, 0, 0, 0.25, 0.75, 0.75)',
],
);

await disposePlatformView(0);
});

test('renders overlays on top of platform views', () async {
Expand Down Expand Up @@ -428,6 +441,10 @@ void testMain() {
await createPlatformView(0, 'test-platform-view');
renderTestScene(viewCount: 0);
_expectSceneMatches(<_EmbeddedViewMarker>[_overlay]);

for (int i = 0; i < 16; i++) {
await disposePlatformView(i);
}
// TODO(yjbanov): skipped due to https://github.com/flutter/flutter/issues/73867
}, skip: isSafari);
Comment thread
harryterkelsen marked this conversation as resolved.
Outdated

Expand Down Expand Up @@ -561,6 +578,10 @@ void testMain() {
_overlay,
]);

for (int i = 0; i < 20; i++) {
await disposePlatformView(i);
}

// TODO(yjbanov): skipped due to https://github.com/flutter/flutter/issues/73867
}, skip: isSafari);
Comment thread
harryterkelsen marked this conversation as resolved.
Outdated

Expand Down Expand Up @@ -641,6 +662,8 @@ void testMain() {

implicitView.debugPhysicalSizeOverride = null;
implicitView.debugForceResize();

await disposePlatformView(0);
// ImageDecoder is not supported in Safari or Firefox.
}, skip: isSafari || isFirefox);

Expand Down Expand Up @@ -695,6 +718,9 @@ void testMain() {
platformViewsHost.querySelectorAll('flt-platform-view'),
hasLength(2),
);

await disposePlatformView(0);
await disposePlatformView(1);
});

test(
Expand Down Expand Up @@ -729,6 +755,8 @@ void testMain() {
await Future<void>.delayed(Duration.zero);
renderTestScene();
expect(skPathDefs.childNodes, hasLength(1));

await disposePlatformView(0);
});

test('does not crash when a prerolled platform view is not composited',
Expand All @@ -749,6 +777,8 @@ void testMain() {
_expectSceneMatches(<_EmbeddedViewMarker>[
_overlay,
]);

await disposePlatformView(0);
});

test('does not create overlays for invisible platform views', () async {
Expand Down Expand Up @@ -951,6 +981,9 @@ void testMain() {
_platformView,
_overlay,
]);
for (int i = 0; i < 7; i++) {
await disposePlatformView(i);
}
});
});
}
Expand Down
Loading