diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 211959f7eb748..f5a57dcd35738 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -18,8 +18,6 @@ #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" #import "flutter/shell/platform/darwin/ios/ios_surface.h" -static const NSUInteger kFlutterClippingMaskViewPoolCapacity = 5; - @implementation UIView (FirstResponder) - (BOOL)flt_hasFirstResponderInViewHierarchySubtree { if (self.isFirstResponder) { @@ -454,17 +452,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, return clipCount; } -void FlutterPlatformViewsController::ClipViewSetMaskView(UIView* clipView) { - if (clipView.maskView) { - return; - } - UIView* flutterView = flutter_view_.get(); - CGRect frame = - CGRectMake(-clipView.frame.origin.x, -clipView.frame.origin.y, - CGRectGetWidth(flutterView.bounds), CGRectGetHeight(flutterView.bounds)); - clipView.maskView = [mask_view_pool_.get() getMaskViewWithFrame:frame]; -} - void FlutterPlatformViewsController::ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view, const SkRect& bounding_rect) { @@ -475,17 +462,18 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, ResetAnchor(embedded_view.layer); ChildClippingView* clipView = (ChildClippingView*)embedded_view.superview; + CGFloat screenScale = [UIScreen mainScreen].scale; + + UIView* flutter_view = flutter_view_.get(); + FlutterClippingMaskView* maskView = [[[FlutterClippingMaskView alloc] + initWithFrame:CGRectMake(-clipView.frame.origin.x, -clipView.frame.origin.y, + CGRectGetWidth(flutter_view.bounds), + CGRectGetHeight(flutter_view.bounds)) + screenScale:screenScale] autorelease]; + SkMatrix transformMatrix; NSMutableArray* blurFilters = [[[NSMutableArray alloc] init] autorelease]; - FML_DCHECK(!clipView.maskView || - [clipView.maskView isKindOfClass:[FlutterClippingMaskView class]]); - if (mask_view_pool_.get() == nil) { - mask_view_pool_.reset([[FlutterClippingMaskViewPool alloc] - initWithCapacity:kFlutterClippingMaskViewPoolCapacity]); - } - [mask_view_pool_.get() recycleMaskViews]; clipView.maskView = nil; - CGFloat screenScale = [UIScreen mainScreen].scale; auto iter = mutators_stack.Begin(); while (iter != mutators_stack.End()) { switch ((*iter)->GetType()) { @@ -498,9 +486,8 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, transformMatrix)) { break; } - ClipViewSetMaskView(clipView); - [(FlutterClippingMaskView*)clipView.maskView clipRect:(*iter)->GetRect() - matrix:transformMatrix]; + [maskView clipRect:(*iter)->GetRect() matrix:transformMatrix]; + clipView.maskView = maskView; break; } case kClipRRect: { @@ -508,18 +495,16 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, transformMatrix)) { break; } - ClipViewSetMaskView(clipView); - [(FlutterClippingMaskView*)clipView.maskView clipRRect:(*iter)->GetRRect() - matrix:transformMatrix]; + [maskView clipRRect:(*iter)->GetRRect() matrix:transformMatrix]; + clipView.maskView = maskView; break; } case kClipPath: { // TODO(cyanglaz): Find a way to pre-determine if path contains the PlatformView boudning // rect. See `ClipRRectContainsPlatformViewBoundingRect`. // https://github.com/flutter/flutter/issues/118650 - ClipViewSetMaskView(clipView); - [(FlutterClippingMaskView*)clipView.maskView clipPath:(*iter)->GetPath() - matrix:transformMatrix]; + [maskView clipPath:(*iter)->GetPath() matrix:transformMatrix]; + clipView.maskView = maskView; break; } case kOpacity: diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index d97acbbddf537..bc65fca1d8c5e 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -2455,125 +2455,6 @@ - (void)testHasFirstResponderInViewHierarchySubtree_descendantViewBecomesFirstRe XCTAssertFalse(view.flt_hasFirstResponderInViewHierarchySubtree); } -- (void)testFlutterClippingMaskViewPoolReuseViewsAfterRecycle { - FlutterClippingMaskViewPool* pool = - [[[FlutterClippingMaskViewPool alloc] initWithCapacity:2] autorelease]; - FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero]; - FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero]; - [pool recycleMaskViews]; - CGRect newRect = CGRectMake(0, 0, 10, 10); - FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:newRect]; - FlutterClippingMaskView* view4 = [pool getMaskViewWithFrame:newRect]; - XCTAssertEqual(view1, view3); - XCTAssertEqual(view2, view4); - XCTAssertTrue(CGRectEqualToRect(view3.frame, newRect)); - XCTAssertTrue(CGRectEqualToRect(view4.frame, newRect)); -} - -- (void)testFlutterClippingMaskViewPoolAllocsNewMaskViewsAfterReachingCapacity { - FlutterClippingMaskViewPool* pool = - [[[FlutterClippingMaskViewPool alloc] initWithCapacity:2] autorelease]; - FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero]; - FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero]; - FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:CGRectZero]; - XCTAssertNotEqual(view1, view3); - XCTAssertNotEqual(view2, view3); -} - -- (void)testMaskViewsReleasedWhenPoolIsReleased { - UIView* retainedView; - @autoreleasepool { - FlutterClippingMaskViewPool* pool = - [[[FlutterClippingMaskViewPool alloc] initWithCapacity:2] autorelease]; - FlutterClippingMaskView* view = [pool getMaskViewWithFrame:CGRectZero]; - retainedView = [view retain]; - XCTAssertGreaterThan(retainedView.retainCount, 1u); - } - // The only retain left is our manual retain called inside the autorelease pool, meaning the - // maskViews are dealloc'd. - XCTAssertEqual(retainedView.retainCount, 1u); -} - -- (void)testClipMaskViewIsReused { - flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; - auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest"); - flutter::TaskRunners runners(/*label=*/self.name.UTF8String, - /*platform=*/thread_task_runner, - /*raster=*/thread_task_runner, - /*ui=*/thread_task_runner, - /*io=*/thread_task_runner); - auto flutterPlatformViewsController = std::make_shared(); - auto platform_view = std::make_unique( - /*delegate=*/mock_delegate, - /*rendering_api=*/flutter::IOSRenderingAPI::kSoftware, - /*platform_views_controller=*/flutterPlatformViewsController, - /*task_runners=*/runners); - - FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = - [[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease]; - flutterPlatformViewsController->RegisterViewFactory( - factory, @"MockFlutterPlatformView", - FlutterPlatformViewGestureRecognizersBlockingPolicyEager); - FlutterResult result = ^(id result) { - }; - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}], - result); - - XCTAssertNotNil(gMockPlatformView); - UIView* mockFlutterView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)] autorelease]; - flutterPlatformViewsController->SetFlutterView(mockFlutterView); - // Create embedded view params - flutter::MutatorsStack stack1; - // Layer tree always pushes a screen scale factor to the stack - SkMatrix screenScaleMatrix = - SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale); - stack1.PushTransform(screenScaleMatrix); - // Push a clip rect - SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3); - stack1.PushClipRect(rect); - - auto embeddedViewParams1 = std::make_unique( - screenScaleMatrix, SkSize::Make(10, 10), stack1); - - flutter::MutatorsStack stack2; - auto embeddedViewParams2 = std::make_unique( - screenScaleMatrix, SkSize::Make(10, 10), stack2); - - flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1)); - flutterPlatformViewsController->CompositeEmbeddedView(1); - UIView* childClippingView1 = gMockPlatformView.superview.superview; - UIView* maskView1 = childClippingView1.maskView; - XCTAssertNotNil(maskView1); - - // Composite a new frame. - auto embeddedViewParams3 = std::make_unique( - screenScaleMatrix, SkSize::Make(10, 10), stack2); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams3)); - flutterPlatformViewsController->CompositeEmbeddedView(1); - childClippingView1 = gMockPlatformView.superview.superview; - - // This overrides gMockPlatformView to point to the newly created platform view. - flutterPlatformViewsController->OnMethodCall( - [FlutterMethodCall - methodCallWithMethodName:@"create" - arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], - result); - - auto embeddedViewParams4 = std::make_unique( - screenScaleMatrix, SkSize::Make(10, 10), stack1); - flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams4)); - flutterPlatformViewsController->CompositeEmbeddedView(2); - UIView* childClippingView2 = gMockPlatformView.superview.superview; - - UIView* maskView2 = childClippingView2.maskView; - XCTAssertEqual(maskView1, maskView2); - XCTAssertNotNil(childClippingView2.maskView); - XCTAssertNil(childClippingView1.maskView); -} - // Return true if a correct visual effect view is found. It also implies all the validation in this // method passes. // diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 1f767b378e644..7ec51e3b8ffcf 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -30,8 +30,6 @@ - (instancetype)initWithFrame:(CGRect)frame screenScale:(CGFloat)screenScale; -- (void)reset; - // Adds a clip rect operation to the queue. // // The `clipSkRect` is transformed with the `matrix` before adding to the queue. @@ -49,28 +47,6 @@ @end -// A pool that provides |FlutterClippingMaskView|s. -// -// The pool has a capacity that can be set in the initializer. -// When requesting a FlutterClippingMaskView, the pool will first try to reuse an available maskView -// in the pool. If there are none available, a new FlutterClippingMaskView is constructed. If the -// capacity is reached, the newly constructed FlutterClippingMaskView is not added to the pool. -// -// Call |recycleMaskViews| to mark all the FlutterClippingMaskViews in the pool available. -@interface FlutterClippingMaskViewPool : NSObject - -// Initialize the pool with `capacity`. When the `capacity` is reached, a FlutterClippingMaskView is -// constructed when requested, and it is not added to the pool. -- (instancetype)initWithCapacity:(NSInteger)capacity; - -// Reuse a maskView from the pool, or allocate a new one. -- (FlutterClippingMaskView*)getMaskViewWithFrame:(CGRect)frame; - -// Mark all the maskViews available. -- (void)recycleMaskViews; - -@end - // An object represents a blur filter. // // This object produces a `backdropFilterView`. @@ -292,7 +268,6 @@ class FlutterPlatformViewsController { // Traverse the `mutators_stack` and return the number of clip operations. int CountClips(const MutatorsStack& mutators_stack); - void ClipViewSetMaskView(UIView* clipView); // Applies the mutators in the mutators_stack to the UIView chain that was constructed by // `ReconstructClipViewsChain` // @@ -353,7 +328,6 @@ class FlutterPlatformViewsController { fml::scoped_nsobject channel_; fml::scoped_nsobject flutter_view_; fml::scoped_nsobject flutter_view_controller_; - fml::scoped_nsobject mask_view_pool_; std::map>> factories_; std::map>> views_; std::map> touch_interceptors_; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm index c7df3513e6ee3..da7f156bafe95 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm @@ -271,10 +271,6 @@ - (instancetype)initWithFrame:(CGRect)frame screenScale:(CGFloat)screenScale { return self; } -- (void)reset { - paths_.clear(); -} - // In some scenarios, when we add this view as a maskView of the ChildClippingView, iOS added // this view as a subview of the ChildClippingView. // This results this view blocking touch events on the ChildClippingView. @@ -451,66 +447,3 @@ - (void)clipPath:(const SkPath&)path matrix:(const SkMatrix&)matrix { } @end - -@interface FlutterClippingMaskViewPool () - -// The maximum number of `FlutterClippingMaskView` the pool can contain. -// This prevents the pool to grow infinately and limits the maximum memory a pool can use. -@property(assign, nonatomic) NSUInteger capacity; -@property(retain, nonatomic) NSMutableArray* pool; -// The index points to the first available FlutterClippingMaskView in the `pool`. -@property(assign, nonatomic) NSUInteger availableIndex; - -@end - -@implementation FlutterClippingMaskViewPool : NSObject - -- (instancetype)initWithCapacity:(NSInteger)capacity { - if (self = [super init]) { - _pool = [[NSMutableArray alloc] initWithCapacity:capacity]; - _capacity = capacity; - _availableIndex = 0; - } - return self; -} - -- (FlutterClippingMaskView*)getMaskViewWithFrame:(CGRect)frame { - FML_DCHECK(self.availableIndex <= self.capacity); - FlutterClippingMaskView* maskView; - if (self.availableIndex == self.capacity) { - // The pool is full, alloc a new one. - maskView = - [[[FlutterClippingMaskView alloc] initWithFrame:frame - screenScale:[UIScreen mainScreen].scale] autorelease]; - return maskView; - } - - if (self.availableIndex >= self.pool.count) { - // The pool doesn't have enough maskViews, alloc a new one and add to the pool. - maskView = - [[[FlutterClippingMaskView alloc] initWithFrame:frame - screenScale:[UIScreen mainScreen].scale] autorelease]; - [self.pool addObject:maskView]; - FML_DCHECK(self.pool.count <= self.capacity); - } else { - // Reuse a maskView from the pool. - maskView = [self.pool objectAtIndex:self.availableIndex]; - maskView.frame = frame; - [maskView reset]; - } - self.availableIndex++; - return maskView; -} - -- (void)recycleMaskViews { - self.availableIndex = 0; -} - -- (void)dealloc { - [_pool release]; - _pool = nil; - - [super dealloc]; -} - -@end