Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 89f258f

Browse files
author
Chris Yang
committed
recycle maskView at the begining of the frame
remove format revert format change fix typo draft rework recycle pool fix comments and documents
1 parent 1c76486 commit 89f258f

13 files changed

+387
-65
lines changed

shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
1919
#import "flutter/shell/platform/darwin/ios/ios_surface.h"
2020

21-
static const NSUInteger kFlutterClippingMaskViewPoolCapacity = 5;
22-
2321
@implementation UIView (FirstResponder)
2422
- (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
2523
if (self.isFirstResponder) {
@@ -461,12 +459,10 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect,
461459
NSMutableArray* blurFilters = [[[NSMutableArray alloc] init] autorelease];
462460
FML_DCHECK(!clipView.maskView ||
463461
[clipView.maskView isKindOfClass:[FlutterClippingMaskView class]]);
464-
if (mask_view_pool_.get() == nil) {
465-
mask_view_pool_.reset([[FlutterClippingMaskViewPool alloc]
466-
initWithCapacity:kFlutterClippingMaskViewPoolCapacity]);
462+
if (clipView.maskView) {
463+
[mask_view_pool_.get() insertViewToPool:(FlutterClippingMaskView*)(clipView.maskView)];
464+
clipView.maskView = nil;
467465
}
468-
[mask_view_pool_.get() recycleMaskViews];
469-
clipView.maskView = nil;
470466
CGFloat screenScale = [UIScreen mainScreen].scale;
471467
auto iter = mutators_stack.Begin();
472468
while (iter != mutators_stack.End()) {

shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2649,7 +2649,8 @@ - (void)testFlutterClippingMaskViewPoolReuseViewsAfterRecycle {
26492649
[[[FlutterClippingMaskViewPool alloc] initWithCapacity:2] autorelease];
26502650
FlutterClippingMaskView* view1 = [pool getMaskViewWithFrame:CGRectZero];
26512651
FlutterClippingMaskView* view2 = [pool getMaskViewWithFrame:CGRectZero];
2652-
[pool recycleMaskViews];
2652+
[pool insertViewToPool:view1];
2653+
[pool insertViewToPool:view2];
26532654
CGRect newRect = CGRectMake(0, 0, 10, 10);
26542655
FlutterClippingMaskView* view3 = [pool getMaskViewWithFrame:newRect];
26552656
FlutterClippingMaskView* view4 = [pool getMaskViewWithFrame:newRect];
@@ -2727,17 +2728,17 @@ - (void)testClipMaskViewIsReused {
27272728
auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
27282729
screenScaleMatrix, SkSize::Make(10, 10), stack1);
27292730

2730-
flutter::MutatorsStack stack2;
2731-
auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
2732-
screenScaleMatrix, SkSize::Make(10, 10), stack2);
2733-
27342731
flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1));
27352732
flutterPlatformViewsController->CompositeEmbeddedView(1);
27362733
UIView* childClippingView1 = gMockPlatformView.superview.superview;
27372734
UIView* maskView1 = childClippingView1.maskView;
27382735
XCTAssertNotNil(maskView1);
27392736

27402737
// Composite a new frame.
2738+
flutterPlatformViewsController->BeginFrame(SkISize::Make(100, 100));
2739+
flutter::MutatorsStack stack2;
2740+
auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
2741+
screenScaleMatrix, SkSize::Make(10, 10), stack2);
27412742
auto embeddedViewParams3 = std::make_unique<flutter::EmbeddedViewParams>(
27422743
screenScaleMatrix, SkSize::Make(10, 10), stack2);
27432744
flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams3));
@@ -2763,6 +2764,77 @@ - (void)testClipMaskViewIsReused {
27632764
XCTAssertNil(childClippingView1.maskView);
27642765
}
27652766

2767+
- (void)testDifferentClipMaskViewIsUsedForEachView {
2768+
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
2769+
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
2770+
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
2771+
/*platform=*/thread_task_runner,
2772+
/*raster=*/thread_task_runner,
2773+
/*ui=*/thread_task_runner,
2774+
/*io=*/thread_task_runner);
2775+
auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
2776+
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2777+
/*delegate=*/mock_delegate,
2778+
/*rendering_api=*/flutter::IOSRenderingAPI::kSoftware,
2779+
/*platform_views_controller=*/flutterPlatformViewsController,
2780+
/*task_runners=*/runners);
2781+
2782+
FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
2783+
[[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease];
2784+
flutterPlatformViewsController->RegisterViewFactory(
2785+
factory, @"MockFlutterPlatformView",
2786+
FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
2787+
FlutterResult result = ^(id result) {
2788+
};
2789+
2790+
flutterPlatformViewsController->OnMethodCall(
2791+
[FlutterMethodCall
2792+
methodCallWithMethodName:@"create"
2793+
arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
2794+
result);
2795+
UIView* view1 = gMockPlatformView;
2796+
2797+
// This overwrites `gMockPlatformView` to another view.
2798+
flutterPlatformViewsController->OnMethodCall(
2799+
[FlutterMethodCall
2800+
methodCallWithMethodName:@"create"
2801+
arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
2802+
result);
2803+
UIView* view2 = gMockPlatformView;
2804+
2805+
XCTAssertNotNil(gMockPlatformView);
2806+
UIView* mockFlutterView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)] autorelease];
2807+
flutterPlatformViewsController->SetFlutterView(mockFlutterView);
2808+
// Create embedded view params
2809+
flutter::MutatorsStack stack1;
2810+
// Layer tree always pushes a screen scale factor to the stack
2811+
SkMatrix screenScaleMatrix =
2812+
SkMatrix::Scale([UIScreen mainScreen].scale, [UIScreen mainScreen].scale);
2813+
stack1.PushTransform(screenScaleMatrix);
2814+
// Push a clip rect
2815+
SkRect rect = SkRect::MakeXYWH(2, 2, 3, 3);
2816+
stack1.PushClipRect(rect);
2817+
2818+
auto embeddedViewParams1 = std::make_unique<flutter::EmbeddedViewParams>(
2819+
screenScaleMatrix, SkSize::Make(10, 10), stack1);
2820+
2821+
flutter::MutatorsStack stack2;
2822+
stack2.PushClipRect(rect);
2823+
auto embeddedViewParams2 = std::make_unique<flutter::EmbeddedViewParams>(
2824+
screenScaleMatrix, SkSize::Make(10, 10), stack2);
2825+
2826+
flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams1));
2827+
flutterPlatformViewsController->CompositeEmbeddedView(1);
2828+
UIView* childClippingView1 = view1.superview.superview;
2829+
2830+
flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams2));
2831+
flutterPlatformViewsController->CompositeEmbeddedView(2);
2832+
UIView* childClippingView2 = view2.superview.superview;
2833+
UIView* maskView1 = childClippingView1.maskView;
2834+
UIView* maskView2 = childClippingView2.maskView;
2835+
XCTAssertNotEqual(maskView1, maskView2);
2836+
}
2837+
27662838
// Return true if a correct visual effect view is found. It also implies all the validation in this
27672839
// method passes.
27682840
//

shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
// in the pool. If there are none available, a new FlutterClippingMaskView is constructed. If the
5757
// capacity is reached, the newly constructed FlutterClippingMaskView is not added to the pool.
5858
//
59-
// Call |recycleMaskViews| to mark all the FlutterClippingMaskViews in the pool available.
59+
// Call |insertViewToPool:| to return a maskView to the pool.
6060
@interface FlutterClippingMaskViewPool : NSObject
6161

6262
// Initialize the pool with `capacity`. When the `capacity` is reached, a FlutterClippingMaskView is
@@ -66,8 +66,8 @@
6666
// Reuse a maskView from the pool, or allocate a new one.
6767
- (FlutterClippingMaskView*)getMaskViewWithFrame:(CGRect)frame;
6868

69-
// Mark all the maskViews available.
70-
- (void)recycleMaskViews;
69+
// Insert the `maskView` into the pool.
70+
- (void)insertViewToPool:(FlutterClippingMaskView*)maskView;
7171

7272
@end
7373

@@ -291,27 +291,30 @@ class FlutterPlatformViewsController {
291291
int CountClips(const MutatorsStack& mutators_stack);
292292

293293
void ClipViewSetMaskView(UIView* clipView);
294+
294295
// Applies the mutators in the mutators_stack to the UIView chain that was constructed by
295296
// `ReconstructClipViewsChain`
296297
//
297-
// Clips are applied to the super view with a CALayer mask. Transforms are applied to the
298-
// current view that's at the head of the chain. For example the following mutators stack [T_1,
299-
// C_2, T_3, T_4, C_5, T_6] where T denotes a transform and C denotes a clip, will result in the
300-
// following UIView tree:
301-
//
302-
// C_2 -> C_5 -> PLATFORM_VIEW
303-
// (PLATFORM_VIEW is a subview of C_5 which is a subview of C_2)
304-
//
305-
// T_1 is applied to C_2, T_3 and T_4 are applied to C_5, and T_6 is applied to PLATFORM_VIEW.
306-
//
307-
// After each clip operation, we update the head to the super view of the current head.
298+
// Clips are applied to the `embedded_view`'s super view(|ChildClippingView|) using a
299+
// |FlutterClippingMaskView|. Transforms are applied to `embedded_view`
308300
//
309301
// The `bounding_rect` is the final bounding rect of the PlatformView
310302
// (EmbeddedViewParams::finalBoundingRect). If a clip mutator's rect contains the final bounding
311303
// rect of the PlatformView, the clip mutator is not applied for performance optimization.
304+
//
305+
// This method is only called when thew `embedded_view` needs to be re-composited at the current
306+
// frame. See: `CompositeWithParams` for details.
312307
void ApplyMutators(const MutatorsStack& mutators_stack,
313308
UIView* embedded_view,
314309
const SkRect& bounding_rect);
310+
311+
// Composite the PlatformView with `view_id`.
312+
// Every frame, during the paint traversal of the layer tree, this method is called for all
313+
// the PlatformViews in `views_to_recomposite_`.
314+
//
315+
// Note that `views_to_recomposite_` does not represent all the views in the view hierarchy,
316+
// if a PlatformView does not change its composition parameter from last frame, it is not
317+
// included in the `views_to_recomposite_`.
315318
void CompositeWithParams(int64_t view_id, const EmbeddedViewParams& params);
316319

317320
// Allocates a new FlutterPlatformViewLayer if needed, draws the pixels within the rect from

shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#import "flutter/shell/platform/darwin/ios/ios_surface.h"
1010

1111
static int kMaxPointsInVerb = 4;
12+
static const NSUInteger kFlutterClippingMaskViewPoolCapacity = 5;
1213

1314
namespace flutter {
1415

@@ -26,7 +27,10 @@
2627

2728
FlutterPlatformViewsController::FlutterPlatformViewsController()
2829
: layer_pool_(std::make_unique<FlutterPlatformViewLayerPool>()),
29-
weak_factory_(std::make_unique<fml::WeakPtrFactory<FlutterPlatformViewsController>>(this)){};
30+
weak_factory_(std::make_unique<fml::WeakPtrFactory<FlutterPlatformViewsController>>(this)) {
31+
mask_view_pool_.reset(
32+
[[FlutterClippingMaskViewPool alloc] initWithCapacity:kFlutterClippingMaskViewPoolCapacity]);
33+
};
3034

3135
FlutterPlatformViewsController::~FlutterPlatformViewsController() = default;
3236

@@ -458,58 +462,51 @@ @interface FlutterClippingMaskViewPool ()
458462
// The maximum number of `FlutterClippingMaskView` the pool can contain.
459463
// This prevents the pool to grow infinately and limits the maximum memory a pool can use.
460464
@property(assign, nonatomic) NSUInteger capacity;
461-
@property(retain, nonatomic) NSMutableArray<FlutterClippingMaskView*>* pool;
462-
// The index points to the first available FlutterClippingMaskView in the `pool`.
463-
@property(assign, nonatomic) NSUInteger availableIndex;
465+
466+
// The pool contains the views that are available to use.
467+
// The number of items in the pool must not excceds `capacity`.
468+
@property(retain, nonatomic) NSMutableSet<FlutterClippingMaskView*>* pool;
464469

465470
@end
466471

467472
@implementation FlutterClippingMaskViewPool : NSObject
468473

469474
- (instancetype)initWithCapacity:(NSInteger)capacity {
470475
if (self = [super init]) {
471-
_pool = [[NSMutableArray alloc] initWithCapacity:capacity];
476+
_pool = [[NSMutableSet alloc] init];
472477
_capacity = capacity;
473-
_availableIndex = 0;
474478
}
475479
return self;
476480
}
477481

478482
- (FlutterClippingMaskView*)getMaskViewWithFrame:(CGRect)frame {
479-
FML_DCHECK(self.availableIndex <= self.capacity);
483+
FML_DCHECK(self.pool.count <= self.capacity);
480484
FlutterClippingMaskView* maskView;
481-
if (self.availableIndex == self.capacity) {
482-
// The pool is full, alloc a new one.
485+
if (self.pool.count == 0) {
486+
// The pool is empty, alloc a new one.
483487
maskView =
484488
[[[FlutterClippingMaskView alloc] initWithFrame:frame
485489
screenScale:[UIScreen mainScreen].scale] autorelease];
486490
return maskView;
487491
}
488-
489-
if (self.availableIndex >= self.pool.count) {
490-
// The pool doesn't have enough maskViews, alloc a new one and add to the pool.
491-
maskView =
492-
[[[FlutterClippingMaskView alloc] initWithFrame:frame
493-
screenScale:[UIScreen mainScreen].scale] autorelease];
494-
[self.pool addObject:maskView];
495-
FML_DCHECK(self.pool.count <= self.capacity);
496-
} else {
497-
// Reuse a maskView from the pool.
498-
maskView = [self.pool objectAtIndex:self.availableIndex];
499-
maskView.frame = frame;
500-
[maskView reset];
501-
}
502-
self.availableIndex++;
492+
maskView = [self.pool anyObject];
493+
maskView.frame = frame;
494+
[maskView reset];
495+
[self.pool removeObject:maskView];
503496
return maskView;
504497
}
505498

506-
- (void)recycleMaskViews {
507-
self.availableIndex = 0;
499+
- (void)insertViewToPool:(FlutterClippingMaskView*)maskView {
500+
FML_DCHECK(![self.pool containsObject:maskView]);
501+
FML_DCHECK([self.pool.count <= self.capacity]);
502+
if (self.pool.count == self.capacity) {
503+
return;
504+
}
505+
[self.pool addObject:maskView];
508506
}
509507

510508
- (void)dealloc {
511509
[_pool release];
512-
_pool = nil;
513510

514511
[super dealloc];
515512
}

0 commit comments

Comments
 (0)