[Impeller] mark the end of a frame boundary for system compositor interop. (flutter/engine#53722)

Fixes https://github.com/flutter/flutter/issues/151274

This is only an issue on iOS (so far) because of the platform view rendering strategy that involves submitting multiple impeller frames per compositor frame.

FYI @bdero
This commit is contained in:
Jonah Williams 2024-07-03 20:48:17 -07:00 committed by GitHub
parent b4a744f468
commit efbf7ddc57
6 changed files with 53 additions and 10 deletions

View File

@ -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<fml::TimePoint> 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();

View File

@ -127,14 +127,16 @@ std::unique_ptr<SurfaceFrame> 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);
}));
});

View File

@ -187,12 +187,13 @@ std::unique_ptr<SurfaceFrame> 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<SurfaceFrame> 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) {

View File

@ -97,5 +97,32 @@ TEST(GPUSurfaceMetalImpeller, AcquireFrameFromCAMetalLayerDoesNotRetainThis) {
ASSERT_TRUE(frame->Submit());
}
TEST(GPUSurfaceMetalImpeller, ResetHostBufferBasedOnFrameBoundary) {
auto delegate = std::make_shared<TestGPUSurfaceMetalDelegate>();
delegate->SetDevice();
auto context = CreateImpellerContext();
std::unique_ptr<Surface> surface =
std::make_unique<GPUSurfaceMetalImpeller>(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

View File

@ -117,9 +117,10 @@ std::unique_ptr<SurfaceFrame> 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
}));
});

View File

@ -864,6 +864,10 @@ std::shared_ptr<FlutterPlatformViewLayer> 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;
}