7 #include "flow/surface_frame.h"
8 #include "flutter/flow/view_slicer.h"
9 #include "flutter/fml/make_copyable.h"
10 #include "fml/synchronization/count_down_latch.h"
22 static const int kDefaultMergedLeaseDuration = 10;
24 static constexpr NSUInteger kFlutterClippingMaskViewPoolCapacity = 5;
29 CATransform3D GetCATransform3DFromSkMatrix(
const SkMatrix& matrix) {
31 CATransform3D transform = CATransform3DIdentity;
32 transform.m11 = matrix.getScaleX();
33 transform.m21 = matrix.getSkewX();
34 transform.m41 = matrix.getTranslateX();
35 transform.m14 = matrix.getPerspX();
37 transform.m12 = matrix.getSkewY();
38 transform.m22 = matrix.getScaleY();
39 transform.m42 = matrix.getTranslateY();
40 transform.m24 = matrix.getPerspY();
47 void ResetAnchor(CALayer* layer) {
49 layer.anchorPoint = CGPointZero;
50 layer.position = CGPointZero;
53 CGRect GetCGRectFromSkRect(
const SkRect& clipSkRect) {
54 return CGRectMake(clipSkRect.fLeft, clipSkRect.fTop, clipSkRect.fRight - clipSkRect.fLeft,
55 clipSkRect.fBottom - clipSkRect.fTop);
66 bool ClipRectContainsPlatformViewBoundingRect(
const SkRect& clip_rect,
67 const SkRect& platformview_boundingrect,
68 const SkMatrix& transform_matrix) {
69 SkRect transformed_rect = transform_matrix.mapRect(clip_rect);
70 return transformed_rect.contains(platformview_boundingrect);
81 bool ClipRRectContainsPlatformViewBoundingRect(
const SkRRect& clip_rrect,
82 const SkRect& platformview_boundingrect,
83 const SkMatrix& transform_matrix) {
84 SkVector upper_left = clip_rrect.radii(SkRRect::Corner::kUpperLeft_Corner);
85 SkVector upper_right = clip_rrect.radii(SkRRect::Corner::kUpperRight_Corner);
86 SkVector lower_right = clip_rrect.radii(SkRRect::Corner::kLowerRight_Corner);
87 SkVector lower_left = clip_rrect.radii(SkRRect::Corner::kLowerLeft_Corner);
88 SkScalar transformed_upper_left_x = transform_matrix.mapRadius(upper_left.x());
89 SkScalar transformed_upper_left_y = transform_matrix.mapRadius(upper_left.y());
90 SkScalar transformed_upper_right_x = transform_matrix.mapRadius(upper_right.x());
91 SkScalar transformed_upper_right_y = transform_matrix.mapRadius(upper_right.y());
92 SkScalar transformed_lower_right_x = transform_matrix.mapRadius(lower_right.x());
93 SkScalar transformed_lower_right_y = transform_matrix.mapRadius(lower_right.y());
94 SkScalar transformed_lower_left_x = transform_matrix.mapRadius(lower_left.x());
95 SkScalar transformed_lower_left_y = transform_matrix.mapRadius(lower_left.y());
96 SkRect transformed_clip_rect = transform_matrix.mapRect(clip_rrect.rect());
97 SkRRect transformed_rrect;
98 SkVector corners[] = {{transformed_upper_left_x, transformed_upper_left_y},
99 {transformed_upper_right_x, transformed_upper_right_y},
100 {transformed_lower_right_x, transformed_lower_right_y},
101 {transformed_lower_left_x, transformed_lower_left_y}};
102 transformed_rrect.setRectRadii(transformed_clip_rect, corners);
103 return transformed_rrect.contains(platformview_boundingrect);
116 mask_view_pool_.reset(
121 const fml::RefPtr<fml::TaskRunner>& platform_task_runner) {
122 platform_task_runner_ = platform_task_runner;
126 return weak_factory_->GetWeakPtr();
130 flutter_view_.reset(flutter_view);
134 UIViewController<FlutterViewResponder>* flutter_view_controller) {
135 flutter_view_controller_.reset(flutter_view_controller);
139 return flutter_view_controller_.get();
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);
157 NSDictionary<NSString*, id>* args = [call arguments];
159 int64_t viewId = [args[
@"id"] longLongValue];
160 NSString* viewTypeString = args[
@"viewType"];
161 std::string viewType(viewTypeString.UTF8String);
163 if (platform_views_.count(viewId) != 0) {
165 message:
@"trying to create an already created view"
166 details:[NSString stringWithFormat:
@"view id: '%lld'", viewId]]);
170 NSObject<FlutterPlatformViewFactory>* factory = factories_[viewType].get();
171 if (factory == nil) {
173 errorWithCode:
@"unregistered_view_type"
174 message:[NSString stringWithFormat:
@"A UIKitView widget is trying to create a "
175 @"PlatformView with an unregistered type: < %@ >",
177 details:
@"If you are the author of the PlatformView, make sure `registerViewFactory` "
180 @"https://docs.flutter.dev/development/platform-integration/"
181 @"platform-views#on-the-platform-side-1 for more details.\n"
182 @"If you are not the author of the PlatformView, make sure to call "
183 @"`GeneratedPluginRegistrant.register`."]);
188 if ([factory respondsToSelector:
@selector(createArgsCodec)]) {
189 NSObject<FlutterMessageCodec>* codec = [factory createArgsCodec];
190 if (codec != nil && args[
@"params"] != nil) {
192 params = [codec decode:paramsData.
data];
196 NSObject<FlutterPlatformView>* embedded_view = [factory createWithFrame:CGRectZero
197 viewIdentifier:viewId
202 [NSString stringWithFormat:
@"platform_view[%lld]", viewId];
207 gestureRecognizersBlockingPolicy:gesture_recognizers_blocking_policies_[viewType]];
210 [clipping_view addSubview:touch_interceptor];
212 platform_views_.emplace(
213 viewId, PlatformViewData{
214 .view = fml::scoped_nsobject<NSObject<FlutterPlatformView>>(embedded_view),
216 fml::scoped_nsobject<FlutterTouchInterceptingView>(touch_interceptor),
217 .root_view = fml::scoped_nsobject<UIView>(clipping_view)
224 NSNumber* arg = [call arguments];
225 int64_t viewId = [arg longLongValue];
227 if (platform_views_.count(viewId) == 0) {
229 message:
@"trying to dispose an unknown"
230 details:[NSString stringWithFormat:
@"view id: '%lld'", viewId]]);
234 views_to_dispose_.insert(viewId);
239 NSDictionary<NSString*, id>* args = [call arguments];
240 int64_t viewId = [args[
@"id"] longLongValue];
242 if (platform_views_.count(viewId) == 0) {
244 message:
@"trying to set gesture state for an unknown view"
245 details:[NSString stringWithFormat:
@"view id: '%lld'", viewId]]);
250 [view releaseGesture];
256 NSDictionary<NSString*, id>* args = [call arguments];
257 int64_t viewId = [args[
@"id"] longLongValue];
259 if (platform_views_.count(viewId) == 0) {
261 message:
@"trying to set gesture state for an unknown view"
262 details:[NSString stringWithFormat:
@"view id: '%lld'", viewId]]);
273 NSObject<FlutterPlatformViewFactory>* factory,
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;
284 frame_size_ = frame_size;
292 const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger,
293 bool impeller_enabled) {
295 #ifdef FML_OS_IOS_SIMULATOR
296 const bool merge_threads =
true;
298 const bool merge_threads = !impeller_enabled;
299 #endif // FML_OS_IOS_SIMULATOR
302 if (composition_order_.empty()) {
303 return PostPrerollResult::kSuccess;
305 if (!raster_thread_merger->IsMerged()) {
315 return PostPrerollResult::kSkipAndRetryFrame;
321 raster_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration);
323 return PostPrerollResult::kSuccess;
327 bool should_resubmit_frame,
328 const fml::RefPtr<fml::RasterThreadMerger>& raster_thread_merger,
329 bool impeller_enabled) {
330 #if FML_OS_IOS_SIMULATOR
331 bool run_check =
true;
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);
341 const std::shared_ptr<const DlImageFilter>& filter,
342 const SkRect& filter_rect) {
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;
352 std::unique_ptr<EmbeddedViewParams> params) {
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));
358 composition_order_.push_back(view_id);
360 if (current_composition_params_.count(view_id) == 1 &&
361 current_composition_params_[view_id] == *params.get()) {
365 current_composition_params_[view_id] = EmbeddedViewParams(*params.get());
366 views_to_recomposite_.insert(view_id);
370 return composition_order_.size();
374 return layer_pool_->size();
383 if (platform_views_.empty()) {
386 return platform_views_[view_id].touch_interceptor.get();
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) {
399 void PlatformViewsController::ClipViewSetMaskView(UIView* clipView) {
400 FML_DCHECK([[NSThread currentThread] isMainThread]);
401 if (clipView.maskView) {
404 UIView* flutterView = flutter_view_.get();
406 CGRectMake(-clipView.frame.origin.x, -clipView.frame.origin.y,
407 CGRectGetWidth(flutterView.bounds), CGRectGetHeight(flutterView.bounds));
408 clipView.maskView = [mask_view_pool_.get() getMaskViewWithFrame:frame];
413 void PlatformViewsController::ApplyMutators(
const MutatorsStack& mutators_stack,
414 UIView* embedded_view,
415 const SkRect& bounding_rect) {
416 if (flutter_view_ ==
nullptr) {
420 ResetAnchor(embedded_view.layer);
423 SkMatrix transformMatrix;
424 NSMutableArray* blurFilters = [[NSMutableArray alloc] init];
425 FML_DCHECK(!clipView.maskView ||
427 if (clipView.maskView) {
429 clipView.maskView = nil;
431 CGFloat screenScale = [UIScreen mainScreen].scale;
432 auto iter = mutators_stack.Begin();
433 while (iter != mutators_stack.End()) {
434 switch ((*iter)->GetType()) {
436 transformMatrix.preConcat((*iter)->GetMatrix());
440 if (ClipRectContainsPlatformViewBoundingRect((*iter)->GetRect(), bounding_rect,
444 ClipViewSetMaskView(clipView);
446 matrix:transformMatrix];
450 if (ClipRRectContainsPlatformViewBoundingRect((*iter)->GetRRect(), bounding_rect,
454 ClipViewSetMaskView(clipView);
456 matrix:transformMatrix];
463 ClipViewSetMaskView(clipView);
465 matrix:transformMatrix];
469 embedded_view.alpha = (*iter)->GetAlphaFloat() * embedded_view.alpha;
471 case kBackdropFilter: {
476 CGRect filterRect = GetCGRectFromSkRect((*iter)->GetFilterMutation().GetFilterRect());
478 filterRect = CGRectApplyAffineTransform(
479 filterRect, CGAffineTransformMakeScale(1 / screenScale, 1 / screenScale));
483 if (CGRectIsNull(CGRectIntersection(filterRect, clipView.frame))) {
486 CGRect intersection = CGRectIntersection(filterRect, clipView.frame);
487 CGRect frameInClipView = [flutter_view_.get() convertRect:intersection toView:clipView];
492 CGFloat blurRadius = (*iter)->GetFilterMutation().GetFilter().asBlur()->sigma_x();
493 UIVisualEffectView* visualEffectView = [[UIVisualEffectView alloc]
494 initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]];
496 blurRadius:blurRadius
497 visualEffectView:visualEffectView];
501 [blurFilters addObject:filter];
510 [clipView applyBlurBackdropFilters:blurFilters];
518 transformMatrix.postScale(1 / screenScale, 1 / screenScale);
527 transformMatrix.postTranslate(-clipView.frame.origin.x, -clipView.frame.origin.y);
529 embedded_view.layer.transform = GetCATransform3DFromSkMatrix(transformMatrix);
541 const EmbeddedViewParams& params) {
542 CGRect frame = CGRectMake(0, 0, params.sizePoints().width(), params.sizePoints().height());
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);
550 @"A Embedded PlatformView's origin is not CGPointZero.\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]);
562 touchInterceptor.layer.transform = CATransform3DIdentity;
563 touchInterceptor.frame = frame;
564 touchInterceptor.alpha = 1;
566 const MutatorsStack& mutatorStack = params.mutatorsStack();
567 UIView* clippingView = platform_views_[view_id].root_view.get();
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);
580 return slices_[view_id]->canvas();
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];
592 platform_views_.clear();
595 composition_order_.clear();
597 current_composition_params_.clear();
598 views_to_recomposite_.clear();
599 layer_pool_->RecycleLayers();
600 visited_platform_views_.clear();
604 const std::shared_ptr<IOSContext>& ios_context,
605 std::unique_ptr<SurfaceFrame> background_frame) {
606 TRACE_EVENT0(
"flutter",
"PlatformViewsController::SubmitFrame");
609 if (flutter_view_ ==
nullptr || (composition_order_.empty() && !had_platform_views_)) {
610 had_platform_views_ =
false;
611 return background_frame->Submit();
613 had_platform_views_ = !composition_order_.empty();
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;
621 for (int64_t view_id : composition_order_) {
622 view_rects[view_id] = current_composition_params_[view_id].finalBoundingRect();
625 std::unordered_map<int64_t, SkRect> overlay_layers =
626 SliceViews(background_frame->Canvas(), composition_order_, slices_, view_rects);
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()) {
634 required_overlay_layers++;
640 CreateMissingOverlays(gr_context, ios_context, required_overlay_layers);
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()) {
648 std::shared_ptr<OverlayLayer> layer = GetExistingLayer();
653 std::unique_ptr<SurfaceFrame> frame = layer->surface->AcquireFrame(frame_size_);
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);
668 frame->set_submit_info({.frame_boundary =
false, .present_with_transaction =
true});
669 layer->did_submit_last_frame = frame->Encode();
671 did_encode &= layer->did_submit_last_frame;
672 platform_view_layers[view_id] = LayerData{
673 .rect = overlay->second,
675 .overlay_id = overlay_id,
678 surface_frames.push_back(std::move(frame));
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,
688 background_frame->Encode();
689 surface_frames.push_back(std::move(background_frame));
692 std::vector<std::shared_ptr<OverlayLayer>> unused_layers = layer_pool_->RemoveUnusedLayers();
693 layer_pool_->RecycleLayers();
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)
703 PerformSubmit(platform_view_layers,
704 current_composition_params,
705 views_to_recomposite,
712 fml::TaskRunner::RunNowOrPostTask(platform_task_runner_, fml::MakeCopyable(std::move(task)));
717 void PlatformViewsController::CreateMissingOverlays(GrDirectContext* gr_context,
718 const std::shared_ptr<IOSContext>& ios_context,
719 size_t required_overlay_layers) {
720 TRACE_EVENT0(
"flutter",
"PlatformViewsController::CreateMissingLayers");
722 if (required_overlay_layers <= layer_pool_->size()) {
725 auto missing_layer_count = required_overlay_layers - layer_pool_->size();
729 auto latch = std::make_shared<fml::CountDownLatch>(1u);
730 fml::TaskRunner::RunNowOrPostTask(platform_task_runner_, [&]() {
731 for (
auto i = 0u; i < missing_layer_count; i++) {
732 CreateLayer(gr_context,
739 if (![[NSThread currentThread] isMainThread]) {
745 void PlatformViewsController::PerformSubmit(
746 const LayersMap& platform_view_layers,
747 std::unordered_map<int64_t, EmbeddedViewParams>& current_composition_params,
748 const std::unordered_set<int64_t>& views_to_recomposite,
749 const std::vector<int64_t>& composition_order,
750 const std::vector<std::shared_ptr<OverlayLayer>>& unused_layers,
751 const std::vector<std::unique_ptr<SurfaceFrame>>& surface_frames) {
752 TRACE_EVENT0(
"flutter",
"PlatformViewsController::PerformSubmit");
753 FML_DCHECK([[NSThread currentThread] isMainThread]);
755 [CATransaction begin];
758 for (
const auto& [view_id, layer_data] : platform_view_layers) {
759 layer_data.layer->UpdateViewState(flutter_view_,
762 layer_data.overlay_id
767 for (
auto& view : GetViewsToDispose()) {
768 [view removeFromSuperview];
772 for (int64_t view_id : views_to_recomposite) {
777 for (
const auto& frame : surface_frames) {
783 RemoveUnusedLayers(unused_layers, composition_order);
786 BringLayersIntoView(platform_view_layers, composition_order);
788 [CATransaction commit];
791 void PlatformViewsController::BringLayersIntoView(
const LayersMap& layer_map,
792 const std::vector<int64_t>& composition_order) {
793 FML_DCHECK(flutter_view_);
794 UIView* flutter_view = flutter_view_.get();
796 previous_composition_order_.clear();
797 NSMutableArray* desired_platform_subviews = [NSMutableArray array];
798 for (int64_t platform_view_id : composition_order) {
799 previous_composition_order_.push_back(platform_view_id);
800 UIView* platform_view_root = platform_views_[platform_view_id].root_view.get();
801 if (platform_view_root != nil) {
802 [desired_platform_subviews addObject:platform_view_root];
805 auto maybe_layer_data = layer_map.find(platform_view_id);
806 if (maybe_layer_data != layer_map.end()) {
807 auto view = maybe_layer_data->second.layer->overlay_view_wrapper;
809 [desired_platform_subviews addObject:view];
814 NSSet* desired_platform_subviews_set = [NSSet setWithArray:desired_platform_subviews];
815 NSArray* existing_platform_subviews = [flutter_view.subviews
816 filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
id object,
817 NSDictionary* bindings) {
818 return [desired_platform_subviews_set containsObject:object];
825 if (![desired_platform_subviews isEqualToArray:existing_platform_subviews]) {
826 for (UIView* subview in desired_platform_subviews) {
828 [flutter_view addSubview:subview];
833 std::shared_ptr<OverlayLayer> PlatformViewsController::GetExistingLayer() {
834 return layer_pool_->GetNextLayer();
837 void PlatformViewsController::CreateLayer(GrDirectContext* gr_context,
838 const std::shared_ptr<IOSContext>& ios_context,
839 MTLPixelFormat pixel_format) {
840 layer_pool_->CreateLayer(gr_context, ios_context, pixel_format);
843 void PlatformViewsController::RemoveUnusedLayers(
844 const std::vector<std::shared_ptr<OverlayLayer>>& unused_layers,
845 const std::vector<int64_t>& composition_order) {
846 for (
const std::shared_ptr<OverlayLayer>& layer : unused_layers) {
847 [layer->overlay_view_wrapper removeFromSuperview];
850 std::unordered_set<int64_t> composition_order_set;
851 for (int64_t view_id : composition_order) {
852 composition_order_set.insert(view_id);
855 for (int64_t view_id : previous_composition_order_) {
856 if (composition_order_set.find(view_id) == composition_order_set.end()) {
857 UIView* platform_view_root = platform_views_[view_id].root_view.get();
858 [platform_view_root removeFromSuperview];
863 std::vector<UIView*> PlatformViewsController::GetViewsToDispose() {
864 std::vector<UIView*> views;
865 if (views_to_dispose_.empty()) {
869 std::unordered_set<int64_t> views_to_composite(composition_order_.begin(),
870 composition_order_.end());
871 std::unordered_set<int64_t> views_to_delay_dispose;
872 for (int64_t viewId : views_to_dispose_) {
873 if (views_to_composite.count(viewId)) {
874 views_to_delay_dispose.insert(viewId);
877 UIView* root_view = platform_views_[viewId].root_view.get();
878 views.push_back(root_view);
879 current_composition_params_.erase(viewId);
880 views_to_recomposite_.erase(viewId);
881 platform_views_.erase(viewId);
883 views_to_dispose_ = std::move(views_to_delay_dispose);
887 void PlatformViewsController::ResetFrameState() {
889 composition_order_.clear();
890 visited_platform_views_.clear();