mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Revert "Lift restriction that embedders may not trample the render thread OpenGL context in composition callbacks. (#16653)" (flutter/engine#16674)
This reverts commit 277bc6c62bf686874e6e519a8bf5707a5d794cf3.
This commit is contained in:
parent
93dfb82753
commit
79c230fc7b
@ -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
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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 <functional>
|
||||
|
||||
namespace fml {
|
||||
|
||||
template <class Type>
|
||||
constexpr void HashCombineSeed(std::size_t& seed, Type arg) {
|
||||
seed ^= std::hash<Type>{}(arg) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
|
||||
template <class Type, class... Rest>
|
||||
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 <class... Type>
|
||||
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_
|
||||
@ -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
|
||||
@ -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.
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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<EmbeddedViewParams> 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<SkPictureRecorder>()),
|
||||
canvas_spy_(std::make_unique<CanvasSpy>(
|
||||
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
|
||||
@ -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 <optional>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#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<PlatformViewID> 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<ViewIdentifier,
|
||||
ViewIdentifier::Hash,
|
||||
ViewIdentifier::Equal>;
|
||||
|
||||
using PendingViews = std::unordered_map<ViewIdentifier,
|
||||
std::unique_ptr<EmbedderExternalView>,
|
||||
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<EmbeddedViewParams> 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<EmbeddedViewParams> embedded_view_params_;
|
||||
std::unique_ptr<SkPictureRecorder> recorder_;
|
||||
std::unique_ptr<CanvasSpy> canvas_spy_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderExternalView);
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_VIEW_H_
|
||||
@ -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,76 +47,6 @@ void EmbedderExternalViewEmbedder::CancelFrame() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
// |ExternalViewEmbedder|
|
||||
void EmbedderExternalViewEmbedder::BeginFrame(SkISize frame_size,
|
||||
GrContext* context,
|
||||
double device_pixel_ratio) {
|
||||
Reset();
|
||||
|
||||
pending_frame_size_ = 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<EmbedderExternalView>(
|
||||
pending_frame_size_, pending_surface_transformation_);
|
||||
composition_order_.push_back(kRootViewIdentifier);
|
||||
}
|
||||
|
||||
// |ExternalViewEmbedder|
|
||||
void EmbedderExternalViewEmbedder::PrerollCompositeEmbeddedView(
|
||||
int view_id,
|
||||
std::unique_ptr<EmbeddedViewParams> params) {
|
||||
FML_DCHECK(pending_views_.count(view_id) == 0);
|
||||
|
||||
pending_views_[view_id] = std::make_unique<EmbedderExternalView>(
|
||||
pending_frame_size_, // frame size
|
||||
pending_surface_transformation_, // surface xformation
|
||||
view_id, // view identifier
|
||||
std::move(params) // embedded view 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<SkCanvas*> EmbedderExternalViewEmbedder::GetCurrentCanvases() {
|
||||
std::vector<SkCanvas*> 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());
|
||||
}
|
||||
}
|
||||
return canvases;
|
||||
}
|
||||
|
||||
// |ExternalViewEmbedder|
|
||||
SkCanvas* EmbedderExternalViewEmbedder::CompositeEmbeddedView(int view_id) {
|
||||
auto found = pending_views_.find(view_id);
|
||||
if (found == pending_views_.end()) {
|
||||
FML_DCHECK(false) << "Attempted to composite a view that was not "
|
||||
"pre-rolled.";
|
||||
return nullptr;
|
||||
}
|
||||
return found->second->GetCanvas();
|
||||
}
|
||||
|
||||
static FlutterBackingStoreConfig MakeBackingStoreConfig(
|
||||
const SkISize& backing_store_size) {
|
||||
FlutterBackingStoreConfig config = {};
|
||||
@ -128,131 +59,229 @@ static FlutterBackingStoreConfig MakeBackingStoreConfig(
|
||||
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|
|
||||
bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) {
|
||||
auto [matched_render_targets, pending_keys] =
|
||||
render_target_cache_.GetExistingTargetsInCache(pending_views_);
|
||||
void EmbedderExternalViewEmbedder::BeginFrame(SkISize frame_size,
|
||||
GrContext* context,
|
||||
double device_pixel_ratio) {
|
||||
Reset();
|
||||
|
||||
// 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();
|
||||
pending_frame_size_ = frame_size;
|
||||
pending_device_pixel_ratio_ = device_pixel_ratio;
|
||||
pending_surface_transformation_ = GetSurfaceTransformation();
|
||||
|
||||
for (const auto& pending_key : pending_keys) {
|
||||
const auto& external_view = pending_views_.at(pending_key);
|
||||
const auto surface_size = TransformedSurfaceSize(
|
||||
pending_frame_size_, pending_surface_transformation_);
|
||||
|
||||
// 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, backing_store_config);
|
||||
|
||||
if (!render_target) {
|
||||
FML_LOG(ERROR) << "Embedder did not return a valid render target.";
|
||||
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);
|
||||
}
|
||||
|
||||
// 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 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();
|
||||
}
|
||||
|
||||
// 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
|
||||
);
|
||||
}
|
||||
|
||||
// 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());
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Flush the layer description down to the embedder for presentation.
|
||||
//
|
||||
// @warning: Embedder may trample on our OpenGL context here.
|
||||
presented_layers.InvokePresentCallback(present_callback_);
|
||||
}
|
||||
|
||||
// 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));
|
||||
// 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<SkPictureRecorder>();
|
||||
root_picture_recorder_->beginRecording(pending_frame_size_.width(),
|
||||
pending_frame_size_.height());
|
||||
}
|
||||
|
||||
// |ExternalViewEmbedder|
|
||||
void EmbedderExternalViewEmbedder::PrerollCompositeEmbeddedView(
|
||||
int view_id,
|
||||
std::unique_ptr<EmbeddedViewParams> 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<SkPictureRecorder>();
|
||||
SkCanvas* recording_canvas = pending_recorders_[view_id]->beginRecording(
|
||||
pending_frame_size_.width(), pending_frame_size_.height());
|
||||
pending_canvas_spies_[view_id] =
|
||||
std::make_unique<CanvasSpy>(recording_canvas);
|
||||
pending_params_[view_id] = *params;
|
||||
composition_order_.push_back(view_id);
|
||||
}
|
||||
|
||||
// |ExternalViewEmbedder|
|
||||
std::vector<SkCanvas*> EmbedderExternalViewEmbedder::GetCurrentCanvases() {
|
||||
std::vector<SkCanvas*> canvases;
|
||||
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_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->GetSpyingCanvas();
|
||||
}
|
||||
|
||||
bool EmbedderExternalViewEmbedder::RenderPictureToRenderTarget(
|
||||
sk_sp<SkPicture> picture,
|
||||
const EmbedderRenderTarget* render_target) const {
|
||||
if (!picture || render_target == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto render_surface = render_target->GetRenderSurface();
|
||||
|
||||
if (!render_surface) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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<EmbedderRenderTarget> 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) << "Could not acquire external render target for "
|
||||
"on-screen composition.";
|
||||
return false;
|
||||
}
|
||||
|
||||
render_targets_used[registry_key] = 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;
|
||||
}
|
||||
|
||||
// Indicate a layer for the backing store containing contents rendered by
|
||||
// Flutter.
|
||||
presented_layers.PushBackingStoreLayer(render_target->GetBackingStore());
|
||||
}
|
||||
|
||||
// Flush the layer description down to the embedder for presentation.
|
||||
presented_layers.InvokePresentCallback(present_callback_);
|
||||
|
||||
// Keep the previously used render target around in case they are required
|
||||
// next frame.
|
||||
registry_ = std::move(render_targets_used);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// |ExternalViewEmbedder|
|
||||
SkCanvas* EmbedderExternalViewEmbedder::GetRootCanvas() {
|
||||
if (!root_picture_recorder_) {
|
||||
return nullptr;
|
||||
}
|
||||
return root_picture_recorder_->getRecordingCanvas();
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -9,10 +9,10 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#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,
|
||||
std::shared_ptr<EmbedderRenderTarget>,
|
||||
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<EmbedderExternalView::ViewIdentifier> composition_order_;
|
||||
EmbedderRenderTargetCache render_target_cache_;
|
||||
std::map<ViewIdentifier, std::unique_ptr<SkPictureRecorder>>
|
||||
pending_recorders_;
|
||||
std::map<ViewIdentifier, std::unique_ptr<CanvasSpy>> pending_canvas_spies_;
|
||||
std::map<ViewIdentifier, EmbeddedViewParams> pending_params_;
|
||||
std::vector<ViewIdentifier> composition_order_;
|
||||
std::shared_ptr<EmbedderRenderTarget> root_render_target_;
|
||||
std::unique_ptr<SkPictureRecorder> root_picture_recorder_;
|
||||
Registry registry_;
|
||||
|
||||
void Reset();
|
||||
|
||||
SkMatrix GetSurfaceTransformation() const;
|
||||
|
||||
bool RenderPictureToRenderTarget(
|
||||
sk_sp<SkPicture> picture,
|
||||
const EmbedderRenderTarget* render_target) const;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderExternalViewEmbedder);
|
||||
};
|
||||
|
||||
|
||||
@ -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.
|
||||
@ -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<const FlutterLayer*> presented_layers_pointers;
|
||||
|
||||
@ -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::RenderTargets,
|
||||
EmbedderExternalView::ViewIdentifierSet>
|
||||
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<EmbedderRenderTarget> 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<EmbedderRenderTarget> 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
|
||||
@ -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 <stack>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
|
||||
#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,
|
||||
std::unique_ptr<EmbedderRenderTarget>,
|
||||
EmbedderExternalView::ViewIdentifier::Hash,
|
||||
EmbedderExternalView::ViewIdentifier::Equal>;
|
||||
|
||||
std::pair<RenderTargets, EmbedderExternalView::ViewIdentifierSet>
|
||||
GetExistingTargetsInCache(
|
||||
const EmbedderExternalView::PendingViews& pending_views);
|
||||
|
||||
void ClearAllRenderTargetsInCache();
|
||||
|
||||
void CacheRenderTarget(std::unique_ptr<EmbedderRenderTarget> target);
|
||||
|
||||
size_t GetCachedTargetsCount() const;
|
||||
|
||||
private:
|
||||
using CachedRenderTargets =
|
||||
std::unordered_map<EmbedderExternalView::RenderTargetDescriptor,
|
||||
std::stack<std::unique_ptr<EmbedderRenderTarget>>,
|
||||
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_
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -24,14 +24,6 @@ void EmbedderTestCompositor::SetRenderTargetType(RenderTargetType type) {
|
||||
type_ = type;
|
||||
}
|
||||
|
||||
static void InvokeAllCallbacks(const std::vector<fml::closure>& 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
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#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<SkImage> 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<SkImage> last_composition_;
|
||||
size_t backing_stores_created_ = 0;
|
||||
size_t backing_stores_collected_ = 0;
|
||||
std::vector<fml::closure> on_create_render_target_callbacks_;
|
||||
std::vector<fml::closure> on_collect_render_target_callbacks_;
|
||||
std::vector<fml::closure> 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);
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user