diff --git a/engine/src/flutter/flow/surface_frame.h b/engine/src/flutter/flow/surface_frame.h index d88e9a21b6..da5516feaa 100644 --- a/engine/src/flutter/flow/surface_frame.h +++ b/engine/src/flutter/flow/surface_frame.h @@ -80,6 +80,13 @@ class SurfaceFrame { // Time at which this frame is scheduled to be presented. This is a hint // that can be passed to the platform to drop queued frames. std::optional presentation_time; + + // Whether this surface frame represents the last in a group frames that + // were submitted as part of a platform compositor interop step, such as + // during iOS platform view compositing. + // + // Defaults to true, which is generally a safe value. + bool frame_boundary = true; }; bool Submit(); diff --git a/engine/src/flutter/shell/gpu/gpu_surface_gl_impeller.cc b/engine/src/flutter/shell/gpu/gpu_surface_gl_impeller.cc index 2c45762a51..ca2c90da8f 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_gl_impeller.cc +++ b/engine/src/flutter/shell/gpu/gpu_surface_gl_impeller.cc @@ -127,14 +127,16 @@ std::unique_ptr GPUSurfaceGLImpeller::AcquireFrame( impeller_dispatcher, SkIRect::MakeWH(cull_rect.width, cull_rect.height)); auto picture = impeller_dispatcher.EndRecordingAsPicture(); + const bool reset_host_buffer = + surface_frame.submit_info().frame_boundary; return renderer->Render( std::move(surface), fml::MakeCopyable( - [aiks_context, picture = std::move(picture)]( + [aiks_context, picture = std::move(picture), reset_host_buffer]( impeller::RenderTarget& render_target) -> bool { return aiks_context->Render(picture, render_target, - /*reset_host_buffer=*/true); + reset_host_buffer); })); }); diff --git a/engine/src/flutter/shell/gpu/gpu_surface_metal_impeller.mm b/engine/src/flutter/shell/gpu/gpu_surface_metal_impeller.mm index 67bd3997ef..04a48a06c5 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_metal_impeller.mm +++ b/engine/src/flutter/shell/gpu/gpu_surface_metal_impeller.mm @@ -187,12 +187,13 @@ std::unique_ptr GPUSurfaceMetalImpeller::AcquireFrameFromCAMetalLa impeller::DlDispatcher impeller_dispatcher(cull_rect); display_list->Dispatch(impeller_dispatcher, sk_cull_rect); auto picture = impeller_dispatcher.EndRecordingAsPicture(); + const bool reset_host_buffer = surface_frame.submit_info().frame_boundary; return renderer->Render( std::move(surface), - fml::MakeCopyable([aiks_context, picture = std::move(picture)]( - impeller::RenderTarget& render_target) -> bool { - return aiks_context->Render(picture, render_target, /*reset_host_buffer=*/true); + fml::MakeCopyable([aiks_context, picture = std::move(picture), + reset_host_buffer](impeller::RenderTarget& render_target) -> bool { + return aiks_context->Render(picture, render_target, reset_host_buffer); })); #endif }); @@ -307,11 +308,12 @@ std::unique_ptr GPUSurfaceMetalImpeller::AcquireFrameFromMTLTextur display_list->Dispatch(impeller_dispatcher, sk_cull_rect); auto picture = impeller_dispatcher.EndRecordingAsPicture(); + const bool reset_host_buffer = surface_frame.submit_info().frame_boundary; bool render_result = renderer->Render( std::move(surface), - fml::MakeCopyable([aiks_context, picture = std::move(picture)]( - impeller::RenderTarget& render_target) -> bool { - return aiks_context->Render(picture, render_target, /*reset_host_buffer=*/true); + fml::MakeCopyable([aiks_context, picture = std::move(picture), + reset_host_buffer](impeller::RenderTarget& render_target) -> bool { + return aiks_context->Render(picture, render_target, reset_host_buffer); })); #endif if (!render_result) { diff --git a/engine/src/flutter/shell/gpu/gpu_surface_metal_impeller_unittests.mm b/engine/src/flutter/shell/gpu/gpu_surface_metal_impeller_unittests.mm index 383d57b2c6..13ccbba3f6 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_metal_impeller_unittests.mm +++ b/engine/src/flutter/shell/gpu/gpu_surface_metal_impeller_unittests.mm @@ -97,5 +97,32 @@ TEST(GPUSurfaceMetalImpeller, AcquireFrameFromCAMetalLayerDoesNotRetainThis) { ASSERT_TRUE(frame->Submit()); } +TEST(GPUSurfaceMetalImpeller, ResetHostBufferBasedOnFrameBoundary) { + auto delegate = std::make_shared(); + delegate->SetDevice(); + + auto context = CreateImpellerContext(); + std::unique_ptr surface = + std::make_unique(delegate.get(), CreateImpellerContext()); + + ASSERT_TRUE(surface->IsValid()); + + auto& host_buffer = surface->GetAiksContext()->GetContentContext().GetTransientsBuffer(); + + EXPECT_EQ(host_buffer.GetStateForTest().current_frame, 0u); + + auto frame = surface->AcquireFrame(SkISize::Make(100, 100)); + frame->set_submit_info({.frame_boundary = false}); + + ASSERT_TRUE(frame->Submit()); + EXPECT_EQ(host_buffer.GetStateForTest().current_frame, 0u); + + frame = surface->AcquireFrame(SkISize::Make(100, 100)); + frame->set_submit_info({.frame_boundary = true}); + + ASSERT_TRUE(frame->Submit()); + EXPECT_EQ(host_buffer.GetStateForTest().current_frame, 1u); +} + } // namespace testing } // namespace flutter diff --git a/engine/src/flutter/shell/gpu/gpu_surface_vulkan_impeller.cc b/engine/src/flutter/shell/gpu/gpu_surface_vulkan_impeller.cc index 3c0824f854..5da0e5eb51 100644 --- a/engine/src/flutter/shell/gpu/gpu_surface_vulkan_impeller.cc +++ b/engine/src/flutter/shell/gpu/gpu_surface_vulkan_impeller.cc @@ -117,9 +117,10 @@ std::unique_ptr GPUSurfaceVulkanImpeller::AcquireFrame( impeller_dispatcher, SkIRect::MakeWH(cull_rect.width, cull_rect.height)); auto picture = impeller_dispatcher.EndRecordingAsPicture(); - + const bool reset_host_buffer = + surface_frame.submit_info().frame_boundary; return aiks_context->Render(picture, render_target, - /*reset_host_buffer=*/true); + reset_host_buffer); #endif })); }); diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 46aafd83e5..551be716b1 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -864,6 +864,10 @@ std::shared_ptr FlutterPlatformViewsController::GetLay slice->render_into(overlay_canvas); overlay_canvas->RestoreToCount(restore_count); + // This flutter view is never the last in a frame, since we always submit the + // underlay view last. + frame->set_submit_info({.frame_boundary = false}); + layer->did_submit_last_frame = frame->Submit(); return layer; }