Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm
Original file line number Diff line number Diff line change
Expand Up @@ -750,17 +750,34 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect,
UIView* flutter_view = flutter_view_.get();
// Clear the `active_composition_order_`, which will be populated down below.
active_composition_order_.clear();
NSMutableArray* desired_platform_subviews = [NSMutableArray array];
for (size_t i = 0; i < composition_order_.size(); i++) {
int64_t platform_view_id = composition_order_[i];
std::vector<std::shared_ptr<FlutterPlatformViewLayer>> layers = layer_map[platform_view_id];
UIView* platform_view_root = root_views_[platform_view_id].get();
// `addSubview` will automatically reorder subview if it is already added.
[flutter_view addSubview:platform_view_root];
[desired_platform_subviews addObject:platform_view_root];
for (const std::shared_ptr<FlutterPlatformViewLayer>& layer : layers) {
[flutter_view addSubview:layer->overlay_view_wrapper];
[desired_platform_subviews addObject:layer->overlay_view_wrapper];
}
active_composition_order_.push_back(platform_view_id);
}

NSSet* desired_platform_subviews_set = [NSSet setWithArray:desired_platform_subviews];
NSArray* existing_platform_subviews = [flutter_view.subviews
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object,
NSDictionary* bindings) {
return [desired_platform_subviews_set containsObject:object];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BringLayersIntoView is meant to be called after RemoveUnusedLayers();, so I think any view in existing_platform_subviews should already be in desired_platform_subviews_set? Did you actually see this filter reduces the size of existing_platform_subviews?

Maybe instead we can NSAssert it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you actually see this filter reduces the size of existing_platform_subviews?

Yes, and my understanding is that there are non-platform views added somewhere

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if it's dummy scroll view on the top status bar that I saw (which enables tap-to-scroll for table views), but that's not important.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah ok forgot about those. Yeah makes sense.

}]];
// Manipulate view hierarchy only if needed, to address a performance issue where
// `BringLayersIntoView` is called even when view hierarchy stays the same.
// See: https://github.com/flutter/flutter/issues/121833
// TODO(hellohuanlin): investigate if it is possible to skip unnecessary BringLayersIntoView.
if (![desired_platform_subviews isEqualToArray:existing_platform_subviews]) {
for (UIView* subview in desired_platform_subviews) {
// `addSubview` will automatically reorder subview if it is already added.
[flutter_view addSubview:subview];
}
}
}

std::shared_ptr<FlutterPlatformViewLayer> FlutterPlatformViewsController::GetLayer(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2342,7 +2342,8 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder {
XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL);
}

- (void)testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectly {
- (void)
testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithDifferentViewHierarchy {
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
Expand Down Expand Up @@ -2437,6 +2438,102 @@ - (void)testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectly
@"The first clipping view should be added after the second clipping view.");
}

- (void)
testFlutterPlatformViewControllerSubmitFrameShouldOrderSubviewsCorrectlyWithSameViewHierarchy {
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<flutter::FlutterPlatformViewsController>();
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
/*delegate=*/mock_delegate,
/*rendering_api=*/flutter::IOSRenderingAPI::kSoftware,
/*platform_views_controller=*/flutterPlatformViewsController,
/*task_runners=*/runners);

UIView* mockFlutterView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)] autorelease];
flutterPlatformViewsController->SetFlutterView(mockFlutterView);

FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
[[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease];
flutterPlatformViewsController->RegisterViewFactory(
factory, @"MockFlutterPlatformView",
FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
FlutterResult result = ^(id result) {
};
flutterPlatformViewsController->OnMethodCall(
[FlutterMethodCall
methodCallWithMethodName:@"create"
arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}],
result);
UIView* view1 = gMockPlatformView;

// This overwrites `gMockPlatformView` to another view.
flutterPlatformViewsController->OnMethodCall(
[FlutterMethodCall
methodCallWithMethodName:@"create"
arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}],
result);
UIView* view2 = gMockPlatformView;

flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
flutter::MutatorsStack stack;
SkMatrix finalMatrix;
auto embeddedViewParams1 =
std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
flutterPlatformViewsController->CompositeEmbeddedView(0);
auto embeddedViewParams2 =
std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(500, 500), stack);
flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2));
flutterPlatformViewsController->CompositeEmbeddedView(1);

// SKSurface is required if the root FlutterView is present.
const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000);
sk_sp<SkSurface> mock_sk_surface = SkSurface::MakeRaster(image_info);
flutter::SurfaceFrame::FramebufferInfo framebuffer_info;
auto mock_surface = std::make_unique<flutter::SurfaceFrame>(
std::move(mock_sk_surface), framebuffer_info,
[](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
/*frame_size=*/SkISize::Make(800, 600));

XCTAssertTrue(
flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
// platform view is wrapped by touch interceptor, which itself is wrapped by clipping view.
UIView* clippingView1 = view1.superview.superview;
UIView* clippingView2 = view2.superview.superview;
UIView* flutterView = clippingView1.superview;
XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] <
[flutterView.subviews indexOfObject:clippingView2],
@"The first clipping view should be added before the second clipping view.");

// Need to recreate these params since they are `std::move`ed.
flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300));
// Process the second frame in the same order.
embeddedViewParams1 =
std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(300, 300), stack);
flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1));
flutterPlatformViewsController->CompositeEmbeddedView(0);
embeddedViewParams2 =
std::make_unique<flutter::EmbeddedViewParams>(finalMatrix, SkSize::Make(500, 500), stack);
flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2));
flutterPlatformViewsController->CompositeEmbeddedView(1);

mock_sk_surface = SkSurface::MakeRaster(image_info);
mock_surface = std::make_unique<flutter::SurfaceFrame>(
std::move(mock_sk_surface), framebuffer_info,
[](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; },
/*frame_size=*/SkISize::Make(800, 600));
XCTAssertTrue(
flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)));
XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] <
[flutterView.subviews indexOfObject:clippingView2],
@"The first clipping view should be added before the second clipping view.");
}

- (void)testThreadMergeAtEndFrame {
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
auto thread_task_runner_platform = CreateNewThread("FlutterPlatformViewsTest1");
Expand Down