Flutter Engine
The Flutter Engine
embedder_external_view_embedder.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "flutter/shell/platform/embedder/embedder_external_view_embedder.h"
6
7#include <cassert>
8#include <utility>
9
10#include "flutter/common/constants.h"
11#include "flutter/shell/platform/embedder/embedder_layers.h"
12#include "flutter/shell/platform/embedder/embedder_render_target.h"
14
15namespace flutter {
16
18
20 bool avoid_backing_store_cache,
21 const CreateRenderTargetCallback& create_render_target_callback,
22 const PresentCallback& present_callback)
23 : avoid_backing_store_cache_(avoid_backing_store_cache),
24 create_render_target_callback_(create_render_target_callback),
25 present_callback_(present_callback) {
26 FML_DCHECK(create_render_target_callback_);
27 FML_DCHECK(present_callback_);
28}
29
31
33 render_target_caches_.erase(view_id);
34}
35
37 SurfaceTransformationCallback surface_transformation_callback) {
38 surface_transformation_callback_ = std::move(surface_transformation_callback);
39}
40
41SkMatrix EmbedderExternalViewEmbedder::GetSurfaceTransformation() const {
42 if (!surface_transformation_callback_) {
43 return SkMatrix{};
44 }
45
46 return surface_transformation_callback_();
47}
48
49void EmbedderExternalViewEmbedder::Reset() {
50 pending_views_.clear();
51 composition_order_.clear();
52}
53
54// |ExternalViewEmbedder|
55void EmbedderExternalViewEmbedder::CancelFrame() {
56 Reset();
57}
58
59// |ExternalViewEmbedder|
60void EmbedderExternalViewEmbedder::BeginFrame(
61 GrDirectContext* context,
62 const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger) {}
63
64// |ExternalViewEmbedder|
65void EmbedderExternalViewEmbedder::PrepareFlutterView(
66 SkISize frame_size,
67 double device_pixel_ratio) {
68 Reset();
69
70 pending_frame_size_ = frame_size;
71 pending_device_pixel_ratio_ = device_pixel_ratio;
72 pending_surface_transformation_ = GetSurfaceTransformation();
73
74 pending_views_[kRootViewIdentifier] = std::make_unique<EmbedderExternalView>(
75 pending_frame_size_, pending_surface_transformation_);
76 composition_order_.push_back(kRootViewIdentifier);
77}
78
79// |ExternalViewEmbedder|
80void EmbedderExternalViewEmbedder::PrerollCompositeEmbeddedView(
81 int64_t view_id,
82 std::unique_ptr<EmbeddedViewParams> params) {
83 auto vid = EmbedderExternalView::ViewIdentifier(view_id);
84 FML_DCHECK(pending_views_.count(vid) == 0);
85
86 pending_views_[vid] = std::make_unique<EmbedderExternalView>(
87 pending_frame_size_, // frame size
88 pending_surface_transformation_, // surface xformation
89 vid, // view identifier
90 std::move(params) // embedded view params
91 );
92 composition_order_.push_back(vid);
93}
94
95// |ExternalViewEmbedder|
96DlCanvas* EmbedderExternalViewEmbedder::GetRootCanvas() {
97 auto found = pending_views_.find(kRootViewIdentifier);
98 if (found == pending_views_.end()) {
99 FML_DLOG(WARNING)
100 << "No root canvas could be found. This is extremely unlikely and "
101 "indicates that the external view embedder did not receive the "
102 "notification to begin the frame.";
103 return nullptr;
104 }
105 return found->second->GetCanvas();
106}
107
108// |ExternalViewEmbedder|
109DlCanvas* EmbedderExternalViewEmbedder::CompositeEmbeddedView(int64_t view_id) {
110 auto vid = EmbedderExternalView::ViewIdentifier(view_id);
111 auto found = pending_views_.find(vid);
112 if (found == pending_views_.end()) {
113 FML_DCHECK(false) << "Attempted to composite a view that was not "
114 "pre-rolled.";
115 return nullptr;
116 }
117 return found->second->GetCanvas();
118}
119
121 int64_t view_id,
122 const SkISize& backing_store_size) {
123 FlutterBackingStoreConfig config = {};
124
125 config.struct_size = sizeof(config);
126
127 config.size.width = backing_store_size.width();
128 config.size.height = backing_store_size.height();
129 config.view_id = view_id;
130
131 return config;
132}
133
134namespace {
135
136struct PlatformView {
137 EmbedderExternalView::ViewIdentifier view_identifier;
138 const EmbeddedViewParams* params;
139
140 // The frame of the platform view, after clipping, in screen coordinates.
142
143 explicit PlatformView(const EmbedderExternalView* view) {
144 FML_DCHECK(view->HasPlatformView());
145 view_identifier = view->GetViewIdentifier();
146 params = view->GetEmbeddedViewParams();
147
148 clipped_frame = view->GetEmbeddedViewParams()->finalBoundingRect();
150 for (auto i = params->mutatorsStack().Begin();
151 i != params->mutatorsStack().End(); ++i) {
152 const auto& m = *i;
153 switch (m->GetType()) {
154 case kClipRect: {
155 auto rect = transform.mapRect(m->GetRect());
158 }
159 break;
160 }
161 case kClipRRect: {
162 auto rect = transform.mapRect(m->GetRRect().getBounds());
165 }
166 break;
167 }
168 case kClipPath: {
169 auto rect = transform.mapRect(m->GetPath().getBounds());
172 }
173 break;
174 }
175 case kTransform: {
176 transform.preConcat(m->GetMatrix());
177 break;
178 }
179 case kOpacity:
180 case kBackdropFilter:
181 break;
182 }
183 }
184 }
185};
186
187/// Each layer will result in a single physical surface that contains Flutter
188/// contents. It may contain multiple platform views and the slices
189/// that would be otherwise rendered between these platform views will be
190/// collapsed into this layer, as long as they do not intersect any of the
191/// platform views.
192/// In Z order the Flutter contents of Layer is above the platform views.
193class Layer {
194 public:
195 /// Returns whether the rectangle intersects any of the platform views of
196 /// this layer.
197 bool IntersectsPlatformView(const SkRect& rect) {
198 for (auto& platform_view : platform_views_) {
199 if (platform_view.clipped_frame.intersects(rect)) {
200 return true;
201 }
202 }
203 return false;
204 }
205
206 /// Returns whether the region intersects any of the platform views of this
207 /// layer.
208 bool IntersectsPlatformView(const DlRegion& region) {
209 for (auto& platform_view : platform_views_) {
210 if (region.intersects(platform_view.clipped_frame.roundOut())) {
211 return true;
212 }
213 }
214 return false;
215 }
216
217 /// Returns whether the rectangle intersects any of the Flutter contents of
218 /// this layer.
219 bool IntersectsFlutterContents(const SkRect& rect) {
220 return flutter_contents_region_.intersects(rect.roundOut());
221 }
222
223 /// Returns whether the region intersects any of the Flutter contents of this
224 /// layer.
225 bool IntersectsFlutterContents(const DlRegion& region) {
226 return flutter_contents_region_.intersects(region);
227 }
228
229 /// Adds a platform view to this layer.
230 void AddPlatformView(const PlatformView& platform_view) {
231 platform_views_.push_back(platform_view);
232 }
233
234 /// Adds Flutter contents to this layer.
235 void AddFlutterContents(EmbedderExternalView* contents,
236 const DlRegion& contents_region) {
237 flutter_contents_.push_back(contents);
238 flutter_contents_region_ =
239 DlRegion::MakeUnion(flutter_contents_region_, contents_region);
240 }
241
242 bool has_flutter_contents() const { return !flutter_contents_.empty(); }
243
244 void SetRenderTarget(std::unique_ptr<EmbedderRenderTarget> target) {
245 FML_DCHECK(render_target_ == nullptr);
246 FML_DCHECK(has_flutter_contents());
247 render_target_ = std::move(target);
248 }
249
250 /// Renders this layer Flutter contents to the render target previously
251 /// assigned with SetRenderTarget.
252 void RenderFlutterContents() {
253 FML_DCHECK(has_flutter_contents());
254 if (render_target_) {
255 bool clear_surface = true;
256 for (auto c : flutter_contents_) {
257 c->Render(*render_target_, clear_surface);
258 clear_surface = false;
259 }
260 }
261 }
262
263 /// Returns platform views for this layer. In Z-order the platform views are
264 /// positioned *below* this layer's Flutter contents.
265 const std::vector<PlatformView>& platform_views() const {
266 return platform_views_;
267 }
268
269 EmbedderRenderTarget* render_target() { return render_target_.get(); }
270
271 std::vector<SkIRect> coverage() {
272 return flutter_contents_region_.getRects();
273 }
274
275 private:
276 std::vector<PlatformView> platform_views_;
277 std::vector<EmbedderExternalView*> flutter_contents_;
278 DlRegion flutter_contents_region_;
279 std::unique_ptr<EmbedderRenderTarget> render_target_;
280 friend class LayerBuilder;
281};
282
283/// A layout builder is responsible for building an optimized list of Layers
284/// from a list of `EmbedderExternalView`s. Single EmbedderExternalView contains
285/// at most one platform view and at most one layer of Flutter contents
286/// ('slice'). LayerBuilder is responsible for producing as few Layers from the
287/// list of EmbedderExternalViews as possible while maintaining identical visual
288/// result.
289///
290/// Implements https://flutter.dev/go/optimized-platform-view-layers
291class LayerBuilder {
292 public:
293 using RenderTargetProvider =
294 std::function<std::unique_ptr<EmbedderRenderTarget>(
295 const SkISize& frame_size)>;
296
297 explicit LayerBuilder(SkISize frame_size) : frame_size_(frame_size) {
298 layers_.push_back(Layer());
299 }
300
301 /// Adds the platform view and/or flutter contents from the
302 /// EmbedderExternalView instance.
303 ///
304 /// This will try to add the content and platform view to an existing layer
305 /// if possible. If not, a new layer will be created.
306 void AddExternalView(EmbedderExternalView* view) {
307 if (view->HasPlatformView()) {
308 PlatformView platform_view(view);
309 AddPlatformView(platform_view);
310 }
311 if (view->HasEngineRenderedContents()) {
312 AddFlutterContents(view);
313 }
314 }
315
316 /// Prepares the render targets for all layers that have Flutter contents.
317 void PrepareBackingStore(const RenderTargetProvider& target_provider) {
318 for (auto& layer : layers_) {
319 if (layer.has_flutter_contents()) {
320 layer.SetRenderTarget(target_provider(frame_size_));
321 }
322 }
323 }
324
325 /// Renders all layers with Flutter contents to their respective render
326 /// targets.
327 void Render() {
328 for (auto& layer : layers_) {
329 if (layer.has_flutter_contents()) {
330 layer.RenderFlutterContents();
331 }
332 }
333 }
334
335 /// Populates EmbedderLayers from layer builder's layers.
336 void PushLayers(EmbedderLayers& layers) {
337 for (auto& layer : layers_) {
338 for (auto& view : layer.platform_views()) {
339 auto platform_view_id = view.view_identifier.platform_view_id;
340 if (platform_view_id.has_value()) {
341 layers.PushPlatformViewLayer(platform_view_id.value(), *view.params);
342 }
343 }
344 if (layer.render_target() != nullptr) {
345 layers.PushBackingStoreLayer(layer.render_target()->GetBackingStore(),
346 layer.coverage());
347 }
348 }
349 }
350
351 /// Removes the render targets from layers and returns them for collection.
352 std::vector<std::unique_ptr<EmbedderRenderTarget>>
353 ClearAndCollectRenderTargets() {
354 std::vector<std::unique_ptr<EmbedderRenderTarget>> result;
355 for (auto& layer : layers_) {
356 if (layer.render_target() != nullptr) {
357 result.push_back(std::move(layer.render_target_));
358 }
359 }
360 layers_.clear();
361 return result;
362 }
363
364 private:
365 void AddPlatformView(PlatformView view) {
366 GetLayerForPlatformView(view).AddPlatformView(view);
367 }
368
369 void AddFlutterContents(EmbedderExternalView* contents) {
370 FML_DCHECK(contents->HasEngineRenderedContents());
371
372 DlRegion region = contents->GetDlRegion();
373 GetLayerForFlutterContentsRegion(region).AddFlutterContents(contents,
374 region);
375 }
376
377 /// Returns the deepest layer to which the platform view can be added. That
378 /// would be (whichever comes first):
379 /// - First layer from back that has platform view that intersects with this
380 /// view
381 /// - Very last layer from back that has surface that doesn't intersect with
382 /// this. That is because layer content renders on top of the platform view.
383 Layer& GetLayerForPlatformView(PlatformView view) {
384 for (auto iter = layers_.rbegin(); iter != layers_.rend(); ++iter) {
385 // This layer has surface that intersects with this view. That means we
386 // went one too far and need the layer before this.
387 if (iter->IntersectsFlutterContents(view.clipped_frame)) {
388 if (iter == layers_.rbegin()) {
389 layers_.emplace_back();
390 return layers_.back();
391 } else {
392 --iter;
393 return *iter;
394 }
395 }
396 if (iter->IntersectsPlatformView(view.clipped_frame)) {
397 return *iter;
398 }
399 }
400 return layers_.front();
401 }
402
403 /// Finds layer to which the Flutter content can be added. That would
404 /// be first layer from back that has any intersection with this region.
405 Layer& GetLayerForFlutterContentsRegion(const DlRegion& region) {
406 for (auto iter = layers_.rbegin(); iter != layers_.rend(); ++iter) {
407 if (iter->IntersectsPlatformView(region) ||
408 iter->IntersectsFlutterContents(region)) {
409 return *iter;
410 }
411 }
412 return layers_.front();
413 }
414
415 std::vector<Layer> layers_;
416 SkISize frame_size_;
417};
418
419}; // namespace
420
421void EmbedderExternalViewEmbedder::SubmitFlutterView(
422 int64_t flutter_view_id,
423 GrDirectContext* context,
424 const std::shared_ptr<impeller::AiksContext>& aiks_context,
425 std::unique_ptr<SurfaceFrame> frame) {
426 // The unordered_map render_target_cache creates a new entry if the view ID is
427 // unrecognized.
428 EmbedderRenderTargetCache& render_target_cache =
429 render_target_caches_[flutter_view_id];
430 SkRect _rect = SkRect::MakeIWH(pending_frame_size_.width(),
431 pending_frame_size_.height());
432 pending_surface_transformation_.mapRect(&_rect);
433
434 LayerBuilder builder(SkISize::Make(_rect.width(), _rect.height()));
435
436 for (auto view_id : composition_order_) {
437 auto& view = pending_views_[view_id];
438 builder.AddExternalView(view.get());
439 }
440
441 builder.PrepareBackingStore([&](const SkISize& frame_size) {
442 if (!avoid_backing_store_cache_) {
443 std::unique_ptr<EmbedderRenderTarget> target =
444 render_target_cache.GetRenderTarget(
445 EmbedderExternalView::RenderTargetDescriptor(frame_size));
446 if (target != nullptr) {
447 return target;
448 }
449 }
450 auto config = MakeBackingStoreConfig(flutter_view_id, frame_size);
451 return create_render_target_callback_(context, aiks_context, config);
452 });
453
454 // This is where unused render targets will be collected. Control may flow
455 // to the embedder. Here, the embedder has the opportunity to trample on the
456 // OpenGL context.
457 //
458 // For optimum performance, we should tell the render target cache to clear
459 // its unused entries before allocating new ones. This collection step
460 // before allocating new render targets ameliorates peak memory usage within
461 // the frame. But, this causes an issue in a known internal embedder. To
462 // work around this issue while that embedder migrates, collection of render
463 // targets is deferred after the presentation.
464 //
465 // @warning: Embedder may trample on our OpenGL context here.
466 auto deferred_cleanup_render_targets =
467 render_target_cache.ClearAllRenderTargetsInCache();
468
469#if !SLIMPELLER
470 // The OpenGL context could have been trampled by the embedder at this point
471 // as it attempted to collect old render targets and create new ones. Tell
472 // Skia to not rely on existing bindings.
473 if (context) {
475 }
476#endif // !SLIMPELLER
477
478 builder.Render();
479
480#if !SLIMPELLER
481 // We are going to be transferring control back over to the embedder there
482 // the context may be trampled upon again. Flush all operations to the
483 // underlying rendering API.
484 //
485 // @warning: Embedder may trample on our OpenGL context here.
486 if (context) {
487 context->flushAndSubmit();
488 }
489#endif // !SLIMPELLER
490
491 {
492 auto presentation_time_optional = frame->submit_info().presentation_time;
493 uint64_t presentation_time =
494 presentation_time_optional.has_value()
495 ? presentation_time_optional->ToEpochDelta().ToNanoseconds()
496 : 0;
497
498 // Submit the scribbled layer to the embedder for presentation.
499 //
500 // @warning: Embedder may trample on our OpenGL context here.
501 EmbedderLayers presented_layers(
502 pending_frame_size_, pending_device_pixel_ratio_,
503 pending_surface_transformation_, presentation_time);
504
505 builder.PushLayers(presented_layers);
506
507 presented_layers.InvokePresentCallback(flutter_view_id, present_callback_);
508 }
509
510 // See why this is necessary in the comment where this collection in
511 // realized.
512 //
513 // @warning: Embedder may trample on our OpenGL context here.
514 deferred_cleanup_render_targets.clear();
515
516 auto render_targets = builder.ClearAndCollectRenderTargets();
517 for (auto& render_target : render_targets) {
518 if (!avoid_backing_store_cache_) {
519 render_target_cache.CacheRenderTarget(std::move(render_target));
520 }
521 }
522
523 frame->Submit();
524}
525
526} // namespace flutter
std::unique_ptr< flutter::PlatformViewIOS > platform_view
static const uint32_t kAll_GrBackendState
Definition: GrTypes.h:176
void resetContext(uint32_t state=kAll_GrBackendState)
void flushAndSubmit(GrSyncCpu sync=GrSyncCpu::kNo)
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkMatrix.cpp:1141
bool intersects(const SkIRect &rect) const
Definition: SkRegion.cpp:502
static DlRegion MakeUnion(const DlRegion &a, const DlRegion &b)
Definition: dl_region.cc:405
EmbedderExternalViewEmbedder(bool avoid_backing_store_cache, const CreateRenderTargetCallback &create_render_target_callback, const PresentCallback &present_callback)
Creates an external view embedder used by the generic embedder API.
std::function< SkMatrix(void)> SurfaceTransformationCallback
~EmbedderExternalViewEmbedder() override
Collects the external view embedder.
std::function< bool(FlutterViewId view_id, const std::vector< const FlutterLayer * > &layers)> PresentCallback
std::function< std::unique_ptr< EmbedderRenderTarget >(GrDirectContext *context, const std::shared_ptr< impeller::AiksContext > &aiks_context, const FlutterBackingStoreConfig &config)> CreateRenderTargetCallback
void SetSurfaceTransformationCallback(SurfaceTransformationCallback surface_transformation_callback)
Sets the surface transformation callback used by the external view embedder to ask the platform for t...
PlatformView(Delegate &delegate, const TaskRunners &task_runners)
Creates a platform view with the specified delegate and task runner. The base class by itself does no...
EmbedderExternalView::ViewIdentifier view_identifier
const EmbeddedViewParams * params
double frame
Definition: examples.cpp:31
GAsyncResult * result
uint32_t * target
#define FML_DLOG(severity)
Definition: logging.h:102
#define FML_DCHECK(condition)
Definition: logging.h:103
ClipOpAndAA opAA SkRegion region
Definition: SkRecords.h:238
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
@ kBackdropFilter
static FlutterBackingStoreConfig MakeBackingStoreConfig(int64_t view_id, const SkISize &backing_store_size)
static const auto kRootViewIdentifier
flutter::DlCanvas DlCanvas
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition: p3.cpp:47
FlutterViewId view_id
Definition: embedder.h:1787
size_t struct_size
The size of this struct. Must be sizeof(FlutterBackingStoreConfig).
Definition: embedder.h:1782
FlutterSize size
The size of the render target the engine expects to render into.
Definition: embedder.h:1784
double height
Definition: embedder.h:425
double width
Definition: embedder.h:424
Definition: SkSize.h:16
static constexpr SkISize Make(int32_t w, int32_t h)
Definition: SkSize.h:20
constexpr int32_t width() const
Definition: SkSize.h:36
constexpr int32_t height() const
Definition: SkSize.h:37
static constexpr SkRect MakeEmpty()
Definition: SkRect.h:595
bool intersect(const SkRect &r)
Definition: SkRect.cpp:114
static SkRect MakeIWH(int w, int h)
Definition: SkRect.h:623
constexpr float height() const
Definition: SkRect.h:769
constexpr float width() const
Definition: SkRect.h:762