From 7a1fd055b8a4e9f1226d522d0ff566a3d4ec6990 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 13 Aug 2024 22:55:36 +0200 Subject: [PATCH] Preserve background frame damage (flutter/engine#54540) Fixes https://github.com/flutter/flutter/issues/153335 When platform views are present, we always repaint entire frame. We must pass the buffer damage all the way to `GPUSurfaceMetalSkia/GPUSurfaceMetalImpeller` so that they know that the other two surfaces are lagging behind front surface and need to be repainted. This is currently not being done with platform view present, that's why we miss full frame repaint when removing platform view. With this merged in, it should be safe to reland https://github.com/flutter/engine/pull/54537 without any changes. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style --- .../Source/FlutterPlatformViewsTest.mm | 80 +++++++++++++++++++ .../Source/platform_views_controller.mm | 7 +- 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 05e999777b0..ec44e2af421 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -3388,4 +3388,84 @@ fml::RefPtr GetDefaultTaskRunner() { XCTAssertEqual(pool.size(), 1u); } +- (void)testFlutterPlatformViewControllerSubmitFramePreservingFrameDamage { + flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate; + + flutter::TaskRunners runners(/*label=*/self.name.UTF8String, + /*platform=*/GetDefaultTaskRunner(), + /*raster=*/GetDefaultTaskRunner(), + /*ui=*/GetDefaultTaskRunner(), + /*io=*/GetDefaultTaskRunner()); + auto flutterPlatformViewsController = std::make_shared(); + flutterPlatformViewsController->SetTaskRunner(GetDefaultTaskRunner()); + auto platform_view = std::make_unique( + /*delegate=*/mock_delegate, + /*rendering_api=*/mock_delegate.settings_.enable_impeller + ? flutter::IOSRenderingAPI::kMetal + : flutter::IOSRenderingAPI::kSoftware, + /*platform_views_controller=*/flutterPlatformViewsController, + /*task_runners=*/runners, + /*worker_task_runner=*/nil, + /*is_gpu_disabled_jsync_switch=*/std::make_shared()); + + UIView* flutterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + flutterPlatformViewsController->SetFlutterView(flutterView); + + FlutterPlatformViewsTestMockFlutterPlatformFactory* factory = + [[FlutterPlatformViewsTestMockFlutterPlatformFactory alloc] init]; + flutterPlatformViewsController->RegisterViewFactory( + factory, @"MockFlutterPlatformView", + FlutterPlatformViewGestureRecognizersBlockingPolicyEager); + FlutterResult result = ^(id result) { + }; + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @0, @"viewType" : @"MockFlutterPlatformView"}], + result); + + // This overwrites `gMockPlatformView` to another view. + flutterPlatformViewsController->OnMethodCall( + [FlutterMethodCall + methodCallWithMethodName:@"create" + arguments:@{@"id" : @1, @"viewType" : @"MockFlutterPlatformView"}], + result); + + flutterPlatformViewsController->BeginFrame(SkISize::Make(300, 300)); + flutter::MutatorsStack stack; + SkMatrix finalMatrix; + auto embeddedViewParams1 = + std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(0, std::move(embeddedViewParams1)); + + auto embeddedViewParams2 = + std::make_unique(finalMatrix, SkSize::Make(500, 500), stack); + flutterPlatformViewsController->PrerollCompositeEmbeddedView(1, std::move(embeddedViewParams2)); + + // SKSurface is required if the root FlutterView is present. + const SkImageInfo image_info = SkImageInfo::MakeN32Premul(1000, 1000); + sk_sp mock_sk_surface = SkSurfaces::Raster(image_info); + flutter::SurfaceFrame::FramebufferInfo framebuffer_info; + std::optional submit_info; + auto mock_surface = std::make_unique( + std::move(mock_sk_surface), framebuffer_info, + [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, + [&](const flutter::SurfaceFrame& surface_frame) { + submit_info = surface_frame.submit_info(); + return true; + }, + /*frame_size=*/SkISize::Make(800, 600)); + mock_surface->set_submit_info({ + .frame_damage = SkIRect::MakeWH(800, 600), + .buffer_damage = SkIRect::MakeWH(400, 600), + }); + + XCTAssertTrue( + flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + + XCTAssertTrue(submit_info.has_value()); + XCTAssertEqual(*submit_info->frame_damage, SkIRect::MakeWH(800, 600)); + XCTAssertEqual(*submit_info->buffer_damage, SkIRect::MakeWH(400, 600)); +} + @end diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/platform_views_controller.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/platform_views_controller.mm index 806d9510011..0ad4ad6ff55 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/platform_views_controller.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/platform_views_controller.mm @@ -679,7 +679,12 @@ bool PlatformViewsController::SubmitFrame(GrDirectContext* gr_context, overlay_id++; } - background_frame->set_submit_info({.present_with_transaction = true}); + auto previous_submit_info = background_frame->submit_info(); + background_frame->set_submit_info({ + .frame_damage = previous_submit_info.frame_damage, + .buffer_damage = previous_submit_info.buffer_damage, + .present_with_transaction = true, + }); background_frame->Encode(); surface_frames.push_back(std::move(background_frame));