Flutter iOS Embedder
flutter::PlatformViewsController Class Reference

Composites Flutter UI and overlay layers alongside embedded UIViews. More...

#include <platform_views_controller.h>

Public Member Functions

 PlatformViewsController ()
 
 ~PlatformViewsController ()=default
 
fml::WeakPtr< flutter::PlatformViewsControllerGetWeakPtr ()
 Retrieve a weak pointer to this controller. More...
 
void SetTaskRunner (const fml::RefPtr< fml::TaskRunner > &platform_task_runner)
 Set the platform task runner used to post rendering tasks. More...
 
void SetFlutterView (UIView *flutter_view) __attribute__((cf_audited_transfer))
 Set the flutter view. More...
 
void SetFlutterViewController (UIViewController< FlutterViewResponder > *flutter_view_controller) __attribute__((cf_audited_transfer))
 Set the flutter view controller. More...
 
UIViewController< FlutterViewResponder > * GetFlutterViewController () __attribute__((cf_audited_transfer))
 Retrieve the view controller. More...
 
void RegisterViewFactory (NSObject< FlutterPlatformViewFactory > *factory, NSString *factoryId, FlutterPlatformViewGestureRecognizersBlockingPolicy gestureRecognizerBlockingPolicy) __attribute__((cf_audited_transfer))
 set the factory used to construct embedded UI Views. More...
 
void BeginFrame (SkISize frame_size)
 Mark the beginning of a frame and record the size of the onscreen. More...
 
void CancelFrame ()
 Cancel the current frame, indicating that no platform views are composited. More...
 
void PrerollCompositeEmbeddedView (int64_t view_id, std::unique_ptr< flutter::EmbeddedViewParams > params)
 Record a platform view in the layer tree to be rendered, along with the positioning and mutator parameters. More...
 
FlutterTouchInterceptingViewGetFlutterTouchInterceptingViewByID (int64_t view_id)
 Returns theFlutterTouchInterceptingView with the provided view_id. More...
 
PostPrerollResult PostPrerollAction (const fml::RefPtr< fml::RasterThreadMerger > &raster_thread_merger, bool impeller_enabled)
 Determine if thread merging is required after prerolling platform views. More...
 
void EndFrame (bool should_resubmit_frame, const fml::RefPtr< fml::RasterThreadMerger > &raster_thread_merger, bool impeller_enabled)
 Mark the end of a compositor frame. More...
 
DlCanvas * CompositeEmbeddedView (int64_t view_id)
 Returns the Canvas for the overlay slice for the given platform view. More...
 
void Reset ()
 Discards all platform views instances and auxiliary resources. More...
 
bool SubmitFrame (GrDirectContext *gr_context, const std::shared_ptr< IOSContext > &ios_context, std::unique_ptr< SurfaceFrame > frame)
 Encode rendering for the Flutter overlay views and queue up perform platform view mutations. More...
 
void OnMethodCall (FlutterMethodCall *call, FlutterResult result) __attribute__((cf_audited_transfer))
 Handler for platform view message channels. More...
 
long FindFirstResponderPlatformViewId ()
 Returns the platform view id if the platform view (or any of its descendant view) is the first responder. More...
 
void PushFilterToVisitedPlatformViews (const std::shared_ptr< const DlImageFilter > &filter, const SkRect &filter_rect)
 Pushes backdrop filter mutation to the mutator stack of each visited platform view. More...
 
void PushVisitedPlatformView (int64_t view_id)
 Pushes the view id of a visted platform view to the list of visied platform views. More...
 
size_t EmbeddedViewCount () const
 
size_t LayerPoolSize () const
 
UIView * GetPlatformViewByID (int64_t view_id)
 
void CompositeWithParams (int64_t view_id, const EmbeddedViewParams &params)
 
const EmbeddedViewParams & GetCompositionParams (int64_t view_id) const
 

Detailed Description

Composites Flutter UI and overlay layers alongside embedded UIViews.

Definition at line 31 of file platform_views_controller.h.

Constructor & Destructor Documentation

◆ PlatformViewsController()

flutter::PlatformViewsController::PlatformViewsController ( )

Definition at line 113 of file platform_views_controller.mm.

114  : layer_pool_(std::make_unique<OverlayLayerPool>()),
115  weak_factory_(std::make_unique<fml::WeakPtrFactory<PlatformViewsController>>(this)) {
116  mask_view_pool_.reset(
117  [[FlutterClippingMaskViewPool alloc] initWithCapacity:kFlutterClippingMaskViewPoolCapacity]);
118 };

◆ ~PlatformViewsController()

flutter::PlatformViewsController::~PlatformViewsController ( )
default

Member Function Documentation

◆ BeginFrame()

void flutter::PlatformViewsController::BeginFrame ( SkISize  frame_size)

Mark the beginning of a frame and record the size of the onscreen.

Definition at line 282 of file platform_views_controller.mm.

282  {
283  ResetFrameState();
284  frame_size_ = frame_size;
285 }

◆ CancelFrame()

void flutter::PlatformViewsController::CancelFrame ( )

Cancel the current frame, indicating that no platform views are composited.

Additionally, reverts the composition order to its original state at the beginning of the frame.

Definition at line 287 of file platform_views_controller.mm.

287  {
288  ResetFrameState();
289 }

Referenced by PostPrerollAction().

◆ CompositeEmbeddedView()

DlCanvas * flutter::PlatformViewsController::CompositeEmbeddedView ( int64_t  view_id)

Returns the Canvas for the overlay slice for the given platform view.

Called from the raster thread.

Definition at line 579 of file platform_views_controller.mm.

579  {
580  return slices_[view_id]->canvas();
581 }

◆ CompositeWithParams()

void flutter::PlatformViewsController::CompositeWithParams ( int64_t  view_id,
const EmbeddedViewParams &  params 
)

Definition at line 540 of file platform_views_controller.mm.

541  {
542  CGRect frame = CGRectMake(0, 0, params.sizePoints().width(), params.sizePoints().height());
543  FlutterTouchInterceptingView* touchInterceptor = platform_views_[view_id].touch_interceptor.get();
544 #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
545  FML_DCHECK(CGPointEqualToPoint([touchInterceptor embeddedView].frame.origin, CGPointZero));
546  if (non_zero_origin_views_.find(view_id) == non_zero_origin_views_.end() &&
547  !CGPointEqualToPoint([touchInterceptor embeddedView].frame.origin, CGPointZero)) {
548  non_zero_origin_views_.insert(view_id);
549  NSLog(
550  @"A Embedded PlatformView's origin is not CGPointZero.\n"
551  " View id: %@\n"
552  " View info: \n %@ \n"
553  "A non-zero origin might cause undefined behavior.\n"
554  "See https://github.com/flutter/flutter/issues/109700 for more details.\n"
555  "If you are the author of the PlatformView, please update the implementation of the "
556  "PlatformView to have a (0, 0) origin.\n"
557  "If you have a valid case of using a non-zero origin, "
558  "please leave a comment at https://github.com/flutter/flutter/issues/109700 with details.",
559  @(view_id), [touchInterceptor embeddedView]);
560  }
561 #endif
562  touchInterceptor.layer.transform = CATransform3DIdentity;
563  touchInterceptor.frame = frame;
564  touchInterceptor.alpha = 1;
565 
566  const MutatorsStack& mutatorStack = params.mutatorsStack();
567  UIView* clippingView = platform_views_[view_id].root_view.get();
568  // The frame of the clipping view should be the final bounding rect.
569  // Because the translate matrix in the Mutator Stack also includes the offset,
570  // when we apply the transforms matrix in |ApplyMutators|, we need
571  // to remember to do a reverse translate.
572  const SkRect& rect = params.finalBoundingRect();
573  CGFloat screenScale = [UIScreen mainScreen].scale;
574  clippingView.frame = CGRectMake(rect.x() / screenScale, rect.y() / screenScale,
575  rect.width() / screenScale, rect.height() / screenScale);
576  ApplyMutators(mutatorStack, touchInterceptor, rect);
577 }

◆ EmbeddedViewCount()

size_t flutter::PlatformViewsController::EmbeddedViewCount ( ) const

Definition at line 369 of file platform_views_controller.mm.

369  {
370  return composition_order_.size();
371 }

◆ EndFrame()

void flutter::PlatformViewsController::EndFrame ( bool  should_resubmit_frame,
const fml::RefPtr< fml::RasterThreadMerger > &  raster_thread_merger,
bool  impeller_enabled 
)

Mark the end of a compositor frame.

May determine changes are required to the thread merging state. Called from the raster thread.

Definition at line 326 of file platform_views_controller.mm.

329  {
330 #if FML_OS_IOS_SIMULATOR
331  bool run_check = true;
332 #else
333  bool run_check = !impeller_enabled;
334 #endif // FML_OS_IOS_SIMULATOR
335  if (run_check && should_resubmit_frame) {
336  raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration);
337  }
338 }

◆ FindFirstResponderPlatformViewId()

long flutter::PlatformViewsController::FindFirstResponderPlatformViewId ( )

Returns the platform view id if the platform view (or any of its descendant view) is the first responder.

Returns -1 if no such platform view is found.

Definition at line 389 of file platform_views_controller.mm.

389  {
390  for (auto const& [id, platform_view_data] : platform_views_) {
391  UIView* root_view = (UIView*)platform_view_data.root_view.get();
392  if (root_view.flt_hasFirstResponderInViewHierarchySubtree) {
393  return id;
394  }
395  }
396  return -1;
397 }

◆ GetCompositionParams()

const EmbeddedViewParams& flutter::PlatformViewsController::GetCompositionParams ( int64_t  view_id) const
inline

Definition at line 151 of file platform_views_controller.h.

151  {
152  return current_composition_params_.find(view_id)->second;
153  }

◆ GetFlutterTouchInterceptingViewByID()

FlutterTouchInterceptingView * flutter::PlatformViewsController::GetFlutterTouchInterceptingViewByID ( int64_t  view_id)

Returns theFlutterTouchInterceptingView with the provided view_id.

Returns nil if there is no platform view with the provided id. Called from the platform thread.

Definition at line 381 of file platform_views_controller.mm.

382  {
383  if (platform_views_.empty()) {
384  return nil;
385  }
386  return platform_views_[view_id].touch_interceptor.get();
387 }

Referenced by GetPlatformViewByID().

◆ GetFlutterViewController()

UIViewController< FlutterViewResponder > * flutter::PlatformViewsController::GetFlutterViewController ( )

Retrieve the view controller.

Definition at line 138 of file platform_views_controller.mm.

138  {
139  return flutter_view_controller_.get();
140 }

◆ GetPlatformViewByID()

UIView * flutter::PlatformViewsController::GetPlatformViewByID ( int64_t  view_id)

Definition at line 377 of file platform_views_controller.mm.

377  {
378  return [GetFlutterTouchInterceptingViewByID(view_id) embeddedView];
379 }

References GetFlutterTouchInterceptingViewByID().

◆ GetWeakPtr()

fml::WeakPtr< flutter::PlatformViewsController > flutter::PlatformViewsController::GetWeakPtr ( )

Retrieve a weak pointer to this controller.

Definition at line 125 of file platform_views_controller.mm.

125  {
126  return weak_factory_->GetWeakPtr();
127 }

◆ LayerPoolSize()

size_t flutter::PlatformViewsController::LayerPoolSize ( ) const

Definition at line 373 of file platform_views_controller.mm.

373  {
374  return layer_pool_->size();
375 }

◆ OnMethodCall()

void flutter::PlatformViewsController::OnMethodCall ( FlutterMethodCall call,
FlutterResult  result 
)

Handler for platform view message channels.

Definition at line 142 of file platform_views_controller.mm.

142  {
143  if ([[call method] isEqualToString:@"create"]) {
144  OnCreate(call, result);
145  } else if ([[call method] isEqualToString:@"dispose"]) {
146  OnDispose(call, result);
147  } else if ([[call method] isEqualToString:@"acceptGesture"]) {
148  OnAcceptGesture(call, result);
149  } else if ([[call method] isEqualToString:@"rejectGesture"]) {
150  OnRejectGesture(call, result);
151  } else {
153  }
154 }

References FlutterMethodNotImplemented.

◆ PostPrerollAction()

PostPrerollResult flutter::PlatformViewsController::PostPrerollAction ( const fml::RefPtr< fml::RasterThreadMerger > &  raster_thread_merger,
bool  impeller_enabled 
)

Determine if thread merging is required after prerolling platform views.

Called from the raster thread.

Definition at line 291 of file platform_views_controller.mm.

293  {
294  // TODO(jonahwilliams): remove this once Software backend is removed for iOS Sim.
295 #ifdef FML_OS_IOS_SIMULATOR
296  const bool merge_threads = true;
297 #else
298  const bool merge_threads = !impeller_enabled;
299 #endif // FML_OS_IOS_SIMULATOR
300 
301  if (merge_threads) {
302  if (composition_order_.empty()) {
303  return PostPrerollResult::kSuccess;
304  }
305  if (!raster_thread_merger->IsMerged()) {
306  // The raster thread merger may be disabled if the rasterizer is being
307  // created or teared down.
308  //
309  // In such cases, the current frame is dropped, and a new frame is attempted
310  // with the same layer tree.
311  //
312  // Eventually, the frame is submitted once this method returns `kSuccess`.
313  // At that point, the raster tasks are handled on the platform thread.
314  CancelFrame();
315  return PostPrerollResult::kSkipAndRetryFrame;
316  }
317  // If the post preroll action is successful, we will display platform views in the current
318  // frame. In order to sync the rendering of the platform views (quartz) with skia's rendering,
319  // We need to begin an explicit CATransaction. This transaction needs to be submitted
320  // after the current frame is submitted.
321  raster_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration);
322  }
323  return PostPrerollResult::kSuccess;
324 }

References CancelFrame().

◆ PrerollCompositeEmbeddedView()

void flutter::PlatformViewsController::PrerollCompositeEmbeddedView ( int64_t  view_id,
std::unique_ptr< flutter::EmbeddedViewParams >  params 
)

Record a platform view in the layer tree to be rendered, along with the positioning and mutator parameters.

Called from the raster thread.

Definition at line 350 of file platform_views_controller.mm.

352  {
353  SkRect view_bounds = SkRect::Make(frame_size_);
354  std::unique_ptr<EmbedderViewSlice> view;
355  view = std::make_unique<DisplayListEmbedderViewSlice>(view_bounds);
356  slices_.insert_or_assign(view_id, std::move(view));
357 
358  composition_order_.push_back(view_id);
359 
360  if (current_composition_params_.count(view_id) == 1 &&
361  current_composition_params_[view_id] == *params.get()) {
362  // Do nothing if the params didn't change.
363  return;
364  }
365  current_composition_params_[view_id] = EmbeddedViewParams(*params.get());
366  views_to_recomposite_.insert(view_id);
367 }

◆ PushFilterToVisitedPlatformViews()

void flutter::PlatformViewsController::PushFilterToVisitedPlatformViews ( const std::shared_ptr< const DlImageFilter > &  filter,
const SkRect &  filter_rect 
)

Pushes backdrop filter mutation to the mutator stack of each visited platform view.

Definition at line 340 of file platform_views_controller.mm.

342  {
343  for (int64_t id : visited_platform_views_) {
344  EmbeddedViewParams params = current_composition_params_[id];
345  params.PushImageFilter(filter, filter_rect);
346  current_composition_params_[id] = params;
347  }
348 }

◆ PushVisitedPlatformView()

void flutter::PlatformViewsController::PushVisitedPlatformView ( int64_t  view_id)
inline

Pushes the view id of a visted platform view to the list of visied platform views.

Definition at line 131 of file platform_views_controller.h.

131 { visited_platform_views_.push_back(view_id); }

◆ RegisterViewFactory()

void flutter::PlatformViewsController::RegisterViewFactory ( NSObject< FlutterPlatformViewFactory > *  factory,
NSString *  factoryId,
FlutterPlatformViewGestureRecognizersBlockingPolicy  gestureRecognizerBlockingPolicy 
)

set the factory used to construct embedded UI Views.

Definition at line 272 of file platform_views_controller.mm.

275  {
276  std::string idString([factoryId UTF8String]);
277  FML_CHECK(factories_.count(idString) == 0);
278  factories_[idString] = fml::scoped_nsobject<NSObject<FlutterPlatformViewFactory>>(factory);
279  gesture_recognizers_blocking_policies_[idString] = gestureRecognizerBlockingPolicy;
280 }

◆ Reset()

void flutter::PlatformViewsController::Reset ( )

Discards all platform views instances and auxiliary resources.

Called from the raster thread.

Definition at line 583 of file platform_views_controller.mm.

583  {
584  // Reset will only be called from the raster thread or a merged raster/platform thread.
585  // platform_views_ must only be modified on the platform thread, and any operations that
586  // read or modify platform views should occur there.
587  fml::TaskRunner::RunNowOrPostTask(
588  platform_task_runner_, [&, composition_order = composition_order_]() {
589  for (int64_t view_id : composition_order_) {
590  [platform_views_[view_id].root_view.get() removeFromSuperview];
591  }
592  platform_views_.clear();
593  });
594 
595  composition_order_.clear();
596  slices_.clear();
597  current_composition_params_.clear();
598  views_to_recomposite_.clear();
599  layer_pool_->RecycleLayers();
600  visited_platform_views_.clear();
601 }

◆ SetFlutterView()

void flutter::PlatformViewsController::SetFlutterView ( UIView *  flutter_view)

Set the flutter view.

Definition at line 129 of file platform_views_controller.mm.

129  {
130  flutter_view_.reset(flutter_view);
131 }

◆ SetFlutterViewController()

void flutter::PlatformViewsController::SetFlutterViewController ( UIViewController< FlutterViewResponder > *  flutter_view_controller)

Set the flutter view controller.

Definition at line 133 of file platform_views_controller.mm.

134  {
135  flutter_view_controller_.reset(flutter_view_controller);
136 }

◆ SetTaskRunner()

void flutter::PlatformViewsController::SetTaskRunner ( const fml::RefPtr< fml::TaskRunner > &  platform_task_runner)

Set the platform task runner used to post rendering tasks.

Definition at line 120 of file platform_views_controller.mm.

121  {
122  platform_task_runner_ = platform_task_runner;
123 }

◆ SubmitFrame()

bool flutter::PlatformViewsController::SubmitFrame ( GrDirectContext *  gr_context,
const std::shared_ptr< IOSContext > &  ios_context,
std::unique_ptr< SurfaceFrame >  frame 
)

Encode rendering for the Flutter overlay views and queue up perform platform view mutations.

Called from the raster thread.

Definition at line 603 of file platform_views_controller.mm.

605  {
606  TRACE_EVENT0("flutter", "PlatformViewsController::SubmitFrame");
607 
608  // No platform views to render; we're done.
609  if (flutter_view_ == nullptr || (composition_order_.empty() && !had_platform_views_)) {
610  had_platform_views_ = false;
611  return background_frame->Submit();
612  }
613  had_platform_views_ = !composition_order_.empty();
614 
615  bool did_encode = true;
616  LayersMap platform_view_layers;
617  std::vector<std::unique_ptr<SurfaceFrame>> surface_frames;
618  surface_frames.reserve(composition_order_.size());
619  std::unordered_map<int64_t, SkRect> view_rects;
620 
621  for (int64_t view_id : composition_order_) {
622  view_rects[view_id] = current_composition_params_[view_id].finalBoundingRect();
623  }
624 
625  std::unordered_map<int64_t, SkRect> overlay_layers =
626  SliceViews(background_frame->Canvas(), composition_order_, slices_, view_rects);
627 
628  size_t required_overlay_layers = 0;
629  for (int64_t view_id : composition_order_) {
630  std::unordered_map<int64_t, SkRect>::const_iterator overlay = overlay_layers.find(view_id);
631  if (overlay == overlay_layers.end()) {
632  continue;
633  }
634  required_overlay_layers++;
635  }
636 
637  // If there are not sufficient overlay layers, we must construct them on the platform
638  // thread, at least until we've refactored iOS surface creation to use IOSurfaces
639  // instead of CALayers.
640  CreateMissingOverlays(gr_context, ios_context, required_overlay_layers);
641 
642  int64_t overlay_id = 0;
643  for (int64_t view_id : composition_order_) {
644  std::unordered_map<int64_t, SkRect>::const_iterator overlay = overlay_layers.find(view_id);
645  if (overlay == overlay_layers.end()) {
646  continue;
647  }
648  std::shared_ptr<OverlayLayer> layer = GetExistingLayer();
649  if (!layer) {
650  continue;
651  }
652 
653  std::unique_ptr<SurfaceFrame> frame = layer->surface->AcquireFrame(frame_size_);
654  // If frame is null, AcquireFrame already printed out an error message.
655  if (!frame) {
656  continue;
657  }
658  DlCanvas* overlay_canvas = frame->Canvas();
659  int restore_count = overlay_canvas->GetSaveCount();
660  overlay_canvas->Save();
661  overlay_canvas->ClipRect(overlay->second);
662  overlay_canvas->Clear(DlColor::kTransparent());
663  slices_[view_id]->render_into(overlay_canvas);
664  overlay_canvas->RestoreToCount(restore_count);
665 
666  // This flutter view is never the last in a frame, since we always submit the
667  // underlay view last.
668  frame->set_submit_info({.frame_boundary = false, .present_with_transaction = true});
669  layer->did_submit_last_frame = frame->Encode();
670 
671  did_encode &= layer->did_submit_last_frame;
672  platform_view_layers[view_id] = LayerData{
673  .rect = overlay->second, //
674  .view_id = view_id, //
675  .overlay_id = overlay_id, //
676  .layer = layer //
677  };
678  surface_frames.push_back(std::move(frame));
679  overlay_id++;
680  }
681 
682  auto previous_submit_info = background_frame->submit_info();
683  background_frame->set_submit_info({
684  .frame_damage = previous_submit_info.frame_damage,
685  .buffer_damage = previous_submit_info.buffer_damage,
686  .present_with_transaction = true,
687  });
688  background_frame->Encode();
689  surface_frames.push_back(std::move(background_frame));
690 
691  // Mark all layers as available, so they can be used in the next frame.
692  std::vector<std::shared_ptr<OverlayLayer>> unused_layers = layer_pool_->RemoveUnusedLayers();
693  layer_pool_->RecycleLayers();
694 
695  auto task = [&, //
696  platform_view_layers = std::move(platform_view_layers), //
697  current_composition_params = current_composition_params_, //
698  views_to_recomposite = views_to_recomposite_, //
699  composition_order = composition_order_, //
700  unused_layers = std::move(unused_layers), //
701  surface_frames = std::move(surface_frames) //
702  ]() mutable {
703  PerformSubmit(platform_view_layers, //
704  current_composition_params, //
705  views_to_recomposite, //
706  composition_order, //
707  unused_layers, //
708  surface_frames //
709  );
710  };
711 
712  fml::TaskRunner::RunNowOrPostTask(platform_task_runner_, fml::MakeCopyable(std::move(task)));
713 
714  return did_encode;
715 }

The documentation for this class was generated from the following files:
FlutterMethodNotImplemented
FLUTTER_DARWIN_EXPORT NSObject const * FlutterMethodNotImplemented
FlutterClippingMaskViewPool
Definition: FlutterPlatformViews_Internal.h:66
flutter::PlatformViewsController::GetFlutterTouchInterceptingViewByID
FlutterTouchInterceptingView * GetFlutterTouchInterceptingViewByID(int64_t view_id)
Returns theFlutterTouchInterceptingView with the provided view_id.
Definition: platform_views_controller.mm:381
FlutterTouchInterceptingView
Definition: FlutterPlatformViews_Internal.h:138
flutter::PlatformViewsController::CancelFrame
void CancelFrame()
Cancel the current frame, indicating that no platform views are composited.
Definition: platform_views_controller.mm:287