Flutter Engine
 
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 }
180 break;
181 }
182 }
183 clipped_frame = ToSkRect(clip);
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 auto clipped_frame = ToDlIRect(platform_view.clipped_frame.roundOut());
211 if (region.intersects(clipped_frame)) {
212 return true;
213 }
214 }
215 return false;
216 }
217
218 /// Returns whether the rectangle intersects any of the Flutter contents of
219 /// this layer.
220 bool IntersectsFlutterContents(const SkRect& rect) {
221 return flutter_contents_region_.intersects(ToDlIRect(rect.roundOut()));
222 }
223
224 /// Returns whether the region intersects any of the Flutter contents of this
225 /// layer.
226 bool IntersectsFlutterContents(const DlRegion& region) {
227 return flutter_contents_region_.intersects(region);
228 }
229
230 /// Adds a platform view to this layer.
231 void AddPlatformView(const PlatformView& platform_view) {
232 platform_views_.push_back(platform_view);
233 }
234
235 /// Adds Flutter contents to this layer.
236 void AddFlutterContents(EmbedderExternalView* contents,
237 const DlRegion& contents_region) {
238 flutter_contents_.push_back(contents);
239 flutter_contents_region_ =
240 DlRegion::MakeUnion(flutter_contents_region_, contents_region);
241 }
242
243 bool has_flutter_contents() const { return !flutter_contents_.empty(); }
244
245 void SetRenderTarget(std::unique_ptr<EmbedderRenderTarget> target) {
246 FML_DCHECK(render_target_ == nullptr);
247 FML_DCHECK(has_flutter_contents());
248 render_target_ = std::move(target);
249 }
250
251 /// Renders this layer Flutter contents to the render target previously
252 /// assigned with SetRenderTarget.
253 void RenderFlutterContents() {
254 FML_DCHECK(has_flutter_contents());
255 if (render_target_) {
256 bool clear_surface = true;
257 for (auto c : flutter_contents_) {
258 c->Render(*render_target_, clear_surface);
259 clear_surface = false;
260 }
261 }
262 }
263
264 /// Returns platform views for this layer. In Z-order the platform views are
265 /// positioned *below* this layer's Flutter contents.
266 const std::vector<PlatformView>& platform_views() const {
267 return platform_views_;
268 }
269
270 EmbedderRenderTarget* render_target() { return render_target_.get(); }
271
272 std::vector<DlIRect> coverage() {
273 return flutter_contents_region_.getRects();
274 }
275
276 private:
277 std::vector<PlatformView> platform_views_;
278 std::vector<EmbedderExternalView*> flutter_contents_;
279 DlRegion flutter_contents_region_;
280 std::unique_ptr<EmbedderRenderTarget> render_target_;
281 friend class LayerBuilder;
282};
283
284/// A layout builder is responsible for building an optimized list of Layers
285/// from a list of `EmbedderExternalView`s. Single EmbedderExternalView contains
286/// at most one platform view and at most one layer of Flutter contents
287/// ('slice'). LayerBuilder is responsible for producing as few Layers from the
288/// list of EmbedderExternalViews as possible while maintaining identical visual
289/// result.
290///
291/// Implements https://flutter.dev/go/optimized-platform-view-layers
292class LayerBuilder {
293 public:
294 using RenderTargetProvider =
295 std::function<std::unique_ptr<EmbedderRenderTarget>(
296 const DlISize& frame_size)>;
297
298 explicit LayerBuilder(DlISize frame_size) : frame_size_(frame_size) {
299 layers_.push_back(Layer());
300 }
301
302 /// Adds the platform view and/or flutter contents from the
303 /// EmbedderExternalView instance.
304 ///
305 /// This will try to add the content and platform view to an existing layer
306 /// if possible. If not, a new layer will be created.
307 void AddExternalView(EmbedderExternalView* view) {
308 if (view->HasPlatformView()) {
309 PlatformView platform_view(view);
310 AddPlatformView(platform_view);
311 }
312 if (view->HasEngineRenderedContents()) {
313 AddFlutterContents(view);
314 }
315 }
316
317 /// Prepares the render targets for all layers that have Flutter contents.
318 void PrepareBackingStore(const RenderTargetProvider& target_provider) {
319 for (auto& layer : layers_) {
320 if (layer.has_flutter_contents()) {
321 layer.SetRenderTarget(target_provider(frame_size_));
322 }
323 }
324 }
325
326 /// Renders all layers with Flutter contents to their respective render
327 /// targets.
328 void Render() {
329 for (auto& layer : layers_) {
330 if (layer.has_flutter_contents()) {
331 layer.RenderFlutterContents();
332 }
333 }
334 }
335
336 /// Populates EmbedderLayers from layer builder's layers.
337 void PushLayers(EmbedderLayers& layers) {
338 for (auto& layer : layers_) {
339 for (auto& view : layer.platform_views()) {
340 auto platform_view_id = view.view_identifier.platform_view_id;
341 if (platform_view_id.has_value()) {
342 layers.PushPlatformViewLayer(platform_view_id.value(), *view.params);
343 }
344 }
345 if (layer.render_target() != nullptr) {
346 layers.PushBackingStoreLayer(layer.render_target()->GetBackingStore(),
347 layer.coverage());
348 }
349 }
350 }
351
352 /// Removes the render targets from layers and returns them for collection.
353 std::vector<std::unique_ptr<EmbedderRenderTarget>>
354 ClearAndCollectRenderTargets() {
355 std::vector<std::unique_ptr<EmbedderRenderTarget>> result;
356 for (auto& layer : layers_) {
357 if (layer.render_target() != nullptr) {
358 result.push_back(std::move(layer.render_target_));
359 }
360 }
361 layers_.clear();
362 return result;
363 }
364
365 private:
366 void AddPlatformView(PlatformView view) {
367 GetLayerForPlatformView(view).AddPlatformView(view);
368 }
369
370 void AddFlutterContents(EmbedderExternalView* contents) {
371 FML_DCHECK(contents->HasEngineRenderedContents());
372
373 DlRegion region = contents->GetDlRegion();
374 GetLayerForFlutterContentsRegion(region).AddFlutterContents(contents,
375 region);
376 }
377
378 /// Returns the deepest layer to which the platform view can be added. That
379 /// would be (whichever comes first):
380 /// - First layer from back that has platform view that intersects with this
381 /// view
382 /// - Very last layer from back that has surface that doesn't intersect with
383 /// this. That is because layer content renders on top of the platform view.
384 Layer& GetLayerForPlatformView(PlatformView view) {
385 for (auto iter = layers_.rbegin(); iter != layers_.rend(); ++iter) {
386 // This layer has surface that intersects with this view. That means we
387 // went one too far and need the layer before this.
388 if (iter->IntersectsFlutterContents(view.clipped_frame)) {
389 if (iter == layers_.rbegin()) {
390 layers_.emplace_back();
391 return layers_.back();
392 } else {
393 --iter;
394 return *iter;
395 }
396 }
397 if (iter->IntersectsPlatformView(view.clipped_frame)) {
398 return *iter;
399 }
400 }
401 return layers_.front();
402 }
403
404 /// Finds layer to which the Flutter content can be added. That would
405 /// be first layer from back that has any intersection with this region.
406 Layer& GetLayerForFlutterContentsRegion(const DlRegion& region) {
407 for (auto iter = layers_.rbegin(); iter != layers_.rend(); ++iter) {
408 if (iter->IntersectsPlatformView(region) ||
409 iter->IntersectsFlutterContents(region)) {
410 return *iter;
411 }
412 }
413 return layers_.front();
414 }
415
416 std::vector<Layer> layers_;
417 DlISize frame_size_;
418};
419
420}; // namespace
421
422void EmbedderExternalViewEmbedder::SubmitFlutterView(
423 int64_t flutter_view_id,
424 GrDirectContext* context,
425 const std::shared_ptr<impeller::AiksContext>& aiks_context,
426 std::unique_ptr<SurfaceFrame> frame) {
427 // The unordered_map render_target_cache creates a new entry if the view ID is
428 // unrecognized.
429 EmbedderRenderTargetCache& render_target_cache =
430 render_target_caches_[flutter_view_id];
431 DlRect _rect = DlRect::MakeSize(pending_frame_size_)
432 .TransformAndClipBounds(pending_surface_transformation_);
433
434 LayerBuilder builder(DlIRect::RoundOut(_rect).GetSize());
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 DlISize& 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) {
474 context->resetContext(kAll_GrBackendState);
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 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