mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
278 lines
10 KiB
C++
278 lines
10 KiB
C++
// 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_embedder.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include "flutter/shell/platform/embedder/embedder_layers.h"
|
|
#include "flutter/shell/platform/embedder/embedder_render_target.h"
|
|
#include "third_party/skia/include/gpu/GrDirectContext.h"
|
|
|
|
namespace flutter {
|
|
|
|
EmbedderExternalViewEmbedder::EmbedderExternalViewEmbedder(
|
|
bool avoid_backing_store_cache,
|
|
const CreateRenderTargetCallback& create_render_target_callback,
|
|
const PresentCallback& present_callback)
|
|
: avoid_backing_store_cache_(avoid_backing_store_cache),
|
|
create_render_target_callback_(create_render_target_callback),
|
|
present_callback_(present_callback) {
|
|
FML_DCHECK(create_render_target_callback_);
|
|
FML_DCHECK(present_callback_);
|
|
}
|
|
|
|
EmbedderExternalViewEmbedder::~EmbedderExternalViewEmbedder() = default;
|
|
|
|
void EmbedderExternalViewEmbedder::SetSurfaceTransformationCallback(
|
|
SurfaceTransformationCallback surface_transformation_callback) {
|
|
surface_transformation_callback_ = surface_transformation_callback;
|
|
}
|
|
|
|
SkMatrix EmbedderExternalViewEmbedder::GetSurfaceTransformation() const {
|
|
if (!surface_transformation_callback_) {
|
|
return SkMatrix{};
|
|
}
|
|
|
|
return surface_transformation_callback_();
|
|
}
|
|
|
|
void EmbedderExternalViewEmbedder::Reset() {
|
|
pending_views_.clear();
|
|
composition_order_.clear();
|
|
}
|
|
|
|
// |ExternalViewEmbedder|
|
|
void EmbedderExternalViewEmbedder::CancelFrame() {
|
|
Reset();
|
|
}
|
|
|
|
// |ExternalViewEmbedder|
|
|
void EmbedderExternalViewEmbedder::BeginFrame(
|
|
SkISize frame_size,
|
|
GrDirectContext* context,
|
|
double device_pixel_ratio,
|
|
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
|
|
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 = {};
|
|
|
|
config.struct_size = sizeof(config);
|
|
|
|
config.size.width = backing_store_size.width();
|
|
config.size.height = backing_store_size.height();
|
|
|
|
return config;
|
|
}
|
|
|
|
// |ExternalViewEmbedder|
|
|
void EmbedderExternalViewEmbedder::SubmitFrame(
|
|
GrDirectContext* context,
|
|
std::unique_ptr<SurfaceFrame> frame,
|
|
const std::shared_ptr<fml::SyncSwitch>& gpu_disable_sync_switch) {
|
|
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.
|
|
//
|
|
// For optimum performance, we should 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. But, this causes an issue in a known internal embedder. To work
|
|
// around this issue while that embedder migrates, collection of render
|
|
// targets is deferred after the presentation.
|
|
//
|
|
// @warning: Embedder may trample on our OpenGL context here.
|
|
auto deferred_cleanup_render_targets =
|
|
render_target_cache_.ClearAllRenderTargetsInCache();
|
|
|
|
for (const auto& pending_key : pending_keys) {
|
|
const auto& external_view = pending_views_.at(pending_key);
|
|
|
|
// If the external view does not have engine rendered contents, it makes no
|
|
// sense to ask to embedder to create a render target for us as we don't
|
|
// intend to render into it and ask the embedder for presentation anyway.
|
|
// Save some memory.
|
|
if (!external_view->HasEngineRenderedContents()) {
|
|
continue;
|
|
}
|
|
|
|
// This is the size of render surface we want the embedder to create for
|
|
// us. As or right now, this is going to always be equal to the frame size
|
|
// post transformation. But, in case optimizations are applied that make
|
|
// it so that embedder rendered into surfaces that aren't full screen,
|
|
// this assumption will break. So it's just best to ask view for its size
|
|
// directly.
|
|
const auto render_surface_size = external_view->GetRenderSurfaceSize();
|
|
|
|
const auto backing_store_config =
|
|
MakeBackingStoreConfig(render_surface_size);
|
|
|
|
// This is where the embedder will create render targets for us. Control
|
|
// flow to the embedder makes the engine susceptible to having the embedder
|
|
// trample on the OpenGL context. Before any Skia operations are performed,
|
|
// the context must be reset.
|
|
//
|
|
// @warning: Embedder may trample on our OpenGL context here.
|
|
auto render_target =
|
|
create_render_target_callback_(context, backing_store_config);
|
|
|
|
if (!render_target) {
|
|
FML_LOG(ERROR) << "Embedder did not return a valid render target.";
|
|
return;
|
|
}
|
|
matched_render_targets[pending_key] = std::move(render_target);
|
|
}
|
|
|
|
// The OpenGL context could have been trampled by the embedder at this point
|
|
// as it attempted to collect old render targets and create new ones. Tell
|
|
// Skia to not rely on existing bindings.
|
|
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;
|
|
}
|
|
}
|
|
|
|
// 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->flushAndSubmit();
|
|
}
|
|
|
|
// 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());
|
|
}
|
|
}
|
|
|
|
// Flush the layer description down to the embedder for presentation.
|
|
//
|
|
// @warning: Embedder may trample on our OpenGL context here.
|
|
presented_layers.InvokePresentCallback(present_callback_);
|
|
}
|
|
|
|
// See why this is necessary in the comment where this collection in realized.
|
|
//
|
|
// @warning: Embedder may trample on our OpenGL context here.
|
|
deferred_cleanup_render_targets.clear();
|
|
|
|
// Hold all rendered layers in the render target cache for one frame to
|
|
// see if they may be reused next frame.
|
|
for (auto& render_target : matched_render_targets) {
|
|
if (!avoid_backing_store_cache_) {
|
|
render_target_cache_.CacheRenderTarget(render_target.first,
|
|
std::move(render_target.second));
|
|
}
|
|
}
|
|
|
|
frame->Submit();
|
|
}
|
|
|
|
} // namespace flutter
|