Skip to content

Commit

Permalink
Reduce the size of Overlay FlutterImageView in HC mode (flutter#38393)
Browse files Browse the repository at this point in the history
* intersect

* add

* test fix

* fix test

* modify

* typo

* modify

* test
  • Loading branch information
Nayuta403 authored Jan 5, 2023
1 parent bb40152 commit 74861f3
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 10 deletions.
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

0 comments on commit 74861f3

Please sign in to comment.