From 5720b8a116ec14b617b9eb8079cf3ac59c9782f8 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Mon, 9 May 2022 14:25:49 -0700 Subject: [PATCH 1/2] thraed merge to end frame draft test code format revert revert testing code cleanup clea nup revert scenario test for overlay view frame test thread merge at end frame add scenario tests fix typo --- .../framework/Source/FlutterPlatformViews.mm | 20 ++++-- .../Source/FlutterPlatformViewsTest.mm | 65 ++++++++++++++++- .../Source/FlutterPlatformViews_Internal.h | 3 + .../darwin/ios/ios_external_view_embedder.mm | 2 +- .../UnobstructedPlatformViewTests.m | 70 +++++++++++++++++++ 5 files changed, 151 insertions(+), 9 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 9a5387cf86b18..f04a444e41cfd 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -277,7 +277,6 @@ // Eventually, the frame is submitted once this method returns `kSuccess`. // At that point, the raster tasks are handled on the platform thread. CancelFrame(); - raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); return PostPrerollResult::kSkipAndRetryFrame; } // If the post preroll action is successful, we will display platform views in the current frame. @@ -289,6 +288,14 @@ return PostPrerollResult::kSuccess; } +void FlutterPlatformViewsController::EndFrame( + bool should_resubmit_frame, + fml::RefPtr raster_thread_merger) { + if (should_resubmit_frame) { + raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); + } +} + void FlutterPlatformViewsController::PrerollCompositeEmbeddedView( int view_id, std::unique_ptr params) { @@ -609,14 +616,18 @@ // This wrapper view masks the overlay view. overlay_view_wrapper.frame = CGRectMake(rect.x() / screenScale, rect.y() / screenScale, rect.width() / screenScale, rect.height() / screenScale); - // Set a unique view identifier, so the overlay wrapper can be identified in unit tests. + // Set a unique view identifier, so the overlay_view_wrapper can be identified in XCUITests. overlay_view_wrapper.accessibilityIdentifier = [NSString stringWithFormat:@"platform_view[%lld].overlay[%lld]", view_id, overlay_id]; UIView* overlay_view = layer->overlay_view.get(); // Set the size of the overlay view. // This size is equal to the device screen size. - overlay_view.frame = flutter_view_.get().bounds; + overlay_view.frame = [flutter_view_.get() convertRect:flutter_view_.get().bounds + toView:overlay_view_wrapper]; + // Set a unique view identifier, so the overlay_view can be identified in XCUITests. + overlay_view.accessibilityIdentifier = + [NSString stringWithFormat:@"platform_view[%lld].overlay_view[%lld]", view_id, overlay_id]; std::unique_ptr frame = layer->surface->AcquireFrame(frame_size_); // If frame is null, AcquireFrame already printed out an error message. @@ -625,9 +636,6 @@ } SkCanvas* overlay_canvas = frame->SkiaCanvas(); overlay_canvas->clear(SK_ColorTRANSPARENT); - // Offset the picture since its absolute position on the scene is determined - // by the position of the overlay view. - overlay_canvas->translate(-rect.x(), -rect.y()); overlay_canvas->drawPicture(picture); layer->did_submit_last_frame = frame->Submit(); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index fba267419a116..08a109ace49fb 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -979,11 +979,11 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams_2)); flutterPlatformViewsController->CompositeEmbeddedView(2); - auto mock_surface_submit_false = std::make_unique( + auto mock_surface_submit_true = std::make_unique( nullptr, framebuffer_info, [](const flutter::SurfaceFrame& surface_frame, SkCanvas* canvas) { return true; }); XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, - std::move(mock_surface_submit_false))); + std::move(mock_surface_submit_true))); } - (void) @@ -1088,6 +1088,67 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { XCTAssertEqual(flutterPlatformViewsController->GetCurrentCanvases().size(), 1UL); } +- (void)testThreadMergeAtEndFrame { + flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; + auto thread_task_runner_platform = CreateNewThread("FlutterPlatformViewsTest1"); + auto thread_task_runner_other = CreateNewThread("FlutterPlatformViewsTest2"); + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, + /*platform=*/thread_task_runner_platform, + /*raster=*/thread_task_runner_other, + /*ui=*/thread_task_runner_other, + /*io=*/thread_task_runner_other); + 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); + + UIView* mockFlutterView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)] autorelease]; + flutterPlatformViewsController->SetFlutterView(mockFlutterView); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + XCTestExpectation* waitForPlatformView = + [self expectationWithDescription:@"wait for platform view to be created"]; + FlutterResult result = ^(id result) { + [waitForPlatformView fulfill]; + }; + + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}], + result); + [self waitForExpectations:@[ waitForPlatformView ] timeout:30]; + XCTAssertNotNil(gMockPlatformView); + + flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); + SkMatrix finalMatrix; + flutter::MutatorsStack stack; + auto embeddedViewParams = + std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(2, std::move(embeddedViewParams)); + + fml::RefPtr raster_thread_merger = + fml::MakeRefCounted(thread_task_runner_platform->GetTaskQueueId(), + thread_task_runner_other->GetTaskQueueId()); + XCTAssertEqual(flutterPlatformViewsController->PostPrerollAction(raster_thread_merger), + flutter::PostPrerollResult::kSkipAndRetryFrame); + XCTAssertFalse(raster_thread_merger->IsMerged()); + + flutterPlatformViewsController->EndFrame(true, raster_thread_merger); + XCTAssertTrue(raster_thread_merger->IsMerged()); + + // Unmerge threads before the end of the test + // TaskRunners are required to be unmerged before destruction. + while (raster_thread_merger->DecrementLease() != fml::RasterThreadStatus::kUnmergedNow) + ; +} + - (int)alphaOfPoint:(CGPoint)point onView:(UIView*)view { unsigned char pixel[4] = {0}; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 8d42bc879488d..c20f957633954 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -160,6 +160,9 @@ class FlutterPlatformViewsController { PostPrerollResult PostPrerollAction(fml::RefPtr raster_thread_merger); + void EndFrame(bool should_resubmit_frame, + fml::RefPtr raster_thread_merger); + std::vector GetCurrentCanvases(); SkCanvas* CompositeEmbeddedView(int view_id); diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm index a4921af254af9..1323fa4c2f487 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.mm +++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm @@ -84,7 +84,7 @@ void IOSExternalViewEmbedder::EndFrame(bool should_resubmit_frame, fml::RefPtr raster_thread_merger) { TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::EndFrame"); - FML_CHECK(platform_views_controller_); + platform_views_controller_->EndFrame(should_resubmit_frame, raster_thread_merger); } // |ExternalViewEmbedder| diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m index ac301f3922b3b..b23f3131e59e2 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m @@ -4,6 +4,8 @@ #import +static const CGFloat kCompareAccuracy = 0.001; + @interface UnobstructedPlatformViewTests : XCTestCase @end @@ -60,6 +62,15 @@ - (void)testOneOverlay { XCTAssertEqual(overlay.frame.origin.y, 150); XCTAssertEqual(overlay.frame.size.width, 50); XCTAssertEqual(overlay.frame.size.height, 50); + + XCUIElement* overlayView = app.otherElements[@"platform_view[0].overlay_view[0]"]; + XCTAssertTrue(overlayView.exists); + // Overlay should always be the same frame as the app. + XCTAssertEqualWithAccuracy(overlayView.frame.origin.x, app.frame.origin.x, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView.frame.origin.y, app.frame.origin.x, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView.frame.size.width, app.frame.size.width, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView.frame.size.height, app.frame.size.height, + kCompareAccuracy); } // A is the layer above the platform view. @@ -86,6 +97,15 @@ - (void)testOneOverlayPartialIntersection { XCTAssertEqual(overlay.frame.size.width, 50); // Half the height of the overlay. XCTAssertEqual(overlay.frame.size.height, 25); + + XCUIElement* overlayView = app.otherElements[@"platform_view[0].overlay_view[0]"]; + XCTAssertTrue(overlayView.exists); + // Overlay should always be the same frame as the app. + XCTAssertEqualWithAccuracy(overlayView.frame.origin.x, app.frame.origin.x, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView.frame.origin.y, app.frame.origin.x, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView.frame.size.width, app.frame.size.width, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView.frame.size.height, app.frame.size.height, + kCompareAccuracy); } // A and B are the layers above the platform view. @@ -149,6 +169,24 @@ - (void)testOneOverlayAndTwoIntersectingOverlays { XCTAssertEqual(overlay2.frame.origin.y, 225); XCTAssertEqual(overlay2.frame.size.width, 50); XCTAssertEqual(overlay2.frame.size.height, 50); + + XCUIElement* overlayView0 = app.otherElements[@"platform_view[0].overlay_view[0]"]; + XCTAssertTrue(overlayView0.exists); + // Overlay should always be the same frame as the app. + XCTAssertEqualWithAccuracy(overlayView0.frame.origin.x, app.frame.origin.x, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView0.frame.origin.y, app.frame.origin.x, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView0.frame.size.width, app.frame.size.width, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView0.frame.size.height, app.frame.size.height, + kCompareAccuracy); + + XCUIElement* overlayView1 = app.otherElements[@"platform_view[0].overlay_view[0]"]; + XCTAssertTrue(overlayView1.exists); + // Overlay should always be the same frame as the app. + XCTAssertEqualWithAccuracy(overlayView1.frame.origin.x, app.frame.origin.x, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView1.frame.origin.y, app.frame.origin.x, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView1.frame.size.width, app.frame.size.width, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView1.frame.size.height, app.frame.size.height, + kCompareAccuracy); } // A is the layer, which z index is higher than the platform view. @@ -179,6 +217,8 @@ - (void)testMultiplePlatformViewsWithoutOverlays { XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[0]"].exists); XCTAssertFalse(app.otherElements[@"platform_view[1].overlay[0]"].exists); + XCTAssertFalse(app.otherElements[@"platform_view[0].overlay_view[0]"].exists); + XCTAssertFalse(app.otherElements[@"platform_view[1].overlay_view[0]"].exists); } // A is the layer above both platform view. @@ -220,6 +260,24 @@ - (void)testMultiplePlatformViewsWithOverlays { XCTAssertEqual(overlay2.frame.origin.y, 25); XCTAssertEqual(overlay2.frame.size.width, 200); XCTAssertEqual(overlay2.frame.size.height, 250); + + XCUIElement* overlayView0 = app.otherElements[@"platform_view[0].overlay_view[0]"]; + XCTAssertTrue(overlayView0.exists); + // Overlay should always be the same frame as the app. + XCTAssertEqualWithAccuracy(overlayView0.frame.origin.x, app.frame.origin.x, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView0.frame.origin.y, app.frame.origin.x, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView0.frame.size.width, app.frame.size.width, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView0.frame.size.height, app.frame.size.height, + kCompareAccuracy); + + XCUIElement* overlayView1 = app.otherElements[@"platform_view[1].overlay_view[0]"]; + XCTAssertTrue(overlayView1.exists); + // Overlay should always be the same frame as the app. + XCTAssertEqualWithAccuracy(overlayView1.frame.origin.x, app.frame.origin.x, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView1.frame.origin.y, app.frame.origin.x, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView1.frame.size.width, app.frame.size.width, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView1.frame.size.height, app.frame.size.height, + kCompareAccuracy); } // More then two overlays are merged into a single layer. @@ -246,6 +304,18 @@ - (void)testPlatformViewsMaxOverlays { XCTAssertTrue(overlay.exists); XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[1]"].exists); XCTAssertTrue(CGRectContainsRect(platform_view.frame, overlay.frame)); + + XCUIElement* overlayView0 = app.otherElements[@"platform_view[0].overlay_view[0]"]; + XCTAssertTrue(overlayView0.exists); + // Overlay should always be the same frame as the app. + XCTAssertEqualWithAccuracy(overlayView0.frame.origin.x, app.frame.origin.x, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView0.frame.origin.y, app.frame.origin.x, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView0.frame.size.width, app.frame.size.width, kCompareAccuracy); + XCTAssertEqualWithAccuracy(overlayView0.frame.size.height, app.frame.size.height, + kCompareAccuracy); + + XCUIElement* overlayView1 = app.otherElements[@"platform_view[0].overlay_view[1]"]; + XCTAssertFalse(overlayVie1.exists); } @end From eb94a6b87f508279f6013a9591ca84385bbca1d3 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Thu, 12 May 2022 13:07:08 -0700 Subject: [PATCH 2/2] typo --- .../Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m index b23f3131e59e2..51ab28fadeaae 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m @@ -315,7 +315,7 @@ - (void)testPlatformViewsMaxOverlays { kCompareAccuracy); XCUIElement* overlayView1 = app.otherElements[@"platform_view[0].overlay_view[1]"]; - XCTAssertFalse(overlayVie1.exists); + XCTAssertFalse(overlayView1.exists); } @end