diff --git a/lib/web_ui/lib/src/engine/canvaskit/layer.dart b/lib/web_ui/lib/src/engine/canvaskit/layer.dart index 0b03fa823ed0d..52d6e322b50bd 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/layer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/layer.dart @@ -117,6 +117,17 @@ abstract class ContainerLayer extends Layer { } } +/// The top-most layer in the layer tree. +/// +/// This layer does not draw anything. It's only used so we can add leaf layers +/// to [LayerSceneBuilder] without requiring a [ContainerLayer]. +class RootLayer extends ContainerLayer { + @override + void paint(PaintContext context) { + paintChildren(context); + } +} + class BackdropFilterEngineLayer extends ContainerLayer implements ui.BackdropFilterEngineLayer { final ui.ImageFilter _filter; diff --git a/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart b/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart index ddea2c1868c37..4ea75cf7479b3 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart @@ -8,9 +8,7 @@ part of engine; class LayerScene implements ui.Scene { final LayerTree layerTree; - LayerScene(Layer? rootLayer) : layerTree = LayerTree() { - layerTree.rootLayer = rootLayer; - } + LayerScene(RootLayer rootLayer) : layerTree = LayerTree(rootLayer); @override void dispose() {} @@ -23,8 +21,12 @@ class LayerScene implements ui.Scene { } class LayerSceneBuilder implements ui.SceneBuilder { - Layer? rootLayer; - ContainerLayer? currentLayer; + LayerSceneBuilder() : rootLayer = RootLayer() { + currentLayer = rootLayer; + } + + final RootLayer rootLayer; + late ContainerLayer currentLayer; @override void addChildScene({ @@ -50,16 +52,13 @@ class LayerSceneBuilder implements ui.SceneBuilder { bool isComplexHint = false, bool willChangeHint = false, }) { - currentLayer!.add(PictureLayer( + currentLayer.add(PictureLayer( picture as CkPicture, offset, isComplexHint, willChangeHint)); } @override void addRetained(ui.EngineLayer retainedLayer) { - if (currentLayer == null) { - return; - } - currentLayer!.add(retainedLayer as Layer); + currentLayer.add(retainedLayer as Layer); } @override @@ -82,7 +81,7 @@ class LayerSceneBuilder implements ui.SceneBuilder { double height = 0.0, Object? webOnlyPaintedBy, }) { - currentLayer!.add(PlatformViewLayer(viewId, offset, width, height)); + currentLayer.add(PlatformViewLayer(viewId, offset, width, height)); } @override @@ -92,10 +91,11 @@ class LayerSceneBuilder implements ui.SceneBuilder { @override void pop() { - if (currentLayer == null) { + if (currentLayer == rootLayer) { + // Don't pop the root layer. It must always be there. return; } - currentLayer = currentLayer!.parent; + currentLayer = currentLayer.parent!; } @override @@ -221,16 +221,7 @@ class LayerSceneBuilder implements ui.SceneBuilder { } T pushLayer(T layer) { - if (rootLayer == null) { - rootLayer = currentLayer = layer; - return layer; - } - - if (currentLayer == null) { - return layer; - } - - currentLayer!.add(layer); + currentLayer.add(layer); currentLayer = layer; return layer; } diff --git a/lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart b/lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart index bf020566835dc..986977b4b4f78 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart @@ -7,8 +7,10 @@ part of engine; /// A tree of [Layer]s that, together with a [Size] compose a frame. class LayerTree { + LayerTree(this.rootLayer); + /// The root of the layer tree. - Layer? rootLayer; + final RootLayer rootLayer; /// The size (in physical pixels) of the frame to paint this layer tree into. final ui.Size frameSize = ui.window.physicalSize; @@ -27,7 +29,7 @@ class LayerTree { ignoreRasterCache ? null : frame.rasterCache, frame.viewEmbedder, ); - rootLayer!.preroll(context, Matrix4.identity()); + rootLayer.preroll(context, Matrix4.identity()); } /// Paints the layer tree into the given [frame]. @@ -48,8 +50,8 @@ class LayerTree { ignoreRasterCache ? null : frame.rasterCache, frame.viewEmbedder, ); - if (rootLayer!.needsPainting) { - rootLayer!.paint(context); + if (rootLayer.needsPainting) { + rootLayer.paint(context); } } @@ -59,17 +61,15 @@ class LayerTree { ui.Picture flatten() { CkPictureRecorder recorder = CkPictureRecorder(); CkCanvas canvas = recorder.beginRecording(ui.Rect.largest); - if (rootLayer != null) { - final PrerollContext prerollContext = PrerollContext(null, null); - rootLayer!.preroll(prerollContext, Matrix4.identity()); - - CkNWayCanvas internalNodesCanvas = CkNWayCanvas(); - internalNodesCanvas.addCanvas(canvas); - final PaintContext paintContext = - PaintContext(internalNodesCanvas, canvas, null, null); - if (rootLayer!.needsPainting) { - rootLayer!.paint(paintContext); - } + final PrerollContext prerollContext = PrerollContext(null, null); + rootLayer.preroll(prerollContext, Matrix4.identity()); + + CkNWayCanvas internalNodesCanvas = CkNWayCanvas(); + internalNodesCanvas.addCanvas(canvas); + final PaintContext paintContext = + PaintContext(internalNodesCanvas, canvas, null, null); + if (rootLayer.needsPainting) { + rootLayer.paint(paintContext); } return recorder.endRecording(); } diff --git a/lib/web_ui/test/canvaskit/canvas_golden_test.dart b/lib/web_ui/test/canvaskit/canvas_golden_test.dart index 3688f67e0171b..5ff95df766913 100644 --- a/lib/web_ui/test/canvaskit/canvas_golden_test.dart +++ b/lib/web_ui/test/canvaskit/canvas_golden_test.dart @@ -197,7 +197,7 @@ void testMain() { // Render the scene once without painting the shadow bounds just to // preroll the scene to compute the shadow bounds. - buildTestScene(paintShadowBounds: false).rootLayer!.preroll( + buildTestScene(paintShadowBounds: false).rootLayer.preroll( PrerollContext( RasterCache(), HtmlViewEmbedder(), diff --git a/lib/web_ui/test/canvaskit/layer_test.dart b/lib/web_ui/test/canvaskit/layer_test.dart index df52598c7c6c3..81b86c086b78e 100644 --- a/lib/web_ui/test/canvaskit/layer_test.dart +++ b/lib/web_ui/test/canvaskit/layer_test.dart @@ -44,12 +44,18 @@ void testMain() { sb.addPicture(ui.Offset.zero, picture); final LayerTree layerTree = sb.build().layerTree; dispatcher.rasterizer!.draw(layerTree); - final ClipRectEngineLayer clipRect = layerTree.rootLayer as ClipRectEngineLayer; + final ClipRectEngineLayer clipRect = layerTree.rootLayer.debugLayers.single as ClipRectEngineLayer; expect(clipRect.paintBounds, ui.Rect.fromLTRB(15, 15, 30, 30)); final TransformEngineLayer transform = clipRect.debugLayers.single as TransformEngineLayer; expect(transform.paintBounds, ui.Rect.fromLTRB(0, 0, 30, 30)); }); + + test('can push a leaf layer without a container layer', () async { + final CkPictureRecorder recorder = CkPictureRecorder(); + recorder.beginRecording(ui.Rect.zero); + LayerSceneBuilder().addPicture(ui.Offset.zero, recorder.endRecording()); + }); // TODO: https://github.com/flutter/flutter/issues/60040 }, skip: isIosSafari); }