From d11c3568624a0e580040fa448ad148d907a471f9 Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Thu, 27 Apr 2023 16:45:37 -0700 Subject: [PATCH 1/3] support drawing layers behind canvas --- .../src/engine/canvaskit/embedded_views.dart | 62 +++++++++++-------- .../lib/src/engine/canvaskit/layer.dart | 7 ++- .../lib/src/engine/canvaskit/surface.dart | 2 +- 3 files changed, 44 insertions(+), 27 deletions(-) diff --git a/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart b/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart index e2d5f196348f9..6d58fce341d87 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart @@ -28,6 +28,8 @@ class HtmlViewEmbedder { /// The [HtmlViewEmbedder] singleton. static HtmlViewEmbedder instance = HtmlViewEmbedder._(); + bool renderViewsBehindCanvas = true; + DomElement get skiaSceneHost => CanvasKitRenderer.instance.sceneHost!; /// Force the view embedder to disable overlays. @@ -123,24 +125,27 @@ class HtmlViewEmbedder { } void prerollCompositeEmbeddedView(int viewId, EmbeddedViewParams params) { - final bool hasAvailableOverlay = - _context.pictureRecordersCreatedDuringPreroll.length < - SurfaceFactory.instance.maximumOverlays; - if (!hasAvailableOverlay && !_warnedAboutTooManySurfaces) { - _warnedAboutTooManySurfaces = true; - printWarning('Flutter was unable to create enough overlay surfaces. ' - 'This is usually caused by too many platform views being ' - 'displayed at once. ' - 'You may experience incorrect rendering.'); - } - // We need an overlay for each visible platform view. Invisible platform - // views will be grouped with (at most) one visible platform view later. - final bool needNewOverlay = platformViewManager.isVisible(viewId); - if (needNewOverlay && hasAvailableOverlay) { - final CkPictureRecorder pictureRecorder = CkPictureRecorder(); - pictureRecorder.beginRecording(ui.Offset.zero & _frameSize); - pictureRecorder.recordingCanvas!.clear(const ui.Color(0x00000000)); - _context.pictureRecordersCreatedDuringPreroll.add(pictureRecorder); + if (!renderViewsBehindCanvas) { + + final bool hasAvailableOverlay = + _context.pictureRecordersCreatedDuringPreroll.length < + SurfaceFactory.instance.maximumOverlays; + if (!hasAvailableOverlay && !_warnedAboutTooManySurfaces) { + _warnedAboutTooManySurfaces = true; + printWarning('Flutter was unable to create enough overlay surfaces. ' + 'This is usually caused by too many platform views being ' + 'displayed at once. ' + 'You may experience incorrect rendering.'); + } + // We need an overlay for each visible platform view. Invisible platform + // views will be grouped with (at most) one visible platform view later. + final bool needNewOverlay = platformViewManager.isVisible(viewId); + if (needNewOverlay && hasAvailableOverlay) { + final CkPictureRecorder pictureRecorder = CkPictureRecorder(); + pictureRecorder.beginRecording(ui.Offset.zero & _frameSize); + pictureRecorder.recordingCanvas!.clear(const ui.Color(0x00000000)); + _context.pictureRecordersCreatedDuringPreroll.add(pictureRecorder); + } } // Do nothing if the params didn't change. @@ -167,14 +172,18 @@ class HtmlViewEmbedder { if (platformViewManager.isVisible(viewId)) { _context.visibleViewCount++; } - // We need a new overlay if this is a visible view. - final bool needNewOverlay = platformViewManager.isVisible(viewId); + CkPictureRecorder? recorderToUseForRendering; - if (needNewOverlay) { - if (overlayIndex < _context.pictureRecordersCreatedDuringPreroll.length) { - recorderToUseForRendering = - _context.pictureRecordersCreatedDuringPreroll[overlayIndex]; - _context.pictureRecorders.add(recorderToUseForRendering); + if(!renderViewsBehindCanvas) { + // We need a new overlay if this is a visible view. + final bool needNewOverlay = platformViewManager.isVisible(viewId); + + if (needNewOverlay) { + if (overlayIndex < _context.pictureRecordersCreatedDuringPreroll.length) { + recorderToUseForRendering = + _context.pictureRecordersCreatedDuringPreroll[overlayIndex]; + _context.pictureRecorders.add(recorderToUseForRendering); + } } } @@ -583,6 +592,9 @@ class HtmlViewEmbedder { // // TODO(hterkelsen): Test this more thoroughly. void _updateOverlays(ViewListDiffResult? diffResult) { + if(renderViewsBehindCanvas) { + return; + } if (diffResult != null && diffResult.viewsToAdd.isEmpty && diffResult.viewsToRemove.isEmpty) { diff --git a/lib/web_ui/lib/src/engine/canvaskit/layer.dart b/lib/web_ui/lib/src/engine/canvaskit/layer.dart index 640b3712e36ab..bff4a0e62f7fa 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/layer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/layer.dart @@ -498,7 +498,8 @@ class PhysicalShapeEngineLayer extends ContainerLayer final double _elevation; final ui.Color _color; - final ui.Color? _shadowColor; // ignore: use_late_for_private_fields_and_variables + final ui.Color? + _shadowColor; // ignore: use_late_for_private_fields_and_variables final CkPath _path; final ui.Clip _clipBehavior; @@ -613,5 +614,9 @@ class PlatformViewLayer extends Layer { if (canvas != null) { paintContext.leafNodesCanvas = canvas; } + + if (paintContext.viewEmbedder?.renderViewsBehindCanvas ?? false) { + paintContext.leafNodesCanvas?.clear(const ui.Color.fromARGB(0, 0, 0, 0)); + } } } diff --git a/lib/web_ui/lib/src/engine/canvaskit/surface.dart b/lib/web_ui/lib/src/engine/canvaskit/surface.dart index 76719d68dcea8..b7cdbcc940d67 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/surface.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/surface.dart @@ -91,7 +91,7 @@ class Surface { /// Conversely, the canvas that lives inside this element can be swapped, for /// example, when the screen size changes, or when the WebGL context is lost /// due to the browser tab becoming dormant. - final DomElement htmlElement = createDomElement('flt-canvas-container'); + final DomElement htmlElement = createDomElement('flt-canvas-container')..setAttribute('style', 'position:relative;z-index:1;'); /// The underlying `` element used for this surface. DomCanvasElement? htmlCanvas; From db9520ca0696b9e564050ad6d6d974e274132c0b Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Sun, 30 Apr 2023 21:51:31 -0700 Subject: [PATCH 2/3] support platform view layers z-index for canvaskit in order to reduce webgl contexts --- lib/ui/compositing.dart | 3 +++ lib/web_ui/lib/compositing.dart | 1 + .../src/engine/canvaskit/embedded_views.dart | 25 +++++++++++-------- .../lib/src/engine/canvaskit/layer.dart | 6 +++-- .../engine/canvaskit/layer_scene_builder.dart | 5 +++- .../lib/src/engine/canvaskit/surface.dart | 2 +- .../lib/src/engine/html/scene_builder.dart | 1 + .../skwasm/skwasm_impl/scene_builder.dart | 3 ++- 8 files changed, 31 insertions(+), 15 deletions(-) diff --git a/lib/ui/compositing.dart b/lib/ui/compositing.dart index f20ecff7349c6..d24b262275905 100644 --- a/lib/ui/compositing.dart +++ b/lib/ui/compositing.dart @@ -814,11 +814,14 @@ class SceneBuilder extends NativeFieldWrapperClass1 { /// synchronized with the UIView frames adding additional performance overhead. /// /// The `offset` argument is not used for iOS and Android. + /// The `zIndex` argument is only used by the CanvasKit renderer. Negative values render behind the + /// canvas and will not require additional WebGL contexts. void addPlatformView( int viewId, { Offset offset = Offset.zero, double width = 0.0, double height = 0.0, + int? zIndex, }) { _addPlatformView(offset.dx, offset.dy, width, height, viewId); } diff --git a/lib/web_ui/lib/compositing.dart b/lib/web_ui/lib/compositing.dart index d3f0acddd6797..9b0ac872b9f64 100644 --- a/lib/web_ui/lib/compositing.dart +++ b/lib/web_ui/lib/compositing.dart @@ -120,6 +120,7 @@ abstract class SceneBuilder { Offset offset = Offset.zero, double width = 0.0, double height = 0.0, + int zIndex, }); void setRasterizerTracingThreshold(int frameInterval); void setCheckerboardRasterCacheImages(bool checkerboard); diff --git a/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart b/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart index 6d58fce341d87..9267a9f7acdf6 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart @@ -28,8 +28,6 @@ class HtmlViewEmbedder { /// The [HtmlViewEmbedder] singleton. static HtmlViewEmbedder instance = HtmlViewEmbedder._(); - bool renderViewsBehindCanvas = true; - DomElement get skiaSceneHost => CanvasKitRenderer.instance.sceneHost!; /// Force the view embedder to disable overlays. @@ -125,7 +123,7 @@ class HtmlViewEmbedder { } void prerollCompositeEmbeddedView(int viewId, EmbeddedViewParams params) { - if (!renderViewsBehindCanvas) { + if (params.zIndex >= 0) { final bool hasAvailableOverlay = _context.pictureRecordersCreatedDuringPreroll.length < @@ -174,7 +172,7 @@ class HtmlViewEmbedder { } CkPictureRecorder? recorderToUseForRendering; - if(!renderViewsBehindCanvas) { + if((_currentCompositionParams[viewId]?.zIndex ?? defaultPlatformViewLayerZIndex) >= 0) { // We need a new overlay if this is a visible view. final bool needNewOverlay = platformViewManager.isVisible(viewId); @@ -224,6 +222,10 @@ class HtmlViewEmbedder { root: newPlatformViewRoot, clipCount: currentClippingCount, ); + + if(params.zIndex != 0) { + newPlatformViewRoot.style.zIndex = params.zIndex.toString(); + } } // Apply mutators to the slot @@ -592,9 +594,6 @@ class HtmlViewEmbedder { // // TODO(hterkelsen): Test this more thoroughly. void _updateOverlays(ViewListDiffResult? diffResult) { - if(renderViewsBehindCanvas) { - return; - } if (diffResult != null && diffResult.viewsToAdd.isEmpty && diffResult.viewsToRemove.isEmpty) { @@ -658,6 +657,10 @@ class HtmlViewEmbedder { for (int i = 0; i < views.length; i++) { final int view = views[i]; + if((_currentCompositionParams[view]?.zIndex ?? defaultPlatformViewLayerZIndex) < 0) { + // If view will be rendered behind canvas we don't need an overlay + continue; + } if (platformViewManager.isInvisible(view)) { // We add as many invisible views as we find to the current group. currentGroup.add(view); @@ -797,12 +800,13 @@ class ViewClipChain { /// The parameters passed to the view embedder. class EmbeddedViewParams { - EmbeddedViewParams(this.offset, this.size, MutatorsStack mutators) + EmbeddedViewParams(this.offset, this.size, MutatorsStack mutators, this.zIndex) : mutators = MutatorsStack._copy(mutators); final ui.Offset offset; final ui.Size size; final MutatorsStack mutators; + final int zIndex; @override bool operator ==(Object other) { @@ -812,11 +816,12 @@ class EmbeddedViewParams { return other is EmbeddedViewParams && other.offset == offset && other.size == size && - other.mutators == mutators; + other.mutators == mutators && + other.zIndex == zIndex; } @override - int get hashCode => Object.hash(offset, size, mutators); + int get hashCode => Object.hash(offset, size, mutators, zIndex); } enum MutatorType { diff --git a/lib/web_ui/lib/src/engine/canvaskit/layer.dart b/lib/web_ui/lib/src/engine/canvaskit/layer.dart index bff4a0e62f7fa..56c303baacddb 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/layer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/layer.dart @@ -584,12 +584,13 @@ class ColorFilterEngineLayer extends ContainerLayer /// A layer which renders a platform view (an HTML element in this case). class PlatformViewLayer extends Layer { - PlatformViewLayer(this.viewId, this.offset, this.width, this.height); + PlatformViewLayer(this.viewId, this.offset, this.width, this.height, this.zIndex); final int viewId; final ui.Offset offset; final double width; final double height; + final int zIndex; @override void preroll(PrerollContext prerollContext, Matrix4 matrix) { @@ -603,6 +604,7 @@ class PlatformViewLayer extends Layer { offset, ui.Size(width, height), prerollContext.mutatorsStack, + zIndex, ), ); } @@ -615,7 +617,7 @@ class PlatformViewLayer extends Layer { paintContext.leafNodesCanvas = canvas; } - if (paintContext.viewEmbedder?.renderViewsBehindCanvas ?? false) { + if (zIndex < 0) { paintContext.leafNodesCanvas?.clear(const ui.Color.fromARGB(0, 0, 0, 0)); } } 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 e1f71752e63c1..13548e4343e31 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 @@ -12,6 +12,8 @@ import 'layer_tree.dart'; import 'path.dart'; import 'picture.dart'; +const int defaultPlatformViewLayerZIndex = -1; + class LayerScene implements ui.Scene { LayerScene(RootLayer rootLayer) : layerTree = LayerTree(rootLayer); @@ -82,8 +84,9 @@ class LayerSceneBuilder implements ui.SceneBuilder { double width = 0.0, double height = 0.0, Object? webOnlyPaintedBy, + int? zIndex, }) { - currentLayer.add(PlatformViewLayer(viewId, offset, width, height)); + currentLayer.add(PlatformViewLayer(viewId, offset, width, height, zIndex ?? defaultPlatformViewLayerZIndex)); } @override diff --git a/lib/web_ui/lib/src/engine/canvaskit/surface.dart b/lib/web_ui/lib/src/engine/canvaskit/surface.dart index b7cdbcc940d67..76719d68dcea8 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/surface.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/surface.dart @@ -91,7 +91,7 @@ class Surface { /// Conversely, the canvas that lives inside this element can be swapped, for /// example, when the screen size changes, or when the WebGL context is lost /// due to the browser tab becoming dormant. - final DomElement htmlElement = createDomElement('flt-canvas-container')..setAttribute('style', 'position:relative;z-index:1;'); + final DomElement htmlElement = createDomElement('flt-canvas-container'); /// The underlying `` element used for this surface. DomCanvasElement? htmlCanvas; diff --git a/lib/web_ui/lib/src/engine/html/scene_builder.dart b/lib/web_ui/lib/src/engine/html/scene_builder.dart index 40270dfaf6ed3..9fae484eb8ebd 100644 --- a/lib/web_ui/lib/src/engine/html/scene_builder.dart +++ b/lib/web_ui/lib/src/engine/html/scene_builder.dart @@ -441,6 +441,7 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { ui.Offset offset = ui.Offset.zero, double width = 0.0, double height = 0.0, + int? zIndex }) { _addPlatformView(offset.dx, offset.dy, width, height, viewId); } diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/scene_builder.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/scene_builder.dart index e2cc62508bf03..1459365d51c61 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/scene_builder.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/scene_builder.dart @@ -59,7 +59,8 @@ class SkwasmSceneBuilder implements ui.SceneBuilder { int viewId, { ui.Offset offset = ui.Offset.zero, double width = 0.0, - double height = 0.0 + double height = 0.0, + int zIndex = 0 }) { throw UnimplementedError('Platform view not yet implemented with skwasm renderer.'); } From 2621d003ce6af85d28b484762ded40a363ba8aef Mon Sep 17 00:00:00 2001 From: Jesse Ezell Date: Mon, 1 May 2023 16:01:13 -0700 Subject: [PATCH 3/3] cleanup dart analysis warnings --- lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart | 2 ++ lib/web_ui/lib/src/engine/skwasm/skwasm_impl/scene_builder.dart | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart b/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart index 9267a9f7acdf6..8a18b44f83cbe 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart @@ -15,12 +15,14 @@ import '../vector_math.dart'; import '../window.dart'; import 'canvas.dart'; import 'embedded_views_diff.dart'; +import 'layer_scene_builder.dart'; import 'path.dart'; import 'picture_recorder.dart'; import 'renderer.dart'; import 'surface.dart'; import 'surface_factory.dart'; + /// This composites HTML views into the [ui.Scene]. class HtmlViewEmbedder { HtmlViewEmbedder._(); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/scene_builder.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/scene_builder.dart index 1459365d51c61..2f1fc5e3f4a21 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/scene_builder.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/scene_builder.dart @@ -60,7 +60,7 @@ class SkwasmSceneBuilder implements ui.SceneBuilder { ui.Offset offset = ui.Offset.zero, double width = 0.0, double height = 0.0, - int zIndex = 0 + int? zIndex }) { throw UnimplementedError('Platform view not yet implemented with skwasm renderer.'); }