Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce the size of Overlay FlutterImageView in HC mode #38393

Merged
merged 13 commits into from
Jan 5, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ void AndroidExternalViewEmbedder::SubmitFrame(

slice->end_recording();

SkRect joined_rect = SkRect::MakeEmpty();
SkRect full_joined_rect = SkRect::MakeEmpty();

// Determinate if Flutter UI intersects with any of the previous
// platform views stacked by z position.
Expand All @@ -137,6 +137,8 @@ void AndroidExternalViewEmbedder::SubmitFrame(
for (ssize_t j = i; j >= 0; j--) {
int64_t current_view_id = composition_order_[j];
SkRect current_view_rect = GetViewRect(current_view_id);
// The rect above the `current_view_rect`
SkRect partial_joined_rect = SkRect::MakeEmpty();
// Each rect corresponds to a native view that renders Flutter UI.
std::list<SkRect> intersection_rects =
slice->searchNonOverlappingDrawnRects(current_view_rect);
Expand All @@ -146,21 +148,26 @@ void AndroidExternalViewEmbedder::SubmitFrame(
// In this case, the rects are merged into a single one that is the union
// of all the rects.
for (const SkRect& rect : intersection_rects) {
joined_rect.join(rect);
partial_joined_rect.join(rect);
}
// Get the intersection rect with the `current_view_rect`,
partial_joined_rect.intersect(current_view_rect);
// Join the `partial_joined_rect` into `full_joined_rect` to get the rect
// above the current `slice`
full_joined_rect.join(partial_joined_rect);
}
if (!joined_rect.isEmpty()) {
if (!full_joined_rect.isEmpty()) {
// Subpixels in the platform may not align with the canvas subpixels.
//
// To workaround it, round the floating point bounds and make the rect
// slightly larger.
//
// For example, {0.3, 0.5, 3.1, 4.7} becomes {0, 0, 4, 5}.
joined_rect.set(joined_rect.roundOut());
overlay_layers.insert({view_id, joined_rect});
full_joined_rect.set(full_joined_rect.roundOut());
overlay_layers.insert({view_id, full_joined_rect});
// Clip the background canvas, so it doesn't contain any of the pixels
// drawn on the overlay layer.
background_canvas->clipRect(joined_rect, SkClipOp::kDifference);
background_canvas->clipRect(full_joined_rect, SkClipOp::kDifference);
}
if (background_builder) {
slice->render_into(background_builder);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame) {
0, 150, 150, 300, 300, 300, 300, stack1));
// The JNI call to display the overlay surface.
EXPECT_CALL(*jni_mock,
FlutterViewDisplayOverlaySurface(0, 50, 50, 200, 200));
FlutterViewDisplayOverlaySurface(0, 150, 150, 100, 100));

auto did_submit_frame = false;
auto surface_frame = std::make_unique<SurfaceFrame>(
Expand Down Expand Up @@ -491,7 +491,7 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame) {
0, 150, 150, 300, 300, 300, 300, stack1));
// The JNI call to display the overlay surface.
EXPECT_CALL(*jni_mock,
FlutterViewDisplayOverlaySurface(0, 50, 50, 200, 200));
FlutterViewDisplayOverlaySurface(0, 150, 150, 100, 100));

auto did_submit_frame = false;
auto surface_frame = std::make_unique<SurfaceFrame>(
Expand All @@ -516,6 +516,108 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame) {
}
}

TEST(AndroidExternalViewEmbedder, OverlayCoverTwoPlatformViews) {
// In this test we will simulate two Android views appearing on the screen
// with a rect intersecting both of them

auto jni_mock = std::make_shared<JNIMock>();
auto android_context =
std::make_shared<AndroidContext>(AndroidRenderingAPI::kSoftware);

auto window = fml::MakeRefCounted<AndroidNativeWindow>(nullptr);
auto gr_context = GrDirectContext::MakeMock(nullptr);
auto frame_size = SkISize::Make(1000, 1000);
SurfaceFrame::FramebufferInfo framebuffer_info;
auto surface_factory = std::make_shared<TestAndroidSurfaceFactory>(
[&android_context, gr_context, window, frame_size, framebuffer_info]() {
auto surface_frame_1 = std::make_unique<SurfaceFrame>(
SkSurface::MakeNull(1000, 1000), framebuffer_info,
[](const SurfaceFrame& surface_frame, SkCanvas* canvas) {
return true;
},
/*frame_size=*/SkISize::Make(800, 600));

auto surface_mock = std::make_unique<SurfaceMock>();
EXPECT_CALL(*surface_mock, AcquireFrame(frame_size))
.Times(1 /* frames */)
.WillOnce(Return(ByMove(std::move(surface_frame_1))));

auto android_surface_mock =
std::make_unique<AndroidSurfaceMock>(android_context);
EXPECT_CALL(*android_surface_mock, IsValid()).WillOnce(Return(true));

EXPECT_CALL(*android_surface_mock, CreateGPUSurface(gr_context.get()))
.WillOnce(Return(ByMove(std::move(surface_mock))));

EXPECT_CALL(*android_surface_mock, SetNativeWindow(window));
return android_surface_mock;
});
auto embedder = std::make_unique<AndroidExternalViewEmbedder>(
*android_context, jni_mock, surface_factory, GetTaskRunnersForFixture());

auto raster_thread_merger = GetThreadMergerFromPlatformThread();

EXPECT_CALL(*jni_mock, FlutterViewBeginFrame());
embedder->BeginFrame(frame_size, nullptr, 1.5, raster_thread_merger);

{
// Add first Android view.
SkMatrix matrix = SkMatrix::Translate(100, 100);
MutatorsStack stack;
embedder->PrerollCompositeEmbeddedView(
0, std::make_unique<EmbeddedViewParams>(matrix, SkSize::Make(100, 100),
stack));
// The JNI call to display the Android view.
EXPECT_CALL(*jni_mock, FlutterViewOnDisplayPlatformView(
0, 100, 100, 100, 100, 150, 150, stack));
}

{
// Add second Android view.
SkMatrix matrix = SkMatrix::Translate(300, 100);
MutatorsStack stack;
embedder->PrerollCompositeEmbeddedView(
1, std::make_unique<EmbeddedViewParams>(matrix, SkSize::Make(100, 100),
stack));
// The JNI call to display the Android view.
EXPECT_CALL(*jni_mock, FlutterViewOnDisplayPlatformView(
1, 300, 100, 100, 100, 150, 150, stack));
}
auto rect_paint = SkPaint();
rect_paint.setColor(SkColors::kCyan);
rect_paint.setStyle(SkPaint::Style::kFill_Style);

// This simulates Flutter UI that intersects with the two Android views.
// Since we will compute the intersection for each android view in turn, and
// finally merge The final size of the overlay will be smaller than the
// width and height of the rect.
embedder->CompositeEmbeddedView(1).canvas->drawRect(
SkRect::MakeXYWH(150, 50, 200, 200), rect_paint);

EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface())
.WillRepeatedly([&]() {
return std::make_unique<PlatformViewAndroidJNI::OverlayMetadata>(
1, window);
});

// The JNI call to display the overlay surface.
EXPECT_CALL(*jni_mock,
FlutterViewDisplayOverlaySurface(1, 150, 100, 200, 100))
.Times(1);

auto surface_frame = std::make_unique<SurfaceFrame>(
SkSurface::MakeNull(1000, 1000), framebuffer_info,
[](const SurfaceFrame& surface_frame, SkCanvas* canvas) mutable {
return true;
},
/*frame_size=*/SkISize::Make(800, 600));

embedder->SubmitFrame(gr_context.get(), std::move(surface_frame));

EXPECT_CALL(*jni_mock, FlutterViewEndFrame());
embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger);
}

TEST(AndroidExternalViewEmbedder, SubmitFrameOverlayComposition) {
auto jni_mock = std::make_shared<JNIMock>();
auto android_context =
Expand Down Expand Up @@ -776,7 +878,7 @@ TEST(AndroidExternalViewEmbedder, DestroyOverlayLayersOnSizeChange) {
EXPECT_CALL(*jni_mock, FlutterViewOnDisplayPlatformView(0, 0, 0, 200, 200,
300, 300, stack1));
EXPECT_CALL(*jni_mock,
FlutterViewDisplayOverlaySurface(0, 50, 50, 200, 200));
FlutterViewDisplayOverlaySurface(0, 50, 50, 150, 150));

SurfaceFrame::FramebufferInfo framebuffer_info;
auto surface_frame = std::make_unique<SurfaceFrame>(
Expand Down Expand Up @@ -865,7 +967,7 @@ TEST(AndroidExternalViewEmbedder, DoesNotDestroyOverlayLayersOnSizeChange) {
EXPECT_CALL(*jni_mock, FlutterViewOnDisplayPlatformView(0, 0, 0, 200, 200,
300, 300, stack1));
EXPECT_CALL(*jni_mock,
FlutterViewDisplayOverlaySurface(0, 50, 50, 200, 200));
FlutterViewDisplayOverlaySurface(0, 50, 50, 150, 150));

auto surface_frame = std::make_unique<SurfaceFrame>(
SkSurface::MakeNull(1000, 1000), framebuffer_info,
Expand Down