flutter_flutter/shell/platform/embedder/embedder_external_view_embedder.cc
Adlai Holler c57aff1800
Use the GrDirectContext factories instead of deprecated GrContext ones (#19962)
This is part of a larger effort to expose the difference between GrDirectContext,
which runs on the GPU thread and can directly perform operations like uploading
textures, and GrRecordingContext, which can only queue up work to be delivered
to the GrDirectContext later.
2020-07-28 13:32:09 -07:00

273 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(
const CreateRenderTargetCallback& create_render_target_callback,
const PresentCallback& present_callback)
: 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) {
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) {
render_target_cache_.CacheRenderTarget(render_target.first,
std::move(render_target.second));
}
frame->Submit();
}
} // namespace flutter