diff --git a/flow/embedded_views.cc b/flow/embedded_views.cc index 086882b76adf4..1bf6e6c1d2911 100644 --- a/flow/embedded_views.cc +++ b/flow/embedded_views.cc @@ -21,9 +21,8 @@ void DisplayListEmbedderViewSlice::end_recording() { builder_ = nullptr; } -std::list DisplayListEmbedderViewSlice::searchNonOverlappingDrawnRects( - const SkRect& query) const { - return display_list_->rtree()->searchAndConsolidateRects(query); +const DlRegion& DisplayListEmbedderViewSlice::getRegion() const { + return display_list_->rtree()->region(); } void DisplayListEmbedderViewSlice::render_into(DlCanvas* canvas) { diff --git a/flow/embedded_views.h b/flow/embedded_views.h index c16426980f88d..ed414ed678e35 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -338,8 +338,11 @@ class EmbedderViewSlice { virtual ~EmbedderViewSlice() = default; virtual DlCanvas* canvas() = 0; virtual void end_recording() = 0; - virtual std::list searchNonOverlappingDrawnRects( - const SkRect& query) const = 0; + virtual const DlRegion& getRegion() const = 0; + DlRegion region(const SkRect& query) const { + return DlRegion::MakeIntersection(getRegion(), DlRegion(query.roundOut())); + } + virtual void render_into(DlCanvas* canvas) = 0; }; @@ -350,8 +353,8 @@ class DisplayListEmbedderViewSlice : public EmbedderViewSlice { DlCanvas* canvas() override; void end_recording() override; - std::list searchNonOverlappingDrawnRects( - const SkRect& query) const override; + const DlRegion& getRegion() const override; + void render_into(DlCanvas* canvas) override; void dispatch(DlOpReceiver& receiver); bool is_empty(); diff --git a/shell/platform/android/external_view_embedder/external_view_embedder.cc b/shell/platform/android/external_view_embedder/external_view_embedder.cc index 905c2751a3d4b..e92edbca5996e 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder.cc @@ -104,15 +104,15 @@ void AndroidExternalViewEmbedder::SubmitFrame( // The rect above the `current_view_rect` SkRect partial_joined_rect = SkRect::MakeEmpty(); // Each rect corresponds to a native view that renders Flutter UI. - std::list intersection_rects = - slice->searchNonOverlappingDrawnRects(current_view_rect); + std::vector intersection_rects = + slice->region(current_view_rect).getRects(); // Limit the number of native views, so it doesn't grow forever. // // In this case, the rects are merged into a single one that is the union // of all the rects. - for (const SkRect& rect : intersection_rects) { - partial_joined_rect.join(rect); + for (const SkIRect& rect : intersection_rects) { + partial_joined_rect.join(SkRect::Make(rect)); } // Get the intersection rect with the `current_view_rect`, partial_joined_rect.intersect(current_view_rect); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index a9fc569bc1165..dc8904ea2a061 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -705,8 +705,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, for (size_t j = i + 1; j > 0; j--) { int64_t current_platform_view_id = composition_order_[j - 1]; SkRect platform_view_rect = GetPlatformViewRect(current_platform_view_id); - std::list intersection_rects = - slice->searchNonOverlappingDrawnRects(platform_view_rect); + std::vector intersection_rects = slice->region(platform_view_rect).getRects(); auto allocation_size = intersection_rects.size(); // For testing purposes, the overlay id is used to find the overlay view. @@ -719,8 +718,8 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // TODO(egarciad): Consider making this configurable. // https://github.com/flutter/flutter/issues/52510 if (allocation_size > kMaxLayerAllocations) { - SkRect joined_rect = SkRect::MakeEmpty(); - for (const SkRect& rect : intersection_rects) { + SkIRect joined_rect = SkIRect::MakeEmpty(); + for (const SkIRect& rect : intersection_rects) { joined_rect.join(rect); } // Replace the rects in the intersection rects list for a single rect that is @@ -728,18 +727,13 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, intersection_rects.clear(); intersection_rects.push_back(joined_rect); } - for (SkRect& joined_rect : intersection_rects) { + for (SkIRect& joined_rect : intersection_rects) { // Get the intersection rect between the current rect // and the platform view rect. - joined_rect.intersect(platform_view_rect); - // Subpixels in the platform may not align with the canvas subpixels. - // To workaround it, round the floating point bounds and make the rect slightly larger. - // For example, {0.3, 0.5, 3.1, 4.7} becomes {0, 0, 4, 5}. - joined_rect.setLTRB(std::floor(joined_rect.left()), std::floor(joined_rect.top()), - std::ceil(joined_rect.right()), std::ceil(joined_rect.bottom())); + joined_rect.intersect(platform_view_rect.roundOut()); // Clip the background canvas, so it doesn't contain any of the pixels drawn // on the overlay layer. - background_canvas->ClipRect(joined_rect, DlCanvas::ClipOp::kDifference); + background_canvas->ClipRect(SkRect::Make(joined_rect), DlCanvas::ClipOp::kDifference); // Get a new host layer. std::shared_ptr layer = GetLayer(gr_context, // @@ -817,7 +811,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, GrDirectContext* gr_context, const std::shared_ptr& ios_context, EmbedderViewSlice* slice, - SkRect rect, + SkIRect rect, int64_t view_id, int64_t overlay_id, MTLPixelFormat pixel_format) { @@ -852,7 +846,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, DlCanvas* overlay_canvas = frame->Canvas(); int restore_count = overlay_canvas->GetSaveCount(); overlay_canvas->Save(); - overlay_canvas->ClipRect(rect); + overlay_canvas->ClipRect(SkRect::Make(rect)); overlay_canvas->Clear(DlColor::kTransparent()); slice->render_into(overlay_canvas); overlay_canvas->RestoreToCount(restore_count); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index e754aec279257..677941421ca9e 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -319,7 +319,7 @@ class FlutterPlatformViewsController { std::shared_ptr GetLayer(GrDirectContext* gr_context, const std::shared_ptr& ios_context, EmbedderViewSlice* slice, - SkRect rect, + SkIRect rect, int64_t view_id, int64_t overlay_id, MTLPixelFormat pixel_format); diff --git a/shell/platform/embedder/embedder_external_view.cc b/shell/platform/embedder/embedder_external_view.cc index 843ef85dfed91..9360810463026 100644 --- a/shell/platform/embedder/embedder_external_view.cc +++ b/shell/platform/embedder/embedder_external_view.cc @@ -43,7 +43,7 @@ EmbedderExternalView::~EmbedderExternalView() = default; EmbedderExternalView::RenderTargetDescriptor EmbedderExternalView::CreateRenderTargetDescriptor() const { - return {view_identifier_, render_surface_size_}; + return RenderTargetDescriptor(render_surface_size_); } DlCanvas* EmbedderExternalView::GetCanvas() { @@ -62,9 +62,8 @@ bool EmbedderExternalView::HasPlatformView() const { return view_identifier_.platform_view_id.has_value(); } -std::list EmbedderExternalView::GetEngineRenderedContentsRegion( - const SkRect& query) const { - return slice_->searchNonOverlappingDrawnRects(query); +const DlRegion& EmbedderExternalView::GetDlRegion() const { + return slice_->getRegion(); } bool EmbedderExternalView::HasEngineRenderedContents() { @@ -87,7 +86,8 @@ const EmbeddedViewParams* EmbedderExternalView::GetEmbeddedViewParams() const { return embedded_view_params_.get(); } -bool EmbedderExternalView::Render(const EmbedderRenderTarget& render_target) { +bool EmbedderExternalView::Render(const EmbedderRenderTarget& render_target, + bool clear_surface) { TRACE_EVENT0("flutter", "EmbedderExternalView::Render"); TryEndRecording(); FML_DCHECK(HasEngineRenderedContents()) @@ -124,7 +124,9 @@ bool EmbedderExternalView::Render(const EmbedderRenderTarget& render_target) { DlSkCanvasAdapter dl_canvas(canvas); int restore_count = dl_canvas.GetSaveCount(); dl_canvas.SetTransform(surface_transformation_); - dl_canvas.Clear(DlColor::kTransparent()); + if (clear_surface) { + dl_canvas.Clear(DlColor::kTransparent()); + } slice_->render_into(&dl_canvas); dl_canvas.RestoreToCount(restore_count); dl_canvas.Flush(); diff --git a/shell/platform/embedder/embedder_external_view.h b/shell/platform/embedder/embedder_external_view.h index 6ba27860a449b..f2557cb508d82 100644 --- a/shell/platform/embedder/embedder_external_view.h +++ b/shell/platform/embedder/embedder_external_view.h @@ -46,28 +46,23 @@ class EmbedderExternalView { }; struct RenderTargetDescriptor { - ViewIdentifier view_identifier; SkISize surface_size; - RenderTargetDescriptor(ViewIdentifier p_view_identifier, - SkISize p_surface_size) - : view_identifier(p_view_identifier), surface_size(p_surface_size) {} + explicit RenderTargetDescriptor(const SkISize& p_surface_size) + : surface_size(p_surface_size) {} struct Hash { constexpr std::size_t operator()( const RenderTargetDescriptor& desc) const { return fml::HashCombine(desc.surface_size.width(), - desc.surface_size.height(), - ViewIdentifier::Hash{}(desc.view_identifier)); + desc.surface_size.height()); } }; struct Equal { bool operator()(const RenderTargetDescriptor& lhs, const RenderTargetDescriptor& rhs) const { - return lhs.surface_size == rhs.surface_size && - ViewIdentifier::Equal{}(lhs.view_identifier, - rhs.view_identifier); + return lhs.surface_size == rhs.surface_size; } }; }; @@ -107,9 +102,10 @@ class EmbedderExternalView { SkISize GetRenderSurfaceSize() const; - bool Render(const EmbedderRenderTarget& render_target); + bool Render(const EmbedderRenderTarget& render_target, + bool clear_surface = true); - std::list GetEngineRenderedContentsRegion(const SkRect& query) const; + const DlRegion& GetDlRegion() const; private: // End the recording of the slice. diff --git a/shell/platform/embedder/embedder_external_view_embedder.cc b/shell/platform/embedder/embedder_external_view_embedder.cc index 94c770f809277..f0462b19c2068 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.cc +++ b/shell/platform/embedder/embedder_external_view_embedder.cc @@ -4,7 +4,7 @@ #include "flutter/shell/platform/embedder/embedder_external_view_embedder.h" -#include +#include #include #include "flutter/shell/platform/embedder/embedder_layers.h" @@ -13,6 +13,8 @@ namespace flutter { +static const auto kRootViewIdentifier = EmbedderExternalView::ViewIdentifier{}; + EmbedderExternalViewEmbedder::EmbedderExternalViewEmbedder( bool avoid_backing_store_cache, const CreateRenderTargetCallback& create_render_target_callback, @@ -61,9 +63,6 @@ void EmbedderExternalViewEmbedder::BeginFrame( pending_device_pixel_ratio_ = device_pixel_ratio; pending_surface_transformation_ = GetSurfaceTransformation(); - static const auto kRootViewIdentifier = - EmbedderExternalView::ViewIdentifier{}; - pending_views_[kRootViewIdentifier] = std::make_unique( pending_frame_size_, pending_surface_transformation_); composition_order_.push_back(kRootViewIdentifier); @@ -87,7 +86,7 @@ void EmbedderExternalViewEmbedder::PrerollCompositeEmbeddedView( // |ExternalViewEmbedder| DlCanvas* EmbedderExternalViewEmbedder::GetRootCanvas() { - auto found = pending_views_.find(EmbedderExternalView::ViewIdentifier{}); + auto found = pending_views_.find(kRootViewIdentifier); if (found == pending_views_.end()) { FML_DLOG(WARNING) << "No root canvas could be found. This is extremely unlikely and " @@ -122,13 +121,320 @@ static FlutterBackingStoreConfig MakeBackingStoreConfig( return config; } -// |ExternalViewEmbedder| +namespace { + +struct PlatformView { + EmbedderExternalView::ViewIdentifier view_identifier; + const EmbeddedViewParams* params; + + // The frame of the platform view, after clipping, in screen coordinates. + SkRect clipped_frame; + + explicit PlatformView(const EmbedderExternalView* view) { + FML_DCHECK(view->HasPlatformView()); + view_identifier = view->GetViewIdentifier(); + params = view->GetEmbeddedViewParams(); + + clipped_frame = view->GetEmbeddedViewParams()->finalBoundingRect(); + SkMatrix transform; + for (auto i = params->mutatorsStack().Begin(); + i != params->mutatorsStack().End(); ++i) { + const auto& m = *i; + switch (m->GetType()) { + case kClipRect: { + auto rect = transform.mapRect(m->GetRect()); + if (!clipped_frame.intersect(rect)) { + clipped_frame = SkRect::MakeEmpty(); + } + break; + } + case kClipRRect: { + auto rect = transform.mapRect(m->GetRRect().getBounds()); + if (!clipped_frame.intersect(rect)) { + clipped_frame = SkRect::MakeEmpty(); + } + break; + } + case kClipPath: { + auto rect = transform.mapRect(m->GetPath().getBounds()); + if (!clipped_frame.intersect(rect)) { + clipped_frame = SkRect::MakeEmpty(); + } + break; + } + case kTransform: { + transform.preConcat(m->GetMatrix()); + break; + } + case kOpacity: + case kBackdropFilter: + break; + } + } + } +}; + +/// Each layer will result in a single physical surface that contains Flutter +/// contents. It may contain multiple platform views and the slices +/// that would be otherwise rendered between these platform views will be +/// collapsed into this layer, as long as they do not intersect any of the +/// platform views. +/// In Z order the Flutter contents of Layer is above the platform views. +class Layer { + public: + /// Returns whether the rectangle intersects any of the platform views of + /// this layer. + bool IntersectsPlatformView(const SkRect& rect) { + for (auto& platform_view : platform_views_) { + if (platform_view.clipped_frame.intersects(rect)) { + return true; + } + } + return false; + } + + /// Returns whether the region intersects any of the platform views of this + /// layer. + bool IntersectsPlatformView(const DlRegion& region) { + for (auto& platform_view : platform_views_) { + if (region.intersects(platform_view.clipped_frame.roundOut())) { + return true; + } + } + return false; + } + + /// Returns whether the rectangle intersects any of the Flutter contents of + /// this layer. + bool IntersectsFlutterContents(const SkRect& rect) { + return flutter_contents_region_.intersects(rect.roundOut()); + } + + /// Returns whether the region intersects any of the Flutter contents of this + /// layer. + bool IntersectsFlutterContents(const DlRegion& region) { + return flutter_contents_region_.intersects(region); + } + + /// Adds a platform view to this layer. + void AddPlatformView(const PlatformView& platform_view) { + platform_views_.push_back(platform_view); + } + + /// Adds Flutter contents to this layer. + void AddFlutterContents(EmbedderExternalView* contents, + const DlRegion& contents_region) { + flutter_contents_.push_back(contents); + flutter_contents_region_ = + DlRegion::MakeUnion(flutter_contents_region_, contents_region); + } + + bool has_flutter_contents() const { return !flutter_contents_.empty(); } + + void SetRenderTarget(std::unique_ptr target) { + FML_DCHECK(render_target_ == nullptr); + FML_DCHECK(has_flutter_contents()); + render_target_ = std::move(target); + } + + /// Renders this layer Flutter contents to the render target previously + /// assigned with SetRenderTarget. + void RenderFlutterContents() { + FML_DCHECK(has_flutter_contents()); + if (render_target_) { + bool clear_surface = true; + for (auto c : flutter_contents_) { + c->Render(*render_target_, clear_surface); + clear_surface = false; + } + } + } + + /// Returns platform views for this layer. In Z-order the platform views are + /// positioned *below* this layer's Flutter contents. + const std::vector& platform_views() const { + return platform_views_; + } + + EmbedderRenderTarget* render_target() { return render_target_.get(); } + + std::vector coverage() { + return flutter_contents_region_.getRects(); + } + + private: + std::vector platform_views_; + std::vector flutter_contents_; + DlRegion flutter_contents_region_; + std::unique_ptr render_target_; + friend class LayerBuilder; +}; + +/// A layout builder is responsible for building an optimized list of Layers +/// from a list of `EmbedderExternalView`s. Single EmbedderExternalView contains +/// at most one platform view and at most one layer of Flutter contents +/// ('slice'). LayerBuilder is responsible for producing as few Layers from the +/// list of EmbedderExternalViews as possible while maintaining identical visual +/// result. +/// +/// Implements https://flutter.dev/go/optimized-platform-view-layers +class LayerBuilder { + public: + explicit LayerBuilder(SkISize frame_size) : frame_size_(frame_size) { + layers_.push_back(Layer()); + } + + /// Adds the platform view and/or flutter contents from the + /// EmbedderExternalView instance. + /// + /// This will try to add the content and platform view to an existing layer + /// if possible. If not, a new layer will be created. + void AddExternalView(EmbedderExternalView* view) { + if (view->HasPlatformView()) { + PlatformView platform_view(view); + AddPlatformView(platform_view); + } + if (view->HasEngineRenderedContents()) { + AddFlutterContents(view); + } + } + + /// Prepares the render targets for all layers that have Flutter contents. + void PrepareBackingStore( + const std::function( + FlutterBackingStoreConfig)>& target_provider) { + auto config = MakeBackingStoreConfig(frame_size_); + for (auto& layer : layers_) { + if (layer.has_flutter_contents()) { + layer.SetRenderTarget(target_provider(config)); + } + } + } + + /// Renders all layers with Flutter contents to their respective render + /// targets. + void Render() { + for (auto& layer : layers_) { + if (layer.has_flutter_contents()) { + layer.RenderFlutterContents(); + } + } + } + + /// Populates EmbedderLayers from layer builder's layers. + void PushLayers(EmbedderLayers& layers) { + for (auto& layer : layers_) { + for (auto& view : layer.platform_views()) { + auto platform_view_id = view.view_identifier.platform_view_id; + if (platform_view_id.has_value()) { + layers.PushPlatformViewLayer(platform_view_id.value(), *view.params); + } + } + if (layer.render_target() != nullptr) { + layers.PushBackingStoreLayer(layer.render_target()->GetBackingStore(), + layer.coverage()); + } + } + } + + /// Removes the render targets from layers and returns them for collection. + std::vector> + ClearAndCollectRenderTargets() { + std::vector> result; + for (auto& layer : layers_) { + if (layer.render_target() != nullptr) { + result.push_back(std::move(layer.render_target_)); + } + } + layers_.clear(); + return result; + } + + private: + void AddPlatformView(PlatformView view) { + GetLayerForPlatformView(view).AddPlatformView(view); + } + + void AddFlutterContents(EmbedderExternalView* contents) { + FML_DCHECK(contents->HasEngineRenderedContents()); + + DlRegion region = contents->GetDlRegion(); + GetLayerForFlutterContentsRegion(region).AddFlutterContents(contents, + region); + } + + /// Returns the deepest layer to which the platform view can be added. That + /// would be (whichever comes first): + /// - First layer from back that has platform view that intersects with this + /// view + /// - Very last layer from back that has surface that doesn't intersect with + /// this. That is because layer content renders on top of the platform view. + Layer& GetLayerForPlatformView(PlatformView view) { + for (auto iter = layers_.rbegin(); iter != layers_.rend(); ++iter) { + // This layer has surface that intersects with this view. That means we + // went one too far and need the layer before this. + if (iter->IntersectsFlutterContents(view.clipped_frame)) { + if (iter == layers_.rbegin()) { + layers_.emplace_back(); + return layers_.back(); + } else { + --iter; + return *iter; + } + } + if (iter->IntersectsPlatformView(view.clipped_frame)) { + return *iter; + } + } + return layers_.front(); + } + + /// Finds layer to which the Flutter content can be added. That would + /// be first layer from back that has any intersection with this region. + Layer& GetLayerForFlutterContentsRegion(const DlRegion& region) { + for (auto iter = layers_.rbegin(); iter != layers_.rend(); ++iter) { + if (iter->IntersectsPlatformView(region) || + iter->IntersectsFlutterContents(region)) { + return *iter; + } + } + return layers_.front(); + } + + std::vector layers_; + SkISize frame_size_; +}; + +}; // namespace + void EmbedderExternalViewEmbedder::SubmitFrame( GrDirectContext* context, const std::shared_ptr& aiks_context, std::unique_ptr frame) { - auto [matched_render_targets, pending_keys] = - render_target_cache_.GetExistingTargetsInCache(pending_views_); + SkRect _rect = SkRect::MakeIWH(pending_frame_size_.width(), + pending_frame_size_.height()); + pending_surface_transformation_.mapRect(&_rect); + + LayerBuilder builder(SkISize::Make(_rect.width(), _rect.height())); + + for (auto view_id : composition_order_) { + auto& view = pending_views_[view_id]; + builder.AddExternalView(view.get()); + } + + builder.PrepareBackingStore([&](FlutterBackingStoreConfig config) { + std::unique_ptr target; + if (!avoid_backing_store_cache_) { + target = render_target_cache_.GetRenderTarget( + EmbedderExternalView::RenderTargetDescriptor( + SkISize{static_cast(config.size.width), + static_cast(config.size.height)})); + } + if (target != nullptr) { + return target; + } + return create_render_target_callback_(context, aiks_context, config); + }); // This is where unused render targets will be collected. Control may flow to // the embedder. Here, the embedder has the opportunity to trample on the @@ -145,44 +451,6 @@ void EmbedderExternalViewEmbedder::SubmitFrame( auto deferred_cleanup_render_targets = render_target_cache_.ClearAllRenderTargetsInCache(); - for (const auto& pending_key : pending_keys) { - const auto& external_view = pending_views_.at(pending_key); - - // If the external view does not have engine rendered contents, it makes no - // sense to ask to embedder to create a render target for us as we don't - // intend to render into it and ask the embedder for presentation anyway. - // Save some memory. - if (!external_view->HasEngineRenderedContents()) { - continue; - } - - // This is the size of render surface we want the embedder to create for - // us. As or right now, this is going to always be equal to the frame size - // post transformation. But, in case optimizations are applied that make - // it so that embedder rendered into surfaces that aren't full screen, - // this assumption will break. So it's just best to ask view for its size - // directly. - const auto render_surface_size = external_view->GetRenderSurfaceSize(); - - const auto backing_store_config = - MakeBackingStoreConfig(render_surface_size); - - // This is where the embedder will create render targets for us. Control - // flow to the embedder makes the engine susceptible to having the embedder - // trample on the OpenGL context. Before any Skia operations are performed, - // the context must be reset. - // - // @warning: Embedder may trample on our OpenGL context here. - auto render_target = create_render_target_callback_(context, aiks_context, - backing_store_config); - - if (!render_target) { - FML_LOG(ERROR) << "Embedder did not return a valid render target."; - return; - } - matched_render_targets[pending_key] = std::move(render_target); - } - // The OpenGL context could have been trampled by the embedder at this point // as it attempted to collect old render targets and create new ones. Tell // Skia to not rely on existing bindings. @@ -190,16 +458,7 @@ void EmbedderExternalViewEmbedder::SubmitFrame( context->resetContext(kAll_GrBackendState); } - // Scribble embedder provide render targets. The order in which we scribble - // into the buffers is irrelevant to the presentation order. - for (const auto& render_target : matched_render_targets) { - if (!pending_views_.at(render_target.first) - ->Render(*render_target.second)) { - FML_LOG(ERROR) - << "Could not render into the embedder supplied render target."; - return; - } - } + builder.Render(); // We are going to be transferring control back over to the embedder there the // context may be trampled upon again. Flush all operations to the underlying @@ -210,51 +469,16 @@ void EmbedderExternalViewEmbedder::SubmitFrame( context->flushAndSubmit(); } - // Submit the scribbled layer to the embedder for presentation. - // - // @warning: Embedder may trample on our OpenGL context here. { + // Submit the scribbled layer to the embedder for presentation. + // + // @warning: Embedder may trample on our OpenGL context here. EmbedderLayers presented_layers(pending_frame_size_, pending_device_pixel_ratio_, pending_surface_transformation_); - // In composition order, submit backing stores and platform views to the - // embedder. - for (const auto& view_id : composition_order_) { - // If the external view has a platform view, ask the emebdder to place it - // before the Flutter rendered contents for that interleaving level. - const auto& external_view = pending_views_.at(view_id); - if (external_view->HasPlatformView()) { - presented_layers.PushPlatformViewLayer( - // Covered by HasPlatformView(). - // NOLINTNEXTLINE(bugprone-unchecked-optional-access) - external_view->GetViewIdentifier() - .platform_view_id.value(), // view id - *external_view->GetEmbeddedViewParams() // view params - ); - } - // If the view has engine rendered contents, ask the embedder to place - // Flutter rendered contents for this interleaving level on top of a - // platform view. - if (external_view->HasEngineRenderedContents()) { - const auto& exteral_render_target = matched_render_targets.at(view_id); - const auto& external_view = pending_views_.at(view_id); - auto rect_list = - external_view->GetEngineRenderedContentsRegion(SkRect::MakeIWH( - pending_frame_size_.width(), pending_frame_size_.height())); - std::vector rects; - rects.reserve(rect_list.size()); - for (const auto& rect : rect_list) { - rects.push_back(rect.roundOut()); - } - presented_layers.PushBackingStoreLayer( - exteral_render_target->GetBackingStore(), rects); - } - } + builder.PushLayers(presented_layers); - // Flush the layer description down to the embedder for presentation. - // - // @warning: Embedder may trample on our OpenGL context here. presented_layers.InvokePresentCallback(present_callback_); } @@ -263,12 +487,10 @@ void EmbedderExternalViewEmbedder::SubmitFrame( // @warning: Embedder may trample on our OpenGL context here. deferred_cleanup_render_targets.clear(); - // Hold all rendered layers in the render target cache for one frame to - // see if they may be reused next frame. - for (auto& render_target : matched_render_targets) { + auto render_targets = builder.ClearAndCollectRenderTargets(); + for (auto& render_target : render_targets) { if (!avoid_backing_store_cache_) { - render_target_cache_.CacheRenderTarget(render_target.first, - std::move(render_target.second)); + render_target_cache_.CacheRenderTarget(std::move(render_target)); } } diff --git a/shell/platform/embedder/embedder_render_target_cache.cc b/shell/platform/embedder/embedder_render_target_cache.cc index 3d0e6b4c99858..ac0da63959c2f 100644 --- a/shell/platform/embedder/embedder_render_target_cache.cc +++ b/shell/platform/embedder/embedder_render_target_cache.cc @@ -10,63 +10,40 @@ EmbedderRenderTargetCache::EmbedderRenderTargetCache() = default; EmbedderRenderTargetCache::~EmbedderRenderTargetCache() = default; -std::pair -EmbedderRenderTargetCache::GetExistingTargetsInCache( - const EmbedderExternalView::PendingViews& pending_views) { - RenderTargets resolved_render_targets; - EmbedderExternalView::ViewIdentifierSet unmatched_identifiers; - - for (const auto& view : pending_views) { - const auto& external_view = view.second; - if (!external_view->HasEngineRenderedContents()) { - continue; - } - auto& compatible_targets = - cached_render_targets_[external_view->CreateRenderTargetDescriptor()]; - if (compatible_targets.empty()) { - unmatched_identifiers.insert(view.first); - } else { - std::unique_ptr target = - std::move(compatible_targets.top()); - compatible_targets.pop(); - resolved_render_targets[view.first] = std::move(target); - } +std::unique_ptr +EmbedderRenderTargetCache::GetRenderTarget( + const EmbedderExternalView::RenderTargetDescriptor& descriptor) { + auto compatible_target = cached_render_targets_.find(descriptor); + if (compatible_target == cached_render_targets_.end()) { + return nullptr; } - return {std::move(resolved_render_targets), std::move(unmatched_identifiers)}; + auto target = std::move(compatible_target->second); + cached_render_targets_.erase(compatible_target); + return target; } std::set> EmbedderRenderTargetCache::ClearAllRenderTargetsInCache() { std::set> cleared_targets; for (auto& targets : cached_render_targets_) { - auto& targets_stack = targets.second; - while (!targets_stack.empty()) { - cleared_targets.emplace(std::move(targets_stack.top())); - targets_stack.pop(); - } + cleared_targets.insert(std::move(targets.second)); } cached_render_targets_.clear(); return cleared_targets; } void EmbedderRenderTargetCache::CacheRenderTarget( - EmbedderExternalView::ViewIdentifier view_identifier, std::unique_ptr target) { if (target == nullptr) { return; } auto desc = EmbedderExternalView::RenderTargetDescriptor{ - view_identifier, target->GetRenderTargetSize()}; - cached_render_targets_[desc].push(std::move(target)); + target->GetRenderTargetSize()}; + cached_render_targets_.insert(std::make_pair(desc, std::move(target))); } size_t EmbedderRenderTargetCache::GetCachedTargetsCount() const { - size_t count = 0; - for (const auto& targets : cached_render_targets_) { - count += targets.second.size(); - } - return count; + return cached_render_targets_.size(); } } // namespace flutter diff --git a/shell/platform/embedder/embedder_render_target_cache.h b/shell/platform/embedder/embedder_render_target_cache.h index 3c595da9bdb17..fb3251a5b9fdf 100644 --- a/shell/platform/embedder/embedder_render_target_cache.h +++ b/shell/platform/embedder/embedder_render_target_cache.h @@ -25,30 +25,22 @@ class EmbedderRenderTargetCache { ~EmbedderRenderTargetCache(); - using RenderTargets = - std::unordered_map, - EmbedderExternalView::ViewIdentifier::Hash, - EmbedderExternalView::ViewIdentifier::Equal>; - - std::pair - GetExistingTargetsInCache( - const EmbedderExternalView::PendingViews& pending_views); + std::unique_ptr GetRenderTarget( + const EmbedderExternalView::RenderTargetDescriptor& descriptor); std::set> ClearAllRenderTargetsInCache(); - void CacheRenderTarget(EmbedderExternalView::ViewIdentifier view_identifier, - std::unique_ptr target); + void CacheRenderTarget(std::unique_ptr target); size_t GetCachedTargetsCount() const; private: - using CachedRenderTargets = - std::unordered_map>, - EmbedderExternalView::RenderTargetDescriptor::Hash, - EmbedderExternalView::RenderTargetDescriptor::Equal>; + using CachedRenderTargets = std::unordered_multimap< + EmbedderExternalView::RenderTargetDescriptor, + std::unique_ptr, + EmbedderExternalView::RenderTargetDescriptor::Hash, + EmbedderExternalView::RenderTargetDescriptor::Equal>; CachedRenderTargets cached_render_targets_; diff --git a/shell/platform/embedder/fixtures/verifyb143464703_soft_noxform.png b/shell/platform/embedder/fixtures/verifyb143464703_soft_noxform.png index e18fc3769b342..35f482b381d22 100644 Binary files a/shell/platform/embedder/fixtures/verifyb143464703_soft_noxform.png and b/shell/platform/embedder/fixtures/verifyb143464703_soft_noxform.png differ diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 377ea7dced8ec..9e6fa5f6e5351 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1297,7 +1297,7 @@ TEST_F(EmbedderTest, VerifyB143464703WithSoftwareBackend) { fml::CountDownLatch latch(1); context.GetCompositor().SetNextPresentCallback( [&](const FlutterLayer** layers, size_t layers_count) { - ASSERT_EQ(layers_count, 3u); + ASSERT_EQ(layers_count, 2u); // Layer 0 (Root) { @@ -1345,36 +1345,6 @@ TEST_F(EmbedderTest, VerifyB143464703WithSoftwareBackend) { ASSERT_EQ(*layers[1], layer); } - // Layer 2 - { - FlutterBackingStore backing_store = *layers[2]->backing_store; - backing_store.type = kFlutterBackingStoreTypeSoftware; - backing_store.did_update = true; - - FlutterRect paint_region_rects[] = { - FlutterRectMakeLTRB(135, 0, 1024, 60), - }; - FlutterRegion paint_region = { - .struct_size = sizeof(FlutterRegion), - .rects_count = 1, - .rects = paint_region_rects, - }; - FlutterBackingStorePresentInfo present_info = { - .struct_size = sizeof(FlutterBackingStorePresentInfo), - .paint_region = &paint_region, - }; - - FlutterLayer layer = {}; - layer.struct_size = sizeof(layer); - layer.type = kFlutterLayerContentTypeBackingStore; - layer.backing_store = &backing_store; - layer.size = FlutterSizeMake(1024.0, 600.0); - layer.offset = FlutterPointMake(0.0, 0.0); - layer.backing_store_present_info = &present_info; - - ASSERT_EQ(*layers[2], layer); - } - latch.CountDown(); });