diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index b80d4ac5b606b..f8cba6e7c66a6 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -126,8 +126,6 @@ FILE: ../../../flutter/fml/file_unittest.cc FILE: ../../../flutter/fml/gpu_thread_merger.cc FILE: ../../../flutter/fml/gpu_thread_merger.h FILE: ../../../flutter/fml/gpu_thread_merger_unittests.cc -FILE: ../../../flutter/fml/hash_combine.h -FILE: ../../../flutter/fml/hash_combine_unittests.cc FILE: ../../../flutter/fml/icu_util.cc FILE: ../../../flutter/fml/icu_util.h FILE: ../../../flutter/fml/log_level.h @@ -915,20 +913,15 @@ FILE: ../../../flutter/shell/platform/embedder/embedder_engine.cc FILE: ../../../flutter/shell/platform/embedder/embedder_engine.h FILE: ../../../flutter/shell/platform/embedder/embedder_external_texture_gl.cc FILE: ../../../flutter/shell/platform/embedder/embedder_external_texture_gl.h -FILE: ../../../flutter/shell/platform/embedder/embedder_external_view.cc -FILE: ../../../flutter/shell/platform/embedder/embedder_external_view.h FILE: ../../../flutter/shell/platform/embedder/embedder_external_view_embedder.cc FILE: ../../../flutter/shell/platform/embedder/embedder_external_view_embedder.h FILE: ../../../flutter/shell/platform/embedder/embedder_include.c -FILE: ../../../flutter/shell/platform/embedder/embedder_include2.c FILE: ../../../flutter/shell/platform/embedder/embedder_layers.cc FILE: ../../../flutter/shell/platform/embedder/embedder_layers.h FILE: ../../../flutter/shell/platform/embedder/embedder_platform_message_response.cc FILE: ../../../flutter/shell/platform/embedder/embedder_platform_message_response.h FILE: ../../../flutter/shell/platform/embedder/embedder_render_target.cc FILE: ../../../flutter/shell/platform/embedder/embedder_render_target.h -FILE: ../../../flutter/shell/platform/embedder/embedder_render_target_cache.cc -FILE: ../../../flutter/shell/platform/embedder/embedder_render_target_cache.h FILE: ../../../flutter/shell/platform/embedder/embedder_safe_access.h FILE: ../../../flutter/shell/platform/embedder/embedder_surface.cc FILE: ../../../flutter/shell/platform/embedder/embedder_surface.h diff --git a/fml/BUILD.gn b/fml/BUILD.gn index d8c7a95d682ef..b218025f2450f 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -26,7 +26,6 @@ source_set("fml") { "file.h", "gpu_thread_merger.cc", "gpu_thread_merger.h", - "hash_combine.h", "icu_util.cc", "icu_util.h", "log_level.h", @@ -233,7 +232,6 @@ executable("fml_unittests") { "command_line_unittest.cc", "file_unittest.cc", "gpu_thread_merger_unittests.cc", - "hash_combine_unittests.cc", "memory/ref_counted_unittest.cc", "memory/weak_ptr_unittest.cc", "message_loop_task_queues_merge_unmerge_unittests.cc", diff --git a/fml/hash_combine.h b/fml/hash_combine.h deleted file mode 100644 index e06f7d33f7c13..0000000000000 --- a/fml/hash_combine.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_FML_HASH_COMBINE_H_ -#define FLUTTER_FML_HASH_COMBINE_H_ - -#include - -namespace fml { - -template -constexpr void HashCombineSeed(std::size_t& seed, Type arg) { - seed ^= std::hash{}(arg) + 0x9e3779b9 + (seed << 6) + (seed >> 2); -} - -template -constexpr void HashCombineSeed(std::size_t& seed, - Type arg, - Rest... other_args) { - HashCombineSeed(seed, arg); - HashCombineSeed(seed, other_args...); -} - -constexpr std::size_t HashCombine() { - return 0xdabbad00; -} - -template -constexpr std::size_t HashCombine(Type... args) { - std::size_t seed = HashCombine(); - HashCombineSeed(seed, args...); - return seed; -} - -} // namespace fml - -#endif // FLUTTER_FML_HASH_COMBINE_H_ diff --git a/fml/hash_combine_unittests.cc b/fml/hash_combine_unittests.cc deleted file mode 100644 index 230e35c04dc52..0000000000000 --- a/fml/hash_combine_unittests.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/fml/hash_combine.h" -#include "flutter/testing/testing.h" - -namespace fml { -namespace testing { - -TEST(HashCombineTest, CanHash) { - ASSERT_EQ(HashCombine(), HashCombine()); - ASSERT_EQ(HashCombine("Hello"), HashCombine("Hello")); - ASSERT_NE(HashCombine("Hello"), HashCombine("World")); - ASSERT_EQ(HashCombine("Hello", "World"), HashCombine("Hello", "World")); - ASSERT_NE(HashCombine("World", "Hello"), HashCombine("Hello", "World")); - ASSERT_EQ(HashCombine(12u), HashCombine(12u)); - ASSERT_NE(HashCombine(12u), HashCombine(12.0f)); - ASSERT_EQ(HashCombine('a'), HashCombine('a')); -} - -} // namespace testing -} // namespace fml diff --git a/shell/common/canvas_spy.h b/shell/common/canvas_spy.h index bc9da2bd72ae2..e9b721e3912be 100644 --- a/shell/common/canvas_spy.h +++ b/shell/common/canvas_spy.h @@ -26,8 +26,8 @@ class CanvasSpy { public: CanvasSpy(SkCanvas* target_canvas); - //---------------------------------------------------------------------------- - /// @brief Returns true if any non transparent content has been drawn + //------------------------------------------------------------------------------ + /// @brief Returns true if any non trasnparent content has been drawn /// into /// the spying canvas. Note that this class does tries to detect /// empty canvases but in some cases may return true even for @@ -35,7 +35,7 @@ class CanvasSpy { /// canvas). bool DidDrawIntoCanvas(); - //---------------------------------------------------------------------------- + //------------------------------------------------------------------------------ /// @brief The returned canvas delegate all operations to the target /// canvas /// while spying on them. diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index 7215622912c76..7c49cc2b681f3 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -29,20 +29,15 @@ template("embedder_source_set") { "embedder_engine.h", "embedder_external_texture_gl.cc", "embedder_external_texture_gl.h", - "embedder_external_view.cc", - "embedder_external_view.h", "embedder_external_view_embedder.cc", "embedder_external_view_embedder.h", "embedder_include.c", - "embedder_include2.c", "embedder_layers.cc", "embedder_layers.h", "embedder_platform_message_response.cc", "embedder_platform_message_response.h", "embedder_render_target.cc", "embedder_render_target.h", - "embedder_render_target_cache.cc", - "embedder_render_target_cache.h", "embedder_safe_access.h", "embedder_surface.cc", "embedder_surface.h", diff --git a/shell/platform/embedder/embedder_external_view.cc b/shell/platform/embedder/embedder_external_view.cc deleted file mode 100644 index f940c058297bc..0000000000000 --- a/shell/platform/embedder/embedder_external_view.cc +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/platform/embedder/embedder_external_view.h" -#include "flutter/fml/trace_event.h" -#include "flutter/shell/common/canvas_spy.h" - -namespace flutter { - -static SkISize TransformedSurfaceSize(const SkISize& size, - const SkMatrix& transformation) { - const auto source_rect = SkRect::MakeWH(size.width(), size.height()); - const auto transformed_rect = transformation.mapRect(source_rect); - return SkISize::Make(transformed_rect.width(), transformed_rect.height()); -} - -EmbedderExternalView::EmbedderExternalView( - const SkISize& frame_size, - const SkMatrix& surface_transformation) - : EmbedderExternalView(frame_size, surface_transformation, {}, nullptr) {} - -EmbedderExternalView::EmbedderExternalView( - const SkISize& frame_size, - const SkMatrix& surface_transformation, - ViewIdentifier view_identifier, - std::unique_ptr params) - : render_surface_size_( - TransformedSurfaceSize(frame_size, surface_transformation)), - surface_transformation_(surface_transformation), - view_identifier_(view_identifier), - embedded_view_params_(std::move(params)), - recorder_(std::make_unique()), - canvas_spy_(std::make_unique( - recorder_->beginRecording(frame_size.width(), frame_size.height()))) { -} - -EmbedderExternalView::~EmbedderExternalView() = default; - -EmbedderExternalView::RenderTargetDescriptor -EmbedderExternalView::CreateRenderTargetDescriptor() const { - return {render_surface_size_}; -} - -SkCanvas* EmbedderExternalView::GetCanvas() const { - return canvas_spy_->GetSpyingCanvas(); -} - -SkISize EmbedderExternalView::GetRenderSurfaceSize() const { - return render_surface_size_; -} - -bool EmbedderExternalView::IsRootView() const { - return !HasPlatformView(); -} - -bool EmbedderExternalView::HasPlatformView() const { - return view_identifier_.platform_view_id.has_value(); -} - -bool EmbedderExternalView::HasEngineRenderedContents() const { - return canvas_spy_->DidDrawIntoCanvas(); -} - -EmbedderExternalView::ViewIdentifier EmbedderExternalView::GetViewIdentifier() - const { - return view_identifier_; -} - -const EmbeddedViewParams* EmbedderExternalView::GetEmbeddedViewParams() const { - return embedded_view_params_.get(); -} - -bool EmbedderExternalView::Render(const EmbedderRenderTarget& render_target) { - TRACE_EVENT0("flutter", "EmbedderExternalView::Render"); - - FML_DCHECK(HasEngineRenderedContents()) - << "Unnecessarily asked to render into a render target when there was " - "nothing to render."; - - auto picture = recorder_->finishRecordingAsPicture(); - if (!picture) { - return false; - } - - auto surface = render_target.GetRenderSurface(); - if (!surface) { - return false; - } - - FML_DCHECK(SkISize::Make(surface->width(), surface->height()) == - render_surface_size_); - - auto canvas = surface->getCanvas(); - if (!canvas) { - return false; - } - - canvas->setMatrix(surface_transformation_); - canvas->clear(SK_ColorTRANSPARENT); - canvas->drawPicture(picture); - canvas->flush(); - - return true; -} - -} // namespace flutter diff --git a/shell/platform/embedder/embedder_external_view.h b/shell/platform/embedder/embedder_external_view.h deleted file mode 100644 index f842f3794bc00..0000000000000 --- a/shell/platform/embedder/embedder_external_view.h +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_VIEW_H_ -#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_VIEW_H_ - -#include -#include -#include - -#include "flutter/flow/embedded_views.h" -#include "flutter/fml/hash_combine.h" -#include "flutter/fml/macros.h" -#include "flutter/shell/common/canvas_spy.h" -#include "flutter/shell/platform/embedder/embedder_render_target.h" -#include "third_party/skia/include/core/SkPictureRecorder.h" - -namespace flutter { - -class EmbedderExternalView { - public: - using PlatformViewID = int64_t; - struct ViewIdentifier { - std::optional platform_view_id; - - ViewIdentifier() {} - - ViewIdentifier(PlatformViewID view_id) : platform_view_id(view_id) {} - - struct Hash { - constexpr std::size_t operator()(const ViewIdentifier& desc) const { - if (!desc.platform_view_id.has_value()) { - return fml::HashCombine(); - } - - return fml::HashCombine(desc.platform_view_id.value()); - } - }; - - struct Equal { - constexpr bool operator()(const ViewIdentifier& lhs, - const ViewIdentifier& rhs) const { - return lhs.platform_view_id == rhs.platform_view_id; - } - }; - }; - - struct RenderTargetDescriptor { - SkISize surface_size = SkISize::MakeEmpty(); - - struct Hash { - constexpr std::size_t operator()( - const RenderTargetDescriptor& desc) const { - return fml::HashCombine(desc.surface_size.width(), - desc.surface_size.height()); - } - }; - - struct Equal { - bool operator()(const RenderTargetDescriptor& lhs, - const RenderTargetDescriptor& rhs) const { - return lhs.surface_size == rhs.surface_size; - } - }; - }; - - using ViewIdentifierSet = std::unordered_set; - - using PendingViews = std::unordered_map, - ViewIdentifier::Hash, - ViewIdentifier::Equal>; - - EmbedderExternalView(const SkISize& frame_size, - const SkMatrix& surface_transformation); - - EmbedderExternalView(const SkISize& frame_size, - const SkMatrix& surface_transformation, - ViewIdentifier view_identifier, - std::unique_ptr params); - - ~EmbedderExternalView(); - - bool IsRootView() const; - - bool HasPlatformView() const; - - bool HasEngineRenderedContents() const; - - ViewIdentifier GetViewIdentifier() const; - - const EmbeddedViewParams* GetEmbeddedViewParams() const; - - RenderTargetDescriptor CreateRenderTargetDescriptor() const; - - SkCanvas* GetCanvas() const; - - SkISize GetRenderSurfaceSize() const; - - bool Render(const EmbedderRenderTarget& render_target); - - private: - const SkISize render_surface_size_; - const SkMatrix surface_transformation_; - ViewIdentifier view_identifier_; - std::unique_ptr embedded_view_params_; - std::unique_ptr recorder_; - std::unique_ptr canvas_spy_; - - FML_DISALLOW_COPY_AND_ASSIGN(EmbedderExternalView); -}; - -} // namespace flutter - -#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_VIEW_H_ diff --git a/shell/platform/embedder/embedder_external_view_embedder.cc b/shell/platform/embedder/embedder_external_view_embedder.cc index e9ba4554697f0..fae78b67d7dc7 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.cc +++ b/shell/platform/embedder/embedder_external_view_embedder.cc @@ -8,7 +8,6 @@ #include "flutter/shell/platform/embedder/embedder_layers.h" #include "flutter/shell/platform/embedder/embedder_render_target.h" -#include "third_party/skia/include/gpu/GrContext.h" namespace flutter { @@ -37,7 +36,9 @@ SkMatrix EmbedderExternalViewEmbedder::GetSurfaceTransformation() const { } void EmbedderExternalViewEmbedder::Reset() { - pending_views_.clear(); + pending_recorders_.clear(); + pending_canvas_spies_.clear(); + pending_params_.clear(); composition_order_.clear(); } @@ -46,6 +47,25 @@ void EmbedderExternalViewEmbedder::CancelFrame() { Reset(); } +static FlutterBackingStoreConfig MakeBackingStoreConfig( + const SkISize& backing_store_size) { + FlutterBackingStoreConfig config = {}; + + config.struct_size = sizeof(config); + + config.size.width = backing_store_size.width(); + config.size.height = backing_store_size.height(); + + return config; +} + +static SkISize TransformedSurfaceSize(const SkISize& size, + const SkMatrix& transformation) { + const auto source_rect = SkRect::MakeWH(size.width(), size.height()); + const auto transformed_rect = transformation.mapRect(source_rect); + return SkISize::Make(transformed_rect.width(), transformed_rect.height()); +} + // |ExternalViewEmbedder| void EmbedderExternalViewEmbedder::BeginFrame(SkISize frame_size, GrContext* context, @@ -56,203 +76,212 @@ void EmbedderExternalViewEmbedder::BeginFrame(SkISize frame_size, pending_device_pixel_ratio_ = device_pixel_ratio; pending_surface_transformation_ = GetSurfaceTransformation(); - static const auto kRootViewIdentifier = - EmbedderExternalView::ViewIdentifier{}; - - pending_views_[kRootViewIdentifier] = std::make_unique( + const auto surface_size = TransformedSurfaceSize( pending_frame_size_, pending_surface_transformation_); - composition_order_.push_back(kRootViewIdentifier); + + // Decide if we want to discard the previous root render target. + if (root_render_target_) { + auto surface = root_render_target_->GetRenderSurface(); + // This is unlikely to happen but the embedder could have given the + // rasterizer a render target the previous frame that Skia could not + // materialize into a renderable surface. Discard the target and try again. + if (!surface) { + root_render_target_ = nullptr; + } else { + auto last_surface_size = + SkISize::Make(surface->width(), surface->height()); + if (surface_size != last_surface_size) { + root_render_target_ = nullptr; + } + } + } + + // If there is no root render target, create one now. + // TODO(43778): This should now be moved to be later in the submit call. + if (!root_render_target_) { + root_render_target_ = create_render_target_callback_( + context, MakeBackingStoreConfig(surface_size)); + } + + root_picture_recorder_ = std::make_unique(); + root_picture_recorder_->beginRecording(pending_frame_size_.width(), + pending_frame_size_.height()); } // |ExternalViewEmbedder| void EmbedderExternalViewEmbedder::PrerollCompositeEmbeddedView( int view_id, std::unique_ptr params) { - FML_DCHECK(pending_views_.count(view_id) == 0); - - pending_views_[view_id] = std::make_unique( - pending_frame_size_, // frame size - pending_surface_transformation_, // surface xformation - view_id, // view identifier - std::move(params) // embedded view params - ); + FML_DCHECK(pending_recorders_.count(view_id) == 0); + FML_DCHECK(pending_canvas_spies_.count(view_id) == 0); + FML_DCHECK(pending_params_.count(view_id) == 0); + FML_DCHECK(std::find(composition_order_.begin(), composition_order_.end(), + view_id) == composition_order_.end()); + + pending_recorders_[view_id] = std::make_unique(); + SkCanvas* recording_canvas = pending_recorders_[view_id]->beginRecording( + pending_frame_size_.width(), pending_frame_size_.height()); + pending_canvas_spies_[view_id] = + std::make_unique(recording_canvas); + pending_params_[view_id] = *params; composition_order_.push_back(view_id); } -// |ExternalViewEmbedder| -SkCanvas* EmbedderExternalViewEmbedder::GetRootCanvas() { - auto found = pending_views_.find(EmbedderExternalView::ViewIdentifier{}); - if (found == pending_views_.end()) { - FML_DLOG(WARNING) - << "No root canvas could be found. This is extremely unlikely and " - "indicates that the external view embedder did not receive the " - "notification to begin the frame."; - return nullptr; - } - return found->second->GetCanvas(); -} - // |ExternalViewEmbedder| std::vector EmbedderExternalViewEmbedder::GetCurrentCanvases() { std::vector canvases; - for (const auto& view : pending_views_) { - const auto& external_view = view.second; - // This method (for legacy reasons) expects non-root current canvases. - if (!external_view->IsRootView()) { - canvases.push_back(external_view->GetCanvas()); - } + for (const auto& spy : pending_canvas_spies_) { + canvases.push_back(spy.second->GetSpyingCanvas()); } return canvases; } // |ExternalViewEmbedder| SkCanvas* EmbedderExternalViewEmbedder::CompositeEmbeddedView(int view_id) { - auto found = pending_views_.find(view_id); - if (found == pending_views_.end()) { + auto found = pending_canvas_spies_.find(view_id); + if (found == pending_canvas_spies_.end()) { FML_DCHECK(false) << "Attempted to composite a view that was not " "pre-rolled."; return nullptr; } - return found->second->GetCanvas(); + return found->second->GetSpyingCanvas(); } -static FlutterBackingStoreConfig MakeBackingStoreConfig( - const SkISize& backing_store_size) { - FlutterBackingStoreConfig config = {}; +bool EmbedderExternalViewEmbedder::RenderPictureToRenderTarget( + sk_sp picture, + const EmbedderRenderTarget* render_target) const { + if (!picture || render_target == nullptr) { + return false; + } - config.struct_size = sizeof(config); + auto render_surface = render_target->GetRenderSurface(); - config.size.width = backing_store_size.width(); - config.size.height = backing_store_size.height(); + if (!render_surface) { + return false; + } - return config; + auto render_canvas = render_surface->getCanvas(); + + if (render_canvas == nullptr) { + return false; + } + + render_canvas->setMatrix(pending_surface_transformation_); + render_canvas->clear(SK_ColorTRANSPARENT); + render_canvas->drawPicture(picture); + render_canvas->flush(); + + return true; } // |ExternalViewEmbedder| bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { - auto [matched_render_targets, pending_keys] = - render_target_cache_.GetExistingTargetsInCache(pending_views_); - - // 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 - // OpenGL context. - // - // We tell the render target cache to clear its unused entries before - // allocating new ones. This collection step before allocating new render - // targets ameliorates peak memory usage within the frame. - // - // @warning: Embedder may trample on our OpenGL context here. - 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()) { + Registry render_targets_used; + EmbedderLayers presented_layers(pending_frame_size_, + pending_device_pixel_ratio_, + pending_surface_transformation_); + + if (!root_render_target_) { + FML_LOG(ERROR) + << "Could not acquire the root render target from the embedder."; + return false; + } + + // Copy the contents of the root picture recorder onto the root surface. + if (!RenderPictureToRenderTarget( + root_picture_recorder_->finishRecordingAsPicture(), + root_render_target_.get())) { + FML_LOG(ERROR) << "Could not render into the root render target."; + return false; + } + // The root picture recorder will be reset when a new frame begins. + root_picture_recorder_.reset(); + + { + // The root surface is expressed as a layer. + presented_layers.PushBackingStoreLayer( + root_render_target_->GetBackingStore()); + } + + const auto surface_size = TransformedSurfaceSize( + pending_frame_size_, pending_surface_transformation_); + + for (const auto& view_id : composition_order_) { + FML_DCHECK(pending_recorders_.count(view_id) == 1); + FML_DCHECK(pending_canvas_spies_.count(view_id) == 1); + FML_DCHECK(pending_params_.count(view_id) == 1); + + const auto& params = pending_params_.at(view_id); + auto& recorder = pending_recorders_.at(view_id); + + auto picture = recorder->finishRecordingAsPicture(); + if (!picture) { + FML_LOG(ERROR) << "Could not finish recording into the picture before " + "on-screen composition."; + return false; + } + + // Tell the embedder that a platform view layer is present at this point. + presented_layers.PushPlatformViewLayer(view_id, params); + + if (!pending_canvas_spies_.at(view_id)->DidDrawIntoCanvas()) { + // Nothing was drawn into the overlay canvas, we don't need to tell the + // embedder to composite it. 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, backing_store_config); + const auto backing_store_config = MakeBackingStoreConfig(surface_size); + + RegistryKey registry_key(view_id, backing_store_config); + + auto found_render_target = registry_.find(registry_key); + + // Find a cached render target in the registry. If none exists, ask the + // embedder for a new one. + std::shared_ptr render_target; + if (found_render_target == registry_.end()) { + render_target = + create_render_target_callback_(context, backing_store_config); + } else { + render_target = found_render_target->second; + } if (!render_target) { - FML_LOG(ERROR) << "Embedder did not return a valid render target."; + FML_LOG(ERROR) << "Could not acquire external render target for " + "on-screen composition."; return false; } - 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. - if (context) { - context->resetContext(kAll_GrBackendState); - } + render_targets_used[registry_key] = render_target; - // 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."; + if (!RenderPictureToRenderTarget(picture, render_target.get())) { + FML_LOG(ERROR) << "Could not render into the render target for platform " + "view of identifier " + << view_id; return false; } - } - // 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 - // rendering API. - // - // @warning: Embedder may trample on our OpenGL context here. - if (context) { - context->flush(); + // Indicate a layer for the backing store containing contents rendered by + // Flutter. + presented_layers.PushBackingStoreLayer(render_target->GetBackingStore()); } - // 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( - external_view->GetViewIdentifier() - .platform_view_id.value(), // view id - *external_view->GetEmbeddedViewParams() // view params - ); - } + // Flush the layer description down to the embedder for presentation. + presented_layers.InvokePresentCallback(present_callback_); - // 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); - presented_layers.PushBackingStoreLayer( - exteral_render_target->GetBackingStore()); - } - } + // Keep the previously used render target around in case they are required + // next frame. + registry_ = std::move(render_targets_used); - // Flush the layer description down to the embedder for presentation. - // - // @warning: Embedder may trample on our OpenGL context here. - presented_layers.InvokePresentCallback(present_callback_); - } + return true; +} - // 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) { - render_target_cache_.CacheRenderTarget(std::move(render_target.second)); +// |ExternalViewEmbedder| +SkCanvas* EmbedderExternalViewEmbedder::GetRootCanvas() { + if (!root_picture_recorder_) { + return nullptr; } - - return true; + return root_picture_recorder_->getRecordingCanvas(); } } // namespace flutter diff --git a/shell/platform/embedder/embedder_external_view_embedder.h b/shell/platform/embedder/embedder_external_view_embedder.h index 7000d2cde04cd..16f4a5893843e 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.h +++ b/shell/platform/embedder/embedder_external_view_embedder.h @@ -9,10 +9,10 @@ #include #include "flutter/flow/embedded_views.h" -#include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" -#include "flutter/shell/platform/embedder/embedder_external_view.h" -#include "flutter/shell/platform/embedder/embedder_render_target_cache.h" +#include "flutter/shell/common/canvas_spy.h" +#include "flutter/shell/platform/embedder/embedder_render_target.h" +#include "third_party/skia/include/core/SkPictureRecorder.h" namespace flutter { @@ -95,20 +95,59 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder { SkCanvas* GetRootCanvas() override; private: + using ViewIdentifier = int64_t; + struct RegistryKey { + ViewIdentifier view_identifier = 0; + SkISize size = SkISize::Make(0, 0); + + RegistryKey(ViewIdentifier view_identifier, + const FlutterBackingStoreConfig& config) + : view_identifier(view_identifier), + size(SkISize::Make(config.size.width, config.size.height)) {} + + struct Hash { + constexpr std::size_t operator()(RegistryKey const& key) const { + return key.view_identifier; + }; + }; + + struct Equal { + constexpr bool operator()(const RegistryKey& lhs, + const RegistryKey& rhs) const { + return lhs.view_identifier == rhs.view_identifier && + lhs.size == rhs.size; + } + }; + }; + const CreateRenderTargetCallback create_render_target_callback_; const PresentCallback present_callback_; SurfaceTransformationCallback surface_transformation_callback_; + using Registry = std::unordered_map, + RegistryKey::Hash, + RegistryKey::Equal>; + SkISize pending_frame_size_ = SkISize::Make(0, 0); double pending_device_pixel_ratio_ = 1.0; SkMatrix pending_surface_transformation_; - EmbedderExternalView::PendingViews pending_views_; - std::vector composition_order_; - EmbedderRenderTargetCache render_target_cache_; + std::map> + pending_recorders_; + std::map> pending_canvas_spies_; + std::map pending_params_; + std::vector composition_order_; + std::shared_ptr root_render_target_; + std::unique_ptr root_picture_recorder_; + Registry registry_; void Reset(); SkMatrix GetSurfaceTransformation() const; + bool RenderPictureToRenderTarget( + sk_sp picture, + const EmbedderRenderTarget* render_target) const; + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderExternalViewEmbedder); }; diff --git a/shell/platform/embedder/embedder_include2.c b/shell/platform/embedder/embedder_include2.c deleted file mode 100644 index f03500b4308c1..0000000000000 --- a/shell/platform/embedder/embedder_include2.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/platform/embedder/embedder.h" - -// This file is the same as embedder_include.c and ensures that static methods -// don't end up in public API header. This will cause duplicate symbols when the -// header in imported in the multiple translation units in the embedder. diff --git a/shell/platform/embedder/embedder_layers.cc b/shell/platform/embedder/embedder_layers.cc index 3b634235f820a..d9e86ca1e049c 100644 --- a/shell/platform/embedder/embedder_layers.cc +++ b/shell/platform/embedder/embedder_layers.cc @@ -195,8 +195,9 @@ void EmbedderLayers::PushPlatformViewLayer( layer.size.height = transformed_layer_bounds.height(); presented_layers_.push_back(layer); -} +} // namespace flutter +/// @note Procedure doesn't copy all closures. void EmbedderLayers::InvokePresentCallback( const PresentCallback& callback) const { std::vector presented_layers_pointers; diff --git a/shell/platform/embedder/embedder_render_target_cache.cc b/shell/platform/embedder/embedder_render_target_cache.cc deleted file mode 100644 index 2820dd5b9578f..0000000000000 --- a/shell/platform/embedder/embedder_render_target_cache.cc +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/platform/embedder/embedder_render_target_cache.h" - -namespace flutter { - -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.size() == 0) { - 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); - } - } - return {std::move(resolved_render_targets), std::move(unmatched_identifiers)}; -} - -void EmbedderRenderTargetCache::ClearAllRenderTargetsInCache() { - cached_render_targets_.clear(); -} - -void EmbedderRenderTargetCache::CacheRenderTarget( - std::unique_ptr target) { - if (target == nullptr) { - return; - } - auto surface = target->GetRenderSurface(); - EmbedderExternalView::RenderTargetDescriptor desc{ - SkISize::Make(surface->width(), surface->height())}; - cached_render_targets_[desc].push(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; -} - -} // namespace flutter diff --git a/shell/platform/embedder/embedder_render_target_cache.h b/shell/platform/embedder/embedder_render_target_cache.h deleted file mode 100644 index 52ca9d6b5c8aa..0000000000000 --- a/shell/platform/embedder/embedder_render_target_cache.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_RENDER_TARGET_CACHE_H_ -#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_RENDER_TARGET_CACHE_H_ - -#include -#include -#include - -#include "flutter/fml/macros.h" -#include "flutter/shell/platform/embedder/embedder_external_view.h" - -namespace flutter { - -//------------------------------------------------------------------------------ -/// @brief A cache used to reference render targets that are owned by the -/// embedder but needed by th engine to render a frame. -/// -class EmbedderRenderTargetCache { - public: - EmbedderRenderTargetCache(); - - ~EmbedderRenderTargetCache(); - - using RenderTargets = - std::unordered_map, - EmbedderExternalView::ViewIdentifier::Hash, - EmbedderExternalView::ViewIdentifier::Equal>; - - std::pair - GetExistingTargetsInCache( - const EmbedderExternalView::PendingViews& pending_views); - - void ClearAllRenderTargetsInCache(); - - void CacheRenderTarget(std::unique_ptr target); - - size_t GetCachedTargetsCount() const; - - private: - using CachedRenderTargets = - std::unordered_map>, - EmbedderExternalView::RenderTargetDescriptor::Hash, - EmbedderExternalView::RenderTargetDescriptor::Equal>; - - CachedRenderTargets cached_render_targets_; - - FML_DISALLOW_COPY_AND_ASSIGN(EmbedderRenderTargetCache); -}; - -} // namespace flutter - -#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_RENDER_TARGET_CACHE_H_ diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index 95c25aaefb6c1..6368c25afc913 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -689,43 +689,3 @@ void objects_can_be_posted() { signalNativeCount(port.sendPort.nativePort); } -@pragma('vm:entry-point') -void empty_scene_posts_zero_layers_to_compositor() { - window.onBeginFrame = (Duration duration) { - SceneBuilder builder = SceneBuilder(); - // Should not render anything. - builder.pushClipRect(Rect.fromLTRB(0.0, 0.0, 300.0, 200.0)); - window.render(builder.build()); - }; - window.scheduleFrame(); -} - -@pragma('vm:entry-point') -void compositor_can_post_only_platform_views() { - window.onBeginFrame = (Duration duration) { - SceneBuilder builder = SceneBuilder(); - builder.addPlatformView(42, width: 300.0, height: 200.0); - builder.addPlatformView(24, width: 300.0, height: 200.0); - window.render(builder.build()); - }; - window.scheduleFrame(); -} - -@pragma('vm:entry-point') -void render_targets_are_recycled() { - int frame_count = 0; - window.onBeginFrame = (Duration duration) { - SceneBuilder builder = SceneBuilder(); - for (int i = 0; i < 10; i++) { - builder.addPicture(Offset(0.0, 0.0), CreateGradientBox(Size(30.0, 20.0))); - builder.addPlatformView(42 + i, width: 30.0, height: 20.0); - } - window.render(builder.build()); - window.scheduleFrame(); - frame_count++; - if (frame_count == 8) { - signalNativeTest(); - } - }; - window.scheduleFrame(); -} diff --git a/shell/platform/embedder/tests/embedder_test_compositor.cc b/shell/platform/embedder/tests/embedder_test_compositor.cc index 13c329626b76e..153e5df4d6f80 100644 --- a/shell/platform/embedder/tests/embedder_test_compositor.cc +++ b/shell/platform/embedder/tests/embedder_test_compositor.cc @@ -24,14 +24,6 @@ void EmbedderTestCompositor::SetRenderTargetType(RenderTargetType type) { type_ = type; } -static void InvokeAllCallbacks(const std::vector& callbacks) { - for (const auto& callback : callbacks) { - if (callback) { - callback(); - } - } -} - bool EmbedderTestCompositor::CreateBackingStore( const FlutterBackingStoreConfig* config, FlutterBackingStore* backing_store_out) { @@ -51,8 +43,7 @@ bool EmbedderTestCompositor::CreateBackingStore( return false; } if (success) { - backing_stores_created_++; - InvokeAllCallbacks(on_create_render_target_callbacks_); + backing_stores_count_++; } return success; } @@ -63,8 +54,7 @@ bool EmbedderTestCompositor::CollectBackingStore( // stores. Our user_data is just the canvas from that backing store and does // not need to be explicitly collected. Embedders might have some other state // they want to collect though. - backing_stores_collected_++; - InvokeAllCallbacks(on_collect_render_target_callbacks_); + backing_stores_count_--; return true; } @@ -178,7 +168,6 @@ bool EmbedderTestCompositor::Present(const FlutterLayer** layers, callback(layers, layers_count); } - InvokeAllCallbacks(on_present_callbacks_); return true; } @@ -196,6 +185,7 @@ bool EmbedderTestCompositor::CreateFramebufferRenderSurface( kBottomLeft_GrSurfaceOrigin, // surface origin nullptr, // surface properties false // mipmaps + ); if (!surface) { @@ -332,31 +322,8 @@ void EmbedderTestCompositor::SetPlatformViewRendererCallback( platform_view_renderer_callback_ = callback; } -size_t EmbedderTestCompositor::GetPendingBackingStoresCount() const { - FML_CHECK(backing_stores_created_ >= backing_stores_collected_); - return backing_stores_created_ - backing_stores_collected_; -} - -size_t EmbedderTestCompositor::GetBackingStoresCreatedCount() const { - return backing_stores_created_; -} - -size_t EmbedderTestCompositor::GetBackingStoresCollectedCount() const { - return backing_stores_collected_; -} - -void EmbedderTestCompositor::AddOnCreateRenderTargetCallback( - fml::closure callback) { - on_create_render_target_callbacks_.push_back(callback); -} - -void EmbedderTestCompositor::AddOnCollectRenderTargetCallback( - fml::closure callback) { - on_collect_render_target_callbacks_.push_back(callback); -} - -void EmbedderTestCompositor::AddOnPresentCallback(fml::closure callback) { - on_present_callbacks_.push_back(callback); +size_t EmbedderTestCompositor::GetBackingStoresCount() const { + return backing_stores_count_; } } // namespace testing diff --git a/shell/platform/embedder/tests/embedder_test_compositor.h b/shell/platform/embedder/tests/embedder_test_compositor.h index 25c1173913a5b..418b179e18e9e 100644 --- a/shell/platform/embedder/tests/embedder_test_compositor.h +++ b/shell/platform/embedder/tests/embedder_test_compositor.h @@ -7,7 +7,6 @@ #include -#include "flutter/fml/closure.h" #include "flutter/fml/macros.h" #include "flutter/shell/platform/embedder/embedder.h" #include "third_party/skia/include/gpu/GrContext.h" @@ -58,17 +57,7 @@ class EmbedderTestCompositor { sk_sp GetLastComposition(); - size_t GetPendingBackingStoresCount() const; - - size_t GetBackingStoresCreatedCount() const; - - size_t GetBackingStoresCollectedCount() const; - - void AddOnCreateRenderTargetCallback(fml::closure callback); - - void AddOnCollectRenderTargetCallback(fml::closure callback); - - void AddOnPresentCallback(fml::closure callback); + size_t GetBackingStoresCount() const; private: const SkISize surface_size_; @@ -78,11 +67,8 @@ class EmbedderTestCompositor { PresentCallback next_present_callback_; NextSceneCallback next_scene_callback_; sk_sp last_composition_; - size_t backing_stores_created_ = 0; - size_t backing_stores_collected_ = 0; - std::vector on_create_render_target_callbacks_; - std::vector on_collect_render_target_callbacks_; - std::vector on_present_callbacks_; + // The number of currently allocated backing stores (created - collected). + size_t backing_stores_count_ = 0; bool UpdateOffscrenComposition(const FlutterLayer** layers, size_t layers_count); diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 44cf0f18f8741..3f128e2e56a88 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1793,7 +1793,7 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderWithPlatformLayerOnBottom) { ASSERT_TRUE(ImageMatchesFixture( "compositor_with_platform_layer_on_bottom.png", scene_image)); - ASSERT_EQ(context.GetCompositor().GetPendingBackingStoresCount(), 1u); + ASSERT_EQ(context.GetCompositor().GetBackingStoresCount(), 1u); } //------------------------------------------------------------------------------ @@ -2397,11 +2397,26 @@ TEST_F(EmbedderTest, VerifyB141980393) { context.GetCompositor().SetNextPresentCallback( [&](const FlutterLayer** layers, size_t layers_count) { - ASSERT_EQ(layers_count, 1u); + ASSERT_EQ(layers_count, 2u); // Layer Root { - FlutterPlatformView platform_view = *layers[0]->platform_view; + FlutterLayer layer = {}; + FlutterBackingStore backing_store = *layers[0]->backing_store; + layer.backing_store = &backing_store; + layer.struct_size = sizeof(layer); + layer.type = kFlutterLayerContentTypeBackingStore; + + // Our root surface has been rotated. + layer.size = FlutterSizeMake(600.0, 800.0); + layer.offset = FlutterPointMake(0.0, 0.0); + + ASSERT_EQ(*layers[0], layer); + } + + // Layer 1 + { + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 1337; @@ -2443,7 +2458,7 @@ TEST_F(EmbedderTest, VerifyB141980393) { layer.size = FlutterSizeMake(xformed_platform_view_rect.width(), xformed_platform_view_rect.height()); - ASSERT_EQ(*layers[0], layer); + ASSERT_EQ(*layers[1], layer); } latch.Signal(); @@ -3529,10 +3544,10 @@ TEST_F(EmbedderTest, ClipsAreCorrectlyCalculated) { fml::AutoResetWaitableEvent latch; context.GetCompositor().SetNextPresentCallback( [&](const FlutterLayer** layers, size_t layers_count) { - ASSERT_EQ(layers_count, 2u); + ASSERT_EQ(layers_count, 3u); { - FlutterPlatformView platform_view = *layers[0]->platform_view; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 42; @@ -3543,16 +3558,16 @@ TEST_F(EmbedderTest, ClipsAreCorrectlyCalculated) { layer.size = FlutterSizeMake(300.0, 400.0); layer.offset = FlutterPointMake(0.0, 0.0); - ASSERT_EQ(*layers[0], layer); + ASSERT_EQ(*layers[1], layer); bool clip_assertions_checked = false; // The total transformation on the stack upto the platform view. const auto total_xformation = - GetTotalMutationTransformationMatrix(layers[0]->platform_view); + GetTotalMutationTransformationMatrix(layers[1]->platform_view); FilterMutationsByType( - layers[0]->platform_view, + layers[1]->platform_view, kFlutterPlatformViewMutationTypeClipRect, [&](const auto& mutation) { FlutterRect clip = mutation.clip_rect; @@ -3606,10 +3621,10 @@ TEST_F(EmbedderTest, ComplexClipsAreCorrectlyCalculated) { fml::AutoResetWaitableEvent latch; context.GetCompositor().SetNextPresentCallback( [&](const FlutterLayer** layers, size_t layers_count) { - ASSERT_EQ(layers_count, 2u); + ASSERT_EQ(layers_count, 3u); { - FlutterPlatformView platform_view = *layers[0]->platform_view; + FlutterPlatformView platform_view = *layers[1]->platform_view; platform_view.struct_size = sizeof(platform_view); platform_view.identifier = 42; @@ -3620,7 +3635,7 @@ TEST_F(EmbedderTest, ComplexClipsAreCorrectlyCalculated) { layer.size = FlutterSizeMake(600.0, 1024.0); layer.offset = FlutterPointMake(0.0, -256.0); - ASSERT_EQ(*layers[0], layer); + ASSERT_EQ(*layers[1], layer); const auto** mutations = platform_view.mutations; @@ -3991,146 +4006,5 @@ TEST_F(EmbedderTest, CanPostTaskToAllNativeThreads) { ASSERT_FALSE(engine.is_valid()); } -TEST_F(EmbedderTest, CompositorCanPostZeroLayersForPresentation) { - auto& context = GetEmbedderContext(); - - EmbedderConfigBuilder builder(context); - builder.SetOpenGLRendererConfig(SkISize::Make(300, 200)); - builder.SetCompositor(); - builder.SetDartEntrypoint("empty_scene_posts_zero_layers_to_compositor"); - context.GetCompositor().SetRenderTargetType( - EmbedderTestCompositor::RenderTargetType::kOpenGLTexture); - - fml::AutoResetWaitableEvent latch; - - context.GetCompositor().SetNextPresentCallback( - [&](const FlutterLayer** layers, size_t layers_count) { - ASSERT_EQ(layers_count, 0u); - latch.Signal(); - }); - - auto engine = builder.LaunchEngine(); - - FlutterWindowMetricsEvent event = {}; - event.struct_size = sizeof(event); - event.width = 300; - event.height = 200; - event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); - ASSERT_TRUE(engine.is_valid()); - latch.Wait(); - - ASSERT_EQ(context.GetCompositor().GetPendingBackingStoresCount(), 0u); -} - -TEST_F(EmbedderTest, CompositorCanPostOnlyPlatformViews) { - auto& context = GetEmbedderContext(); - - EmbedderConfigBuilder builder(context); - builder.SetOpenGLRendererConfig(SkISize::Make(300, 200)); - builder.SetCompositor(); - builder.SetDartEntrypoint("compositor_can_post_only_platform_views"); - context.GetCompositor().SetRenderTargetType( - EmbedderTestCompositor::RenderTargetType::kOpenGLTexture); - - fml::AutoResetWaitableEvent latch; - - context.GetCompositor().SetNextPresentCallback( - [&](const FlutterLayer** layers, size_t layers_count) { - ASSERT_EQ(layers_count, 2u); - - // Layer 0 - { - FlutterPlatformView platform_view = *layers[0]->platform_view; - platform_view.struct_size = sizeof(platform_view); - platform_view.identifier = 42; - FlutterLayer layer = {}; - layer.struct_size = sizeof(layer); - layer.type = kFlutterLayerContentTypePlatformView; - layer.platform_view = &platform_view; - layer.size = FlutterSizeMake(300.0, 200.0); - layer.offset = FlutterPointMake(0.0, 0.0); - - ASSERT_EQ(*layers[0], layer); - } - - // Layer 1 - { - FlutterPlatformView platform_view = *layers[1]->platform_view; - platform_view.struct_size = sizeof(platform_view); - platform_view.identifier = 24; - FlutterLayer layer = {}; - layer.struct_size = sizeof(layer); - layer.type = kFlutterLayerContentTypePlatformView; - layer.platform_view = &platform_view; - layer.size = FlutterSizeMake(300.0, 200.0); - layer.offset = FlutterPointMake(0.0, 0.0); - - ASSERT_EQ(*layers[1], layer); - } - latch.Signal(); - }); - - auto engine = builder.LaunchEngine(); - - FlutterWindowMetricsEvent event = {}; - event.struct_size = sizeof(event); - event.width = 300; - event.height = 200; - event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); - ASSERT_TRUE(engine.is_valid()); - latch.Wait(); - - ASSERT_EQ(context.GetCompositor().GetPendingBackingStoresCount(), 0u); -} - -TEST_F(EmbedderTest, CompositorRenderTargetsAreRecycled) { - auto& context = GetEmbedderContext(); - - EmbedderConfigBuilder builder(context); - builder.SetOpenGLRendererConfig(SkISize::Make(300, 200)); - builder.SetCompositor(); - builder.SetDartEntrypoint("render_targets_are_recycled"); - context.GetCompositor().SetRenderTargetType( - EmbedderTestCompositor::RenderTargetType::kOpenGLTexture); - - fml::CountDownLatch latch(2); - - context.AddNativeCallback("SignalNativeTest", - CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { - latch.CountDown(); - })); - - context.GetCompositor().SetNextPresentCallback( - [&](const FlutterLayer** layers, size_t layers_count) { - ASSERT_EQ(layers_count, 20u); - latch.CountDown(); - }); - - auto engine = builder.LaunchEngine(); - ASSERT_TRUE(engine.is_valid()); - - FlutterWindowMetricsEvent event = {}; - event.struct_size = sizeof(event); - event.width = 300; - event.height = 200; - event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); - - latch.Wait(); - ASSERT_EQ(context.GetCompositor().GetPendingBackingStoresCount(), 10u); - ASSERT_EQ(context.GetCompositor().GetBackingStoresCreatedCount(), 10u); - ASSERT_EQ(context.GetCompositor().GetBackingStoresCollectedCount(), 0u); - // Killing the engine should immediately collect all pending render targets. - engine.reset(); - ASSERT_EQ(context.GetCompositor().GetPendingBackingStoresCount(), 0u); - ASSERT_EQ(context.GetCompositor().GetBackingStoresCreatedCount(), 10u); - ASSERT_EQ(context.GetCompositor().GetBackingStoresCollectedCount(), 10u); -} - } // namespace testing } // namespace flutter