From f8fcd92c13c3f03b5adc5449616731edc700e5c8 Mon Sep 17 00:00:00 2001 From: Amir Hardon Date: Thu, 1 Nov 2018 20:11:39 -0700 Subject: [PATCH] Add an internal_nodes_canvas to PaintContext. When we visit a PlatformViewLayer during the paint traversal it replaces the PaintContext's canvas with a new one that is painted ontop of the embedded view. We need to make sure that operations applied by parent layers are also applied to the new canvas. To achieve this we collect all the canvases in a SkNWayCanvas and use this canvas by non leaf nodes. Leaf nodes still paint only to the "current" canvas. This PR moves the overlay canvas creation from the paint phase to the preroll phase, collects them into a SkNWayCanvas and set it in PaintContext. To keep this PR focused, I only used the internal_nodes_canvas in the tranform_layer. Will followup with a PR that changes all internal layers to use the internal_nodes_canvas. --- flow/embedded_views.h | 7 ++++- flow/layers/layer.h | 17 +++++++++++ flow/layers/layer_tree.cc | 19 ++++++++++++ flow/layers/platform_view_layer.cc | 10 +++++-- flow/layers/transform_layer.cc | 4 +-- flow/raster_cache.cc | 5 ++++ flow/scene_update_context.cc | 1 + shell/common/rasterizer.cc | 5 ++++ .../framework/Source/FlutterPlatformViews.mm | 30 ++++++++++++++----- .../Source/FlutterPlatformViews_Internal.h | 9 +++++- shell/platform/darwin/ios/ios_surface_gl.h | 9 ++++++ shell/platform/darwin/ios/ios_surface_gl.mm | 18 +++++++++++ .../darwin/ios/ios_surface_software.h | 9 ++++++ .../darwin/ios/ios_surface_software.mm | 18 +++++++++++ 14 files changed, 147 insertions(+), 14 deletions(-) diff --git a/flow/embedded_views.h b/flow/embedded_views.h index a36d941ee3476..a9cb9e2ea5f8f 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -16,7 +16,6 @@ class EmbeddedViewParams { public: SkPoint offsetPixels; SkSize sizePoints; - SkISize canvasBaseLayerSize; }; // This is only used on iOS when running in a non headless mode, @@ -26,6 +25,12 @@ class ExternalViewEmbedder { public: ExternalViewEmbedder() = default; + virtual void SetFrameSize(SkISize frame_size) = 0; + + virtual void PrerollCompositeEmbeddedView(int view_id) = 0; + + virtual std::vector GetCurrentCanvases() = 0; + // Must be called on the UI thread. virtual SkCanvas* CompositeEmbeddedView(int view_id, const EmbeddedViewParams& params) = 0; diff --git a/flow/layers/layer.h b/flow/layers/layer.h index 098ae5f789e07..7b53b71077a78 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -25,6 +25,7 @@ #include "third_party/skia/include/core/SkPicture.h" #include "third_party/skia/include/core/SkRRect.h" #include "third_party/skia/include/core/SkRect.h" +#include "third_party/skia/include/utils/SkNWayCanvas.h" #if defined(OS_FUCHSIA) @@ -44,6 +45,7 @@ class ContainerLayer; struct PrerollContext { RasterCache* raster_cache; GrContext* gr_context; + ExternalViewEmbedder* view_embedder; SkColorSpace* dst_color_space; SkRect child_paint_bounds; @@ -64,6 +66,21 @@ class Layer { virtual void Preroll(PrerollContext* context, const SkMatrix& matrix); struct PaintContext { + // When splitting the scene into multiple canvases (e.g when embedding + // a platform view on iOS) during the paint traversal we apply the non leaf + // flow layers to all canvases, and leaf layers just to the "current" + // canvas. Applying the non leaf layers to all canvases ensures that when + // we switch a canvas (when painting a PlatformViewLayer) the next canvas + // has the exact same state as the current canvas. + // The internal_nodes_canvas is a SkNWayCanvas which is used by non leaf + // and applies the operations to all canvases. + // The leaf_nodes_canvas is the "current" canvas and is used by leaf + // layers. + SkCanvas* internal_nodes_canvas; + // I'm temporarily leaving the name of this field to be canvas to reduce + // noise in the incremental change. A followup change will rename this + // and use the corrrect canvas in each callsite. + // TODO(amirh) rename canvas to leaf_nodes_canvas. SkCanvas* canvas; ExternalViewEmbedder* view_embedder; const Stopwatch& frame_time; diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index 942f5cc9a2060..8dd30691fdb10 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -7,6 +7,7 @@ #include "flutter/flow/layers/layer.h" #include "flutter/fml/trace_event.h" #include "third_party/skia/include/core/SkPictureRecorder.h" +#include "third_party/skia/include/utils/SkNWayCanvas.h" namespace flow { @@ -28,6 +29,7 @@ void LayerTree::Preroll(CompositorContext::ScopedFrame& frame, PrerollContext context = { ignore_raster_cache ? nullptr : &frame.context().raster_cache(), frame.gr_context(), + frame.view_embedder(), color_space, SkRect::MakeEmpty(), frame.context().frame_time(), @@ -66,7 +68,18 @@ void LayerTree::UpdateScene(SceneUpdateContext& context, void LayerTree::Paint(CompositorContext::ScopedFrame& frame, bool ignore_raster_cache) const { TRACE_EVENT0("flutter", "LayerTree::Paint"); + SkISize canvas_size = frame.canvas()->getBaseLayerSize(); + SkNWayCanvas internal_nodes_canvas(canvas_size.width(), canvas_size.height()); + internal_nodes_canvas.addCanvas(frame.canvas()); + if (frame.view_embedder() != nullptr) { + auto overlay_canvases = frame.view_embedder()->GetCurrentCanvases(); + for (size_t i = 0; i < overlay_canvases.size(); i++) { + internal_nodes_canvas.addCanvas(overlay_canvases[i]); + } + } + Layer::PaintContext context = { + (SkCanvas*)&internal_nodes_canvas, frame.canvas(), frame.view_embedder(), frame.context().frame_time(), @@ -98,6 +111,7 @@ sk_sp LayerTree::Flatten(const SkRect& bounds) { PrerollContext preroll_context{ nullptr, // raster_cache (don't consult the cache) nullptr, // gr_context (used for the raster cache) + nullptr, // external view embedder nullptr, // SkColorSpace* dst_color_space SkRect::MakeEmpty(), // SkRect child_paint_bounds unused_stopwatch, // frame time (dont care) @@ -106,7 +120,12 @@ sk_sp LayerTree::Flatten(const SkRect& bounds) { false, // checkerboard_offscreen_layers }; + SkISize canvas_size = canvas->getBaseLayerSize(); + SkNWayCanvas internal_nodes_canvas(canvas_size.width(), canvas_size.height()); + internal_nodes_canvas.addCanvas(canvas); + Layer::PaintContext paint_context = { + (SkCanvas*)&internal_nodes_canvas, canvas, // canvas nullptr, unused_stopwatch, // frame time (dont care) diff --git a/flow/layers/platform_view_layer.cc b/flow/layers/platform_view_layer.cc index efcddbcc98175..65d22dd4f10c9 100644 --- a/flow/layers/platform_view_layer.cc +++ b/flow/layers/platform_view_layer.cc @@ -14,6 +14,13 @@ void PlatformViewLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { set_paint_bounds(SkRect::MakeXYWH(offset_.x(), offset_.y(), size_.width(), size_.height())); + + if (context->view_embedder == nullptr) { + FML_LOG(ERROR) << "Trying to embed a platform view but the PrerollContext " + "does not support embedding"; + return; + } + context->view_embedder->PrerollCompositeEmbeddedView(view_id_); } void PlatformViewLayer::Paint(PaintContext& context) const { @@ -27,12 +34,9 @@ void PlatformViewLayer::Paint(PaintContext& context) const { params.offsetPixels = SkPoint::Make(transform.getTranslateX(), transform.getTranslateY()); params.sizePoints = size_; - params.canvasBaseLayerSize = context.canvas->getBaseLayerSize(); SkCanvas* canvas = context.view_embedder->CompositeEmbeddedView(view_id_, params); - // TODO(amirh): copy the full canvas state here - canvas->concat(context.canvas->getTotalMatrix()); context.canvas = canvas; } } // namespace flow diff --git a/flow/layers/transform_layer.cc b/flow/layers/transform_layer.cc index a96e0e27d7560..38405248ce868 100644 --- a/flow/layers/transform_layer.cc +++ b/flow/layers/transform_layer.cc @@ -36,8 +36,8 @@ void TransformLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "TransformLayer::Paint"); FML_DCHECK(needs_painting()); - SkAutoCanvasRestore save(context.canvas, true); - context.canvas->concat(transform_); + SkAutoCanvasRestore save(context.internal_nodes_canvas, true); + context.internal_nodes_canvas->concat(transform_); PaintChildren(context); } diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index a92e1285379a7..dc4a56304c86b 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -156,7 +156,12 @@ void RasterCache::Prepare(PrerollContext* context, entry.image = Rasterize(context->gr_context, ctm, context->dst_color_space, checkerboard_images_, layer->paint_bounds(), [layer, context](SkCanvas* canvas) { + SkISize canvas_size = canvas->getBaseLayerSize(); + SkNWayCanvas internal_nodes_canvas( + canvas_size.width(), canvas_size.height()); + internal_nodes_canvas.addCanvas(canvas); Layer::PaintContext paintContext = { + (SkCanvas*)&internal_nodes_canvas, canvas, nullptr, context->frame_time, diff --git a/flow/scene_update_context.cc b/flow/scene_update_context.cc index 0c1e5a780c2ec..bfb5f8978393a 100644 --- a/flow/scene_update_context.cc +++ b/flow/scene_update_context.cc @@ -188,6 +188,7 @@ SceneUpdateContext::ExecutePaintTasks(CompositorContext::ScopedFrame& frame) { FML_DCHECK(task.surface); SkCanvas* canvas = task.surface->GetSkiaSurface()->getCanvas(); Layer::PaintContext context = {canvas, + canvas, nullptr, frame.context().frame_time(), frame.context().engine_time(), diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 848590beb110d..21b1ff0e13730 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -164,6 +164,11 @@ bool Rasterizer::DrawToSurface(flow::LayerTree& layer_tree) { auto external_view_embedder = surface_->GetExternalViewEmbedder(); + // TODO(amirh): uncomment this once external_view_embedder is populated. + // if (external_view_embedder != nullptr) { + // external_view_embedder->SetFrameSize(layer_tree.frame_size()); + // } + auto compositor_frame = compositor_context_->AcquireFrame( surface_->GetContext(), canvas, external_view_embedder, surface_->GetRootTransformation(), true); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 798839fab4d1c..1cb8245a44056 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -128,6 +128,25 @@ fml::scoped_nsobject>([factory retain]); } +void FlutterPlatformViewsController::SetFrameSize(SkISize frame_size) { + frame_size_ = frame_size; +} + +void FlutterPlatformViewsController::PrerollCompositeEmbeddedView(int view_id) { + EnsureOverlayInitialized(view_id); + composition_frames_[view_id] = (overlays_[view_id]->surface->AcquireFrame(frame_size_)); + composition_order_.push_back(view_id); +} + +std::vector FlutterPlatformViewsController::GetCurrentCanvases() { + std::vector canvases; + for (size_t i = 0; i < composition_order_.size(); i++) { + int64_t view_id = composition_order_[i]; + canvases.push_back(composition_frames_[view_id]->SkiaCanvas()); + } + return canvases; +} + SkCanvas* FlutterPlatformViewsController::CompositeEmbeddedView( int view_id, const flow::EmbeddedViewParams& params, @@ -135,7 +154,6 @@ // TODO(amirh): assert that this is running on the platform thread once we support the iOS // embedded views thread configuration. // TODO(amirh): do nothing if the params didn't change. - EnsureOverlayInitialized(view_id); CGFloat screenScale = [[UIScreen mainScreen] scale]; CGRect rect = CGRectMake(params.offsetPixels.x() / screenScale, params.offsetPixels.y() / screenScale, @@ -143,19 +161,17 @@ UIView* view = views_[view_id].get(); [view setFrame:rect]; - composition_order_.push_back(view_id); - composition_frames_.push_back( - overlays_[view_id]->surface->AcquireFrame(params.canvasBaseLayerSize)); - SkCanvas* canvas = composition_frames_.back()->SkiaCanvas(); + SkCanvas* canvas = composition_frames_[view_id]->SkiaCanvas(); canvas->clear(SK_ColorTRANSPARENT); return canvas; } bool FlutterPlatformViewsController::Present() { bool did_submit = true; - for (size_t i = 0; i < composition_frames_.size(); i++) { - did_submit &= composition_frames_[i]->Submit(); + for (size_t i = 0; i < composition_order_.size(); i++) { + int64_t view_id = composition_order_[i]; + did_submit &= composition_frames_[view_id]->Submit(); } composition_frames_.clear(); if (composition_order_ == active_composition_order_) { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 92d308c244d43..d4ed3bdf5afbc 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -49,6 +49,12 @@ class FlutterPlatformViewsController { void RegisterViewFactory(NSObject* factory, NSString* factoryId); + void SetFrameSize(SkISize frame_size); + + void PrerollCompositeEmbeddedView(int view_id); + + std::vector GetCurrentCanvases(); + SkCanvas* CompositeEmbeddedView(int view_id, const flow::EmbeddedViewParams& params, IOSSurface& surface); @@ -63,6 +69,7 @@ class FlutterPlatformViewsController { std::map>> factories_; std::map> views_; std::map> overlays_; + SkISize frame_size_; // A vector of embedded view IDs according to their composition order. // The last ID in this vector belond to the that is composited on top of all others. @@ -71,7 +78,7 @@ class FlutterPlatformViewsController { // The latest composition order that was presented in Present(). std::vector active_composition_order_; - std::vector> composition_frames_; + std::map> composition_frames_; void OnCreate(FlutterMethodCall* call, FlutterResult& result); void OnDispose(FlutterMethodCall* call, FlutterResult& result); diff --git a/shell/platform/darwin/ios/ios_surface_gl.h b/shell/platform/darwin/ios/ios_surface_gl.h index a6bcfbdf45d46..db52590728145 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.h +++ b/shell/platform/darwin/ios/ios_surface_gl.h @@ -45,6 +45,15 @@ class IOSSurfaceGL : public IOSSurface, // |shell::GPUSurfaceGLDelegate| flow::ExternalViewEmbedder* GetExternalViewEmbedder() override; + // |flow::ExternalViewEmbedder| + void SetFrameSize(SkISize frame_size) override; + + // |flow::ExternalViewEmbedder| + void PrerollCompositeEmbeddedView(int view_id) override; + + // |flow::ExternalViewEmbedder| + std::vector GetCurrentCanvases() override; + // |flow::ExternalViewEmbedder| SkCanvas* CompositeEmbeddedView(int view_id, const flow::EmbeddedViewParams& params) override; diff --git a/shell/platform/darwin/ios/ios_surface_gl.mm b/shell/platform/darwin/ios/ios_surface_gl.mm index 9291455176070..27d5909d2e876 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.mm +++ b/shell/platform/darwin/ios/ios_surface_gl.mm @@ -74,6 +74,24 @@ } } +void IOSSurfaceGL::SetFrameSize(SkISize frame_size) { + FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController(); + FML_CHECK(platform_views_controller != nullptr); + platform_views_controller->SetFrameSize(frame_size); +} + +void IOSSurfaceGL::PrerollCompositeEmbeddedView(int view_id) { + FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController(); + FML_CHECK(platform_views_controller != nullptr); + platform_views_controller->PrerollCompositeEmbeddedView(view_id); +} + +std::vector IOSSurfaceGL::GetCurrentCanvases() { + FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController(); + FML_CHECK(platform_views_controller != nullptr); + return platform_views_controller->GetCurrentCanvases(); +} + SkCanvas* IOSSurfaceGL::CompositeEmbeddedView(int view_id, const flow::EmbeddedViewParams& params) { FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController(); FML_CHECK(platform_views_controller != nullptr); diff --git a/shell/platform/darwin/ios/ios_surface_software.h b/shell/platform/darwin/ios/ios_surface_software.h index 2e5da95e2b48f..96ca548ab4d89 100644 --- a/shell/platform/darwin/ios/ios_surface_software.h +++ b/shell/platform/darwin/ios/ios_surface_software.h @@ -45,6 +45,15 @@ class IOSSurfaceSoftware final : public IOSSurface, // |shell::GPUSurfaceSoftwareDelegate| flow::ExternalViewEmbedder* GetExternalViewEmbedder() override; + // |flow::ExternalViewEmbedder| + void SetFrameSize(SkISize frame_size) override; + + // |flow::ExternalViewEmbedder| + void PrerollCompositeEmbeddedView(int view_id) override; + + // |flow::ExternalViewEmbedder| + std::vector GetCurrentCanvases() override; + // |flow::ExternalViewEmbedder| SkCanvas* CompositeEmbeddedView(int view_id, const flow::EmbeddedViewParams& params) override; diff --git a/shell/platform/darwin/ios/ios_surface_software.mm b/shell/platform/darwin/ios/ios_surface_software.mm index bcf57f5cd28d4..f5cb8d5317271 100644 --- a/shell/platform/darwin/ios/ios_surface_software.mm +++ b/shell/platform/darwin/ios/ios_surface_software.mm @@ -138,6 +138,24 @@ } } +void IOSSurfaceSoftware::SetFrameSize(SkISize frame_size) { + FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController(); + FML_CHECK(platform_views_controller != nullptr); + platform_views_controller->SetFrameSize(frame_size); +} + +void IOSSurfaceSoftware::PrerollCompositeEmbeddedView(int view_id) { + FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController(); + FML_CHECK(platform_views_controller != nullptr); + platform_views_controller->PrerollCompositeEmbeddedView(view_id); +} + +std::vector IOSSurfaceSoftware::GetCurrentCanvases() { + FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController(); + FML_CHECK(platform_views_controller != nullptr); + return platform_views_controller->GetCurrentCanvases(); +} + SkCanvas* IOSSurfaceSoftware::CompositeEmbeddedView(int view_id, const flow::EmbeddedViewParams& params) { FlutterPlatformViewsController* platform_views_controller = GetPlatformViewsController();