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  const 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, 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  sk_sp<RTree> rtree = view_rtrees_.at(view_id);
104  SkRect joined_rect = SkRect::MakeEmpty();
105 
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 
119  // Limit the number of native views, so it doesn't grow forever.
120  //
121  // In this case, the rects are merged into a single one that is the union
122  // of all the rects.
123  for (const SkRect& rect : intersection_rects) {
124  joined_rect.join(rect);
125  }
126  }
127  if (!joined_rect.isEmpty()) {
128  // Subpixels in the platform may not align with the canvas subpixels.
129  //
130  // To workaround it, round the floating point bounds and make the rect
131  // slightly larger.
132  //
133  // For example, {0.3, 0.5, 3.1, 4.7} becomes {0, 0, 4, 5}.
134  joined_rect.set(joined_rect.roundOut());
135  overlay_layers.insert({view_id, joined_rect});
136  // Clip the background canvas, so it doesn't contain any of the pixels
137  // drawn on the overlay layer.
138  background_canvas->clipRect(joined_rect, SkClipOp::kDifference);
139  }
140  background_canvas->drawPicture(pictures.at(view_id));
141  }
142  // Submit the background canvas frame before switching the GL context to
143  // the overlay surfaces.
144  //
145  // Skip a frame if the embedding is switching surfaces, and indicate in
146  // `PostPrerollAction` that this frame must be resubmitted.
147  auto should_submit_current_frame = previous_frame_view_count_ > 0;
148  if (should_submit_current_frame) {
149  frame->Submit();
150  }
151 
152  for (int64_t view_id : composition_order_) {
153  SkRect view_rect = GetViewRect(view_id);
154  const EmbeddedViewParams& params = view_params_.at(view_id);
155  // Display the platform view. If it's already displayed, then it's
156  // just positioned and sized.
157  jni_facade_->FlutterViewOnDisplayPlatformView(
158  view_id, //
159  view_rect.x(), //
160  view_rect.y(), //
161  view_rect.width(), //
162  view_rect.height(), //
163  params.sizePoints().width() * device_pixel_ratio_,
164  params.sizePoints().height() * device_pixel_ratio_,
165  params.mutatorsStack() //
166  );
167  std::unordered_map<int64_t, SkRect>::const_iterator overlay =
168  overlay_layers.find(view_id);
169  if (overlay == overlay_layers.end()) {
170  continue;
171  }
172  std::unique_ptr<SurfaceFrame> frame =
173  CreateSurfaceIfNeeded(context, //
174  view_id, //
175  pictures.at(view_id), //
176  overlay->second //
177  );
178  if (should_submit_current_frame) {
179  frame->Submit();
180  }
181  }
182 }
183 
184 // |ExternalViewEmbedder|
185 std::unique_ptr<SurfaceFrame>
186 AndroidExternalViewEmbedder::CreateSurfaceIfNeeded(GrDirectContext* context,
187  int64_t view_id,
188  sk_sp<SkPicture> picture,
189  const SkRect& rect) {
190  std::shared_ptr<OverlayLayer> layer = surface_pool_->GetLayer(
191  context, android_context_, jni_facade_, surface_factory_);
192 
193  std::unique_ptr<SurfaceFrame> frame =
194  layer->surface->AcquireFrame(frame_size_);
195  // Display the overlay surface. If it's already displayed, then it's
196  // just positioned and sized.
197  jni_facade_->FlutterViewDisplayOverlaySurface(layer->id, //
198  rect.x(), //
199  rect.y(), //
200  rect.width(), //
201  rect.height() //
202  );
203  SkCanvas* overlay_canvas = frame->SkiaCanvas();
204  overlay_canvas->clear(SK_ColorTRANSPARENT);
205  // Offset the picture since its absolute position on the scene is determined
206  // by the position of the overlay view.
207  overlay_canvas->translate(-rect.x(), -rect.y());
208  overlay_canvas->drawPicture(picture);
209  return frame;
210 }
211 
212 // |ExternalViewEmbedder|
214  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
215  if (!FrameHasPlatformLayers()) {
217  }
218  if (!raster_thread_merger->IsMerged()) {
219  // The raster thread merger may be disabled if the rasterizer is being
220  // created or teared down.
221  //
222  // In such cases, the current frame is dropped, and a new frame is attempted
223  // with the same layer tree.
224  //
225  // Eventually, the frame is submitted once this method returns `kSuccess`.
226  // At that point, the raster tasks are handled on the platform thread.
227  CancelFrame();
228  raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration);
230  }
231  raster_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration);
232  // Surface switch requires to resubmit the frame.
233  // TODO(egarciad): https://github.com/flutter/flutter/issues/65652
234  if (previous_frame_view_count_ == 0) {
236  }
238 }
239 
240 bool AndroidExternalViewEmbedder::FrameHasPlatformLayers() {
241  return composition_order_.size() > 0;
242 }
243 
244 // |ExternalViewEmbedder|
246  // On Android, the root surface is created from the on-screen render target.
247  return nullptr;
248 }
249 
250 void AndroidExternalViewEmbedder::Reset() {
251  previous_frame_view_count_ = composition_order_.size();
252 
253  composition_order_.clear();
254  picture_recorders_.clear();
255 }
256 
257 // |ExternalViewEmbedder|
259  SkISize frame_size,
260  GrDirectContext* context,
261  double device_pixel_ratio,
262  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
263  Reset();
264 
265  // The surface size changed. Therefore, destroy existing surfaces as
266  // the existing surfaces in the pool can't be recycled.
267  if (frame_size_ != frame_size && raster_thread_merger->IsOnPlatformThread()) {
268  surface_pool_->DestroyLayers(jni_facade_);
269  }
270  surface_pool_->SetFrameSize(frame_size);
271  // JNI method must be called on the platform thread.
272  if (raster_thread_merger->IsOnPlatformThread()) {
273  jni_facade_->FlutterViewBeginFrame();
274  }
275 
276  frame_size_ = frame_size;
277  device_pixel_ratio_ = device_pixel_ratio;
278 }
279 
280 // |ExternalViewEmbedder|
282  Reset();
283 }
284 
285 // |ExternalViewEmbedder|
287  bool should_resubmit_frame,
288  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
289  surface_pool_->RecycleLayers();
290  // JNI method must be called on the platform thread.
291  if (raster_thread_merger->IsOnPlatformThread()) {
292  jni_facade_->FlutterViewEndFrame();
293  }
294 }
295 
296 // |ExternalViewEmbedder|
298  return true;
299 }
300 
301 // |ExternalViewEmbedder|
303  surface_pool_->DestroyLayers(jni_facade_);
304 }
305 
306 } // namespace flutter
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:90
PostPrerollResult PostPrerollAction(fml::RefPtr< fml::RasterThreadMerger > raster_thread_merger) override
Holds state that is shared across Android surfaces.
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
AndroidExternalViewEmbedder(const AndroidContext &android_context, std::shared_ptr< PlatformViewAndroidJNI > jni_facade, std::shared_ptr< AndroidSurfaceFactory > surface_factory)
void ExtendLeaseTo(size_t lease_term)