Flutter Engine
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/android/external_view_embedder/external_view_embedder.h"
6 
7 #include "flutter/fml/trace_event.h"
8 #include "flutter/shell/platform/android/surface/android_surface.h"
9 
10 namespace flutter {
11 
13  std::shared_ptr<AndroidContext> android_context,
14  std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
15  std::shared_ptr<AndroidSurfaceFactory> surface_factory)
17  android_context_(android_context),
18  jni_facade_(jni_facade),
19  surface_factory_(surface_factory),
20  surface_pool_(std::make_unique<SurfacePool>()) {}
21 
22 // |ExternalViewEmbedder|
24  int view_id,
25  std::unique_ptr<EmbeddedViewParams> params) {
26  TRACE_EVENT0("flutter",
27  "AndroidExternalViewEmbedder::PrerollCompositeEmbeddedView");
28 
29  auto rtree_factory = RTreeFactory();
30  view_rtrees_.insert_or_assign(view_id, rtree_factory.getInstance());
31 
32  auto picture_recorder = std::make_unique<SkPictureRecorder>();
33  picture_recorder->beginRecording(SkRect::Make(frame_size_), &rtree_factory);
34 
35  picture_recorders_.insert_or_assign(view_id, std::move(picture_recorder));
36  composition_order_.push_back(view_id);
37  // Update params only if they changed.
38  if (view_params_.count(view_id) == 1 &&
39  view_params_.at(view_id) == *params.get()) {
40  return;
41  }
42  view_params_.insert_or_assign(view_id, EmbeddedViewParams(*params.get()));
43 }
44 
45 // |ExternalViewEmbedder|
47  if (picture_recorders_.count(view_id) == 1) {
48  return picture_recorders_.at(view_id)->getRecordingCanvas();
49  }
50  return nullptr;
51 }
52 
53 // |ExternalViewEmbedder|
55  std::vector<SkCanvas*> canvases;
56  for (size_t i = 0; i < composition_order_.size(); i++) {
57  int64_t view_id = composition_order_[i];
58  canvases.push_back(picture_recorders_.at(view_id)->getRecordingCanvas());
59  }
60  return canvases;
61 }
62 
63 SkRect AndroidExternalViewEmbedder::GetViewRect(int view_id) const {
64  const EmbeddedViewParams& params = view_params_.at(view_id);
65  // TODO(egarciad): The rect should be computed from the mutator stack.
66  // (Clipping is missing)
67  // https://github.com/flutter/flutter/issues/59821
68  return SkRect::MakeXYWH(params.finalBoundingRect().x(), //
69  params.finalBoundingRect().y(), //
70  params.finalBoundingRect().width(), //
71  params.finalBoundingRect().height() //
72  );
73 }
74 
75 // |ExternalViewEmbedder|
77  GrDirectContext* context,
78  std::unique_ptr<SurfaceFrame> frame) {
79  TRACE_EVENT0("flutter", "AndroidExternalViewEmbedder::SubmitFrame");
80 
81  if (!FrameHasPlatformLayers()) {
82  frame->Submit();
83  return;
84  }
85 
86  std::unordered_map<int64_t, std::list<SkRect>> overlay_layers;
87  std::unordered_map<int64_t, sk_sp<SkPicture>> pictures;
88  SkCanvas* background_canvas = frame->SkiaCanvas();
89  auto current_frame_view_count = composition_order_.size();
90 
91  // Restore the clip context after exiting this method since it's changed
92  // below.
93  SkAutoCanvasRestore save(background_canvas, /*doSave=*/true);
94 
95  for (size_t i = 0; i < current_frame_view_count; i++) {
96  int64_t view_id = composition_order_[i];
97 
98  sk_sp<SkPicture> picture =
99  picture_recorders_.at(view_id)->finishRecordingAsPicture();
100  FML_CHECK(picture);
101  pictures.insert({view_id, picture});
102 
103  overlay_layers.insert({view_id, {}});
104 
105  sk_sp<RTree> rtree = view_rtrees_.at(view_id);
106  // Determinate if Flutter UI intersects with any of the previous
107  // platform views stacked by z position.
108  //
109  // This is done by querying the r-tree that holds the records for the
110  // picture recorder corresponding to the flow layers added after a platform
111  // view layer.
112  for (ssize_t j = i; j >= 0; j--) {
113  int64_t current_view_id = composition_order_[j];
114  SkRect current_view_rect = GetViewRect(current_view_id);
115  // Each rect corresponds to a native view that renders Flutter UI.
116  std::list<SkRect> intersection_rects =
117  rtree->searchNonOverlappingDrawnRects(current_view_rect);
118  auto allocation_size = intersection_rects.size();
119 
120  // Limit the number of native views, so it doesn't grow forever.
121  //
122  // In this case, the rects are merged into a single one that is the union
123  // of all the rects.
124  if (allocation_size > kMaxLayerAllocations) {
125  SkRect joined_rect;
126  for (const SkRect& rect : intersection_rects) {
127  joined_rect.join(rect);
128  }
129  intersection_rects.clear();
130  intersection_rects.push_back(joined_rect);
131  }
132  for (SkRect& intersection_rect : intersection_rects) {
133  // Subpixels in the platform may not align with the canvas subpixels.
134  //
135  // To workaround it, round the floating point bounds and make the rect
136  // slighly larger. For example, {0.3, 0.5, 3.1, 4.7} becomes {0, 0, 4,
137  // 5}.
138  intersection_rect.set(intersection_rect.roundOut());
139  overlay_layers.at(view_id).push_back(intersection_rect);
140  // Clip the background canvas, so it doesn't contain any of the pixels
141  // drawn on the overlay layer.
142  background_canvas->clipRect(intersection_rect, SkClipOp::kDifference);
143  }
144  }
145  background_canvas->drawPicture(pictures.at(view_id));
146  }
147  // Submit the background canvas frame before switching the GL context to
148  // the overlay surfaces.
149  //
150  // Skip a frame if the embedding is switching surfaces, and indicate in
151  // `PostPrerollAction` that this frame must be resubmitted.
152  auto should_submit_current_frame = previous_frame_view_count_ > 0;
153  if (should_submit_current_frame) {
154  frame->Submit();
155  }
156 
157  for (int64_t view_id : composition_order_) {
158  SkRect view_rect = GetViewRect(view_id);
159  const EmbeddedViewParams& params = view_params_.at(view_id);
160  // Display the platform view. If it's already displayed, then it's
161  // just positioned and sized.
162  jni_facade_->FlutterViewOnDisplayPlatformView(
163  view_id, //
164  view_rect.x(), //
165  view_rect.y(), //
166  view_rect.width(), //
167  view_rect.height(), //
168  params.sizePoints().width() * device_pixel_ratio_,
169  params.sizePoints().height() * device_pixel_ratio_,
170  params.mutatorsStack() //
171  );
172  for (const SkRect& overlay_rect : overlay_layers.at(view_id)) {
173  std::unique_ptr<SurfaceFrame> frame =
174  CreateSurfaceIfNeeded(context, //
175  view_id, //
176  pictures.at(view_id), //
177  overlay_rect //
178  );
179  if (should_submit_current_frame) {
180  frame->Submit();
181  }
182  }
183  }
184 }
185 
186 // |ExternalViewEmbedder|
187 std::unique_ptr<SurfaceFrame>
188 AndroidExternalViewEmbedder::CreateSurfaceIfNeeded(GrDirectContext* context,
189  int64_t view_id,
190  sk_sp<SkPicture> picture,
191  const SkRect& rect) {
192  std::shared_ptr<OverlayLayer> layer = surface_pool_->GetLayer(
193  context, android_context_, jni_facade_, surface_factory_);
194 
195  std::unique_ptr<SurfaceFrame> frame =
196  layer->surface->AcquireFrame(frame_size_);
197  // Display the overlay surface. If it's already displayed, then it's
198  // just positioned and sized.
199  jni_facade_->FlutterViewDisplayOverlaySurface(layer->id, //
200  rect.x(), //
201  rect.y(), //
202  rect.width(), //
203  rect.height() //
204  );
205  SkCanvas* overlay_canvas = frame->SkiaCanvas();
206  overlay_canvas->clear(SK_ColorTRANSPARENT);
207  // Offset the picture since its absolute position on the scene is determined
208  // by the position of the overlay view.
209  overlay_canvas->translate(-rect.x(), -rect.y());
210  overlay_canvas->drawPicture(picture);
211  return frame;
212 }
213 
214 // |ExternalViewEmbedder|
216  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
217  if (!FrameHasPlatformLayers()) {
219  }
220  if (!raster_thread_merger->IsMerged()) {
221  // The raster thread merger may be disabled if the rasterizer is being
222  // created or teared down.
223  //
224  // In such cases, the current frame is dropped, and a new frame is attempted
225  // with the same layer tree.
226  //
227  // Eventually, the frame is submitted once this method returns `kSuccess`.
228  // At that point, the raster tasks are handled on the platform thread.
229  raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration);
230  CancelFrame();
232  }
233  raster_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration);
234  // Surface switch requires to resubmit the frame.
235  // TODO(egarciad): https://github.com/flutter/flutter/issues/65652
236  if (previous_frame_view_count_ == 0) {
238  }
240 }
241 
242 bool AndroidExternalViewEmbedder::FrameHasPlatformLayers() {
243  return composition_order_.size() > 0;
244 }
245 
246 // |ExternalViewEmbedder|
248  // On Android, the root surface is created from the on-screen render target.
249  return nullptr;
250 }
251 
252 void AndroidExternalViewEmbedder::Reset() {
253  previous_frame_view_count_ = composition_order_.size();
254 
255  composition_order_.clear();
256  picture_recorders_.clear();
257 }
258 
259 // |ExternalViewEmbedder|
261  SkISize frame_size,
262  GrDirectContext* context,
263  double device_pixel_ratio,
264  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
265  Reset();
266 
267  // The surface size changed. Therefore, destroy existing surfaces as
268  // the existing surfaces in the pool can't be recycled.
269  if (frame_size_ != frame_size && raster_thread_merger->IsOnPlatformThread()) {
270  surface_pool_->DestroyLayers(jni_facade_);
271  }
272  surface_pool_->SetFrameSize(frame_size);
273  // JNI method must be called on the platform thread.
274  if (raster_thread_merger->IsOnPlatformThread()) {
275  jni_facade_->FlutterViewBeginFrame();
276  }
277 
278  frame_size_ = frame_size;
279  device_pixel_ratio_ = device_pixel_ratio;
280 }
281 
282 // |ExternalViewEmbedder|
284  Reset();
285 }
286 
287 // |ExternalViewEmbedder|
289  bool should_resubmit_frame,
290  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
291  surface_pool_->RecycleLayers();
292  // JNI method must be called on the platform thread.
293  if (raster_thread_merger->IsOnPlatformThread()) {
294  jni_facade_->FlutterViewEndFrame();
295  }
296 }
297 
298 // |ExternalViewEmbedder|
300  return true;
301 }
302 
303 } // namespace flutter
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:75
PostPrerollResult PostPrerollAction(fml::RefPtr< fml::RasterThreadMerger > raster_thread_merger) override
void BeginFrame(SkISize frame_size, GrDirectContext *context, double device_pixel_ratio, fml::RefPtr< fml::RasterThreadMerger > raster_thread_merger) override
Definition: ref_ptr.h:252
SkCanvas * CompositeEmbeddedView(int view_id) override
void SubmitFrame(GrDirectContext *context, std::unique_ptr< SurfaceFrame > frame) override
void MergeWithLease(size_t lease_term)
const SkSize & sizePoints() const
std::vector< SkCanvas * > GetCurrentCanvases() override
#define FML_CHECK(condition)
Definition: logging.h:68
void PrerollCompositeEmbeddedView(int view_id, std::unique_ptr< flutter::EmbeddedViewParams > params) override
const SkRect & finalBoundingRect() const
void EndFrame(bool should_resubmit_frame, fml::RefPtr< fml::RasterThreadMerger > raster_thread_merger) override
const MutatorsStack & mutatorsStack() const
void ExtendLeaseTo(size_t lease_term)
AndroidExternalViewEmbedder(std::shared_ptr< AndroidContext > android_context, std::shared_ptr< PlatformViewAndroidJNI > jni_facade, std::shared_ptr< AndroidSurfaceFactory > surface_factory)