Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
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
6
7#include <cassert>
8#include <utility>
9
13#include "third_party/skia/include/gpu/ganesh/GrDirectContext.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
41DlMatrix EmbedderExternalViewEmbedder::GetSurfaceTransformation() const {
42 if (!surface_transformation_callback_) {
43 return DlMatrix{};
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 DlISize 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 DlISize& 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 DlRect clip = view->GetEmbeddedViewParams()->finalBoundingRect();
149 DlMatrix matrix;
150 for (auto i = params->mutatorsStack().Begin();
151 i != params->mutatorsStack().End(); ++i) {
152 const auto& m = *i;
153 switch (m->GetType()) {
155 auto rect = m->GetRect().TransformAndClipBounds(matrix);
156 clip = clip.IntersectionOrEmpty(rect);
157 break;
158 }
160 auto rect = m->GetRRect().GetBounds().TransformAndClipBounds(matrix);
161 clip = clip.IntersectionOrEmpty(rect);
162 break;
163 }
165 auto rect = m->GetRSE().GetBounds().TransformAndClipBounds(matrix);
166 clip = clip.IntersectionOrEmpty(rect);
167 break;
168 }
170 auto rect = m->GetPath().GetBounds().TransformAndClipBounds(matrix);
171 clip = clip.IntersectionOrEmpty(rect);
172 break;
173 }
175 matrix = matrix * m->GetMatrix();
176 break;
177 }
184 break;
185 }
186 }
187 clipped_frame = ToSkRect(clip);
188 }
189};
190
191/// Each layer will result in a single physical surface that contains Flutter
192/// contents. It may contain multiple platform views and the slices
193/// that would be otherwise rendered between these platform views will be
194/// collapsed into this layer, as long as they do not intersect any of the
195/// platform views.
196/// In Z order the Flutter contents of Layer is above the platform views.
197class Layer {
198 public:
199 /// Returns whether the rectangle intersects any of the platform views of
200 /// this layer.
201 bool IntersectsPlatformView(const SkRect& rect) {
202 for (auto& platform_view : platform_views_) {
203 if (platform_view.clipped_frame.intersects(rect)) {
204 return true;
205 }
206 }
207 return false;
208 }
209
210 /// Returns whether the region intersects any of the platform views of this
211 /// layer.
212 bool IntersectsPlatformView(const DlRegion& region) {
213 for (auto& platform_view : platform_views_) {
214 auto clipped_frame = ToDlIRect(platform_view.clipped_frame.roundOut());
215 if (region.intersects(clipped_frame)) {
216 return true;
217 }
218 }
219 return false;
220 }
221
222 /// Returns whether the rectangle intersects any of the Flutter contents of
223 /// this layer.
224 bool IntersectsFlutterContents(const SkRect& rect) {
225 return flutter_contents_region_.intersects(ToDlIRect(rect.roundOut()));
226 }
227
228 /// Returns whether the region intersects any of the Flutter contents of this
229 /// layer.
230 bool IntersectsFlutterContents(const DlRegion& region) {
231 return flutter_contents_region_.intersects(region);
232 }
233
234 /// Adds a platform view to this layer.
235 void AddPlatformView(const PlatformView& platform_view) {
236 platform_views_.push_back(platform_view);
237 }
238
239 /// Adds Flutter contents to this layer.
240 void AddFlutterContents(EmbedderExternalView* contents,
241 const DlRegion& contents_region) {
242 flutter_contents_.push_back(contents);
243 flutter_contents_region_ =
244 DlRegion::MakeUnion(flutter_contents_region_, contents_region);
245 }
246
247 bool has_flutter_contents() const { return !flutter_contents_.empty(); }
248
249 void SetRenderTarget(std::unique_ptr<EmbedderRenderTarget> target) {
250 FML_DCHECK(render_target_ == nullptr);
251 FML_DCHECK(has_flutter_contents());
252 render_target_ = std::move(target);
253 }
254
255 /// Renders this layer Flutter contents to the render target previously
256 /// assigned with SetRenderTarget.
257 void RenderFlutterContents() {
258 FML_DCHECK(has_flutter_contents());
259 if (render_target_) {
260 bool clear_surface = true;
261 for (auto c : flutter_contents_) {
262 c->Render(*render_target_, clear_surface);
263 clear_surface = false;
264 }
265 }
266 }
267
268 /// Returns platform views for this layer. In Z-order the platform views are
269 /// positioned *below* this layer's Flutter contents.
270 const std::vector<PlatformView>& platform_views() const {
271 return platform_views_;
272 }
273
274 EmbedderRenderTarget* render_target() { return render_target_.get(); }
275
276 std::vector<DlIRect> coverage() {
277 return flutter_contents_region_.getRects();
278 }
279
280 private:
281 std::vector<PlatformView> platform_views_;
282 std::vector<EmbedderExternalView*> flutter_contents_;
283 DlRegion flutter_contents_region_;
284 std::unique_ptr<EmbedderRenderTarget> render_target_;
285 friend class LayerBuilder;
286};
287
288/// A layout builder is responsible for building an optimized list of Layers
289/// from a list of `EmbedderExternalView`s. Single EmbedderExternalView contains
290/// at most one platform view and at most one layer of Flutter contents
291/// ('slice'). LayerBuilder is responsible for producing as few Layers from the
292/// list of EmbedderExternalViews as possible while maintaining identical visual
293/// result.
294///
295/// Implements https://flutter.dev/go/optimized-platform-view-layers
296class LayerBuilder {
297 public:
298 using RenderTargetProvider =
299 std::function<std::unique_ptr<EmbedderRenderTarget>(
300 const DlISize& frame_size)>;
301
302 explicit LayerBuilder(DlISize frame_size) : frame_size_(frame_size) {
303 layers_.push_back(Layer());
304 }
305
306 /// Adds the platform view and/or flutter contents from the
307 /// EmbedderExternalView instance.
308 ///
309 /// This will try to add the content and platform view to an existing layer
310 /// if possible. If not, a new layer will be created.
311 void AddExternalView(EmbedderExternalView* view) {
312 if (view->HasPlatformView()) {
313 PlatformView platform_view(view);
314 AddPlatformView(platform_view);
315 }
316 if (view->HasEngineRenderedContents()) {
317 AddFlutterContents(view);
318 }
319 }
320
321 /// Prepares the render targets for all layers that have Flutter contents.
322 void PrepareBackingStore(const RenderTargetProvider& target_provider) {
323 for (auto& layer : layers_) {
324 if (layer.has_flutter_contents()) {
325 layer.SetRenderTarget(target_provider(frame_size_));
326 }
327 }
328 }
329
330 /// Renders all layers with Flutter contents to their respective render
331 /// targets.
332 void Render() {
333 for (auto& layer : layers_) {
334 if (layer.has_flutter_contents()) {
335 layer.RenderFlutterContents();
336 }
337 }
338 }
339
340 /// Populates EmbedderLayers from layer builder's layers.
341 void PushLayers(EmbedderLayers& layers) {
342 for (auto& layer : layers_) {
343 for (auto& view : layer.platform_views()) {
344 auto platform_view_id = view.view_identifier.platform_view_id;
345 if (platform_view_id.has_value()) {
346 layers.PushPlatformViewLayer(platform_view_id.value(), *view.params);
347 }
348 }
349 if (layer.render_target() != nullptr) {
350 layers.PushBackingStoreLayer(layer.render_target()->GetBackingStore(),
351 layer.coverage());
352 }
353 }
354 }
355
356 /// Removes the render targets from layers and returns them for collection.
357 std::vector<std::unique_ptr<EmbedderRenderTarget>>
358 ClearAndCollectRenderTargets() {
359 std::vector<std::unique_ptr<EmbedderRenderTarget>> result;
360 for (auto& layer : layers_) {
361 if (layer.render_target() != nullptr) {
362 result.push_back(std::move(layer.render_target_));
363 }
364 }
365 layers_.clear();
366 return result;
367 }
368
369 private:
370 void AddPlatformView(PlatformView view) {
371 GetLayerForPlatformView(view).AddPlatformView(view);
372 }
373
374 void AddFlutterContents(EmbedderExternalView* contents) {
375 FML_DCHECK(contents->HasEngineRenderedContents());
376
377 DlRegion region = contents->GetDlRegion();
378 GetLayerForFlutterContentsRegion(region).AddFlutterContents(contents,
379 region);
380 }
381
382 /// Returns the deepest layer to which the platform view can be added. That
383 /// would be (whichever comes first):
384 /// - First layer from back that has platform view that intersects with this
385 /// view
386 /// - Very last layer from back that has surface that doesn't intersect with
387 /// this. That is because layer content renders on top of the platform view.
388 Layer& GetLayerForPlatformView(PlatformView view) {
389 for (auto iter = layers_.rbegin(); iter != layers_.rend(); ++iter) {
390 // This layer has surface that intersects with this view. That means we
391 // went one too far and need the layer before this.
392 if (iter->IntersectsFlutterContents(view.clipped_frame)) {
393 if (iter == layers_.rbegin()) {
394 layers_.emplace_back();
395 return layers_.back();
396 } else {
397 --iter;
398 return *iter;
399 }
400 }
401 if (iter->IntersectsPlatformView(view.clipped_frame)) {
402 return *iter;
403 }
404 }
405 return layers_.front();
406 }
407
408 /// Finds layer to which the Flutter content can be added. That would
409 /// be first layer from back that has any intersection with this region.
410 Layer& GetLayerForFlutterContentsRegion(const DlRegion& region) {
411 for (auto iter = layers_.rbegin(); iter != layers_.rend(); ++iter) {
412 if (iter->IntersectsPlatformView(region) ||
413 iter->IntersectsFlutterContents(region)) {
414 return *iter;
415 }
416 }
417 return layers_.front();
418 }
419
420 std::vector<Layer> layers_;
421 DlISize frame_size_;
422};
423
424}; // namespace
425
426void EmbedderExternalViewEmbedder::SubmitFlutterView(
427 int64_t flutter_view_id,
428 GrDirectContext* context,
429 const std::shared_ptr<impeller::AiksContext>& aiks_context,
430 std::unique_ptr<SurfaceFrame> frame) {
431 // The unordered_map render_target_cache creates a new entry if the view ID is
432 // unrecognized.
433 EmbedderRenderTargetCache& render_target_cache =
434 render_target_caches_[flutter_view_id];
435 DlRect _rect = DlRect::MakeSize(pending_frame_size_)
436 .TransformAndClipBounds(pending_surface_transformation_);
437
438 LayerBuilder builder(DlIRect::RoundOut(_rect).GetSize());
439
440 for (auto view_id : composition_order_) {
441 auto& view = pending_views_[view_id];
442 builder.AddExternalView(view.get());
443 }
444
445 builder.PrepareBackingStore([&](const DlISize& frame_size) {
446 if (!avoid_backing_store_cache_) {
447 std::unique_ptr<EmbedderRenderTarget> target =
448 render_target_cache.GetRenderTarget(
449 EmbedderExternalView::RenderTargetDescriptor(frame_size));
450 if (target != nullptr) {
451 return target;
452 }
453 }
454 auto config = MakeBackingStoreConfig(flutter_view_id, frame_size);
455 return create_render_target_callback_(context, aiks_context, config);
456 });
457
458 // This is where unused render targets will be collected. Control may flow
459 // to the embedder. Here, the embedder has the opportunity to trample on the
460 // OpenGL context.
461 //
462 // For optimum performance, we should tell the render target cache to clear
463 // its unused entries before allocating new ones. This collection step
464 // before allocating new render targets ameliorates peak memory usage within
465 // the frame. But, this causes an issue in a known internal embedder. To
466 // work around this issue while that embedder migrates, collection of render
467 // targets is deferred after the presentation.
468 //
469 // @warning: Embedder may trample on our OpenGL context here.
470 auto deferred_cleanup_render_targets =
471 render_target_cache.ClearAllRenderTargetsInCache();
472
473#if !SLIMPELLER
474 // The OpenGL context could have been trampled by the embedder at this point
475 // as it attempted to collect old render targets and create new ones. Tell
476 // Skia to not rely on existing bindings.
477 if (context) {
478 context->resetContext(kAll_GrBackendState);
479 }
480#endif // !SLIMPELLER
481
482 builder.Render();
483
484#if !SLIMPELLER
485 // We are going to be transferring control back over to the embedder there
486 // the context may be trampled upon again. Flush all operations to the
487 // underlying rendering API.
488 //
489 // @warning: Embedder may trample on our OpenGL context here.
490 if (context) {
491 context->flushAndSubmit();
492 }
493#endif // !SLIMPELLER
494
495 {
496 auto presentation_time_optional = frame->submit_info().presentation_time;
497 uint64_t presentation_time =
498 presentation_time_optional.has_value()
499 ? presentation_time_optional->ToEpochDelta().ToNanoseconds()
500 : 0;
501
502 // Submit the scribbled layer to the embedder for presentation.
503 //
504 // @warning: Embedder may trample on our OpenGL context here.
505 EmbedderLayers presented_layers(
506 pending_frame_size_, pending_device_pixel_ratio_,
507 pending_surface_transformation_, presentation_time);
508
509 builder.PushLayers(presented_layers);
510
511 presented_layers.InvokePresentCallback(flutter_view_id, present_callback_);
512 }
513
514 // See why this is necessary in the comment where this collection in
515 // realized.
516 //
517 // @warning: Embedder may trample on our OpenGL context here.
518 deferred_cleanup_render_targets.clear();
519
520 auto render_targets = builder.ClearAndCollectRenderTargets();
521 for (auto& render_target : render_targets) {
522 if (!avoid_backing_store_cache_) {
523 render_target_cache.CacheRenderTarget(std::move(render_target));
524 }
525 }
526
527 frame->Submit();
528}
529
530} // namespace flutter
std::unique_ptr< flutter::PlatformViewIOS > platform_view
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.
~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...
std::function< DlMatrix(void)> SurfaceTransformationCallback
EmbedderExternalView::ViewIdentifier view_identifier
const EmbeddedViewParams * params
FlView * view
const FlutterLayer ** layers
uint32_t * target
G_BEGIN_DECLS FlutterViewId view_id
#define FML_DLOG(severity)
Definition logging.h:121
#define FML_DCHECK(condition)
Definition logging.h:122
impeller::Matrix DlMatrix
impeller::Rect DlRect
impeller::ISize32 DlISize
static FlutterBackingStoreConfig MakeBackingStoreConfig(int64_t view_id, const DlISize &backing_store_size)
const DlIRect & ToDlIRect(const SkIRect &rect)
static const auto kRootViewIdentifier
const SkRect & ToSkRect(const DlRect &rect)
flutter::DlCanvas DlCanvas
size_t struct_size
The size of this struct. Must be sizeof(FlutterBackingStoreConfig).
Definition embedder.h:2091
FlutterSize size
The size of the render target the engine expects to render into.
Definition embedder.h:2093
double height
Definition embedder.h:629
double width
Definition embedder.h:628
A 4x4 matrix using column-major storage.
Definition matrix.h:37
RoundOut(const TRect< U > &r)
Definition rect.h:679
constexpr TRect TransformAndClipBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle, clipped against the near clippin...
Definition rect.h:438
static constexpr TRect MakeSize(const TSize< U > &size)
Definition rect.h:150
Type height
Definition size.h:29
Type width
Definition size.h:28