diff --git a/shell/platform/android/external_view_embedder/external_view_embedder.cc b/shell/platform/android/external_view_embedder/external_view_embedder.cc index 494b436dce6f4..c22462c02c7ad 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder.cc @@ -84,7 +84,7 @@ void AndroidExternalViewEmbedder::SubmitFrame( return; } - std::unordered_map overlay_layers; + std::unordered_map> overlay_layers; std::unordered_map> pictures; SkCanvas* background_canvas = frame->SkiaCanvas(); auto current_frame_view_count = composition_order_.size(); @@ -101,9 +101,9 @@ void AndroidExternalViewEmbedder::SubmitFrame( FML_CHECK(picture); pictures.insert({view_id, picture}); - sk_sp rtree = view_rtrees_.at(view_id); - SkRect joined_rect = SkRect::MakeEmpty(); + overlay_layers.insert({view_id, {}}); + sk_sp rtree = view_rtrees_.at(view_id); // Determinate if Flutter UI intersects with any of the previous // platform views stacked by z position. // @@ -116,31 +116,33 @@ void AndroidExternalViewEmbedder::SubmitFrame( // Each rect corresponds to a native view that renders Flutter UI. std::list intersection_rects = rtree->searchNonOverlappingDrawnRects(current_view_rect); + auto allocation_size = intersection_rects.size(); // Limit the number of native views, so it doesn't grow forever. // // 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); + if (allocation_size > kMaxLayerAllocations) { + SkRect joined_rect; + for (const SkRect& rect : intersection_rects) { + joined_rect.join(rect); + } + intersection_rects.clear(); + intersection_rects.push_back(joined_rect); + } + for (SkRect& intersection_rect : intersection_rects) { + // 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}. + intersection_rect.set(intersection_rect.roundOut()); + overlay_layers.at(view_id).push_back(intersection_rect); + // Clip the background canvas, so it doesn't contain any of the pixels + // drawn on the overlay layer. + background_canvas->clipRect(intersection_rect, SkClipOp::kDifference); } } - - if (joined_rect.isEmpty()) { - continue; - } - - // 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}); - // 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->drawPicture(pictures.at(view_id)); } // Submit the background canvas frame before switching the GL context to @@ -168,15 +170,16 @@ void AndroidExternalViewEmbedder::SubmitFrame( params.sizePoints().height() * device_pixel_ratio_, params.mutatorsStack() // ); - const SkRect& overlay_rect = overlay_layers.at(view_id); - std::unique_ptr frame = - CreateSurfaceIfNeeded(context, // - view_id, // - pictures.at(view_id), // - overlay_rect // - ); - if (should_submit_current_frame) { - frame->Submit(); + for (const SkRect& overlay_rect : overlay_layers.at(view_id)) { + std::unique_ptr frame = + CreateSurfaceIfNeeded(context, // + view_id, // + pictures.at(view_id), // + overlay_rect // + ); + if (should_submit_current_frame) { + frame->Submit(); + } } } } diff --git a/shell/platform/android/external_view_embedder/external_view_embedder.h b/shell/platform/android/external_view_embedder/external_view_embedder.h index f456674be9a94..01b73ed04bac5 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.h +++ b/shell/platform/android/external_view_embedder/external_view_embedder.h @@ -80,6 +80,8 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder { SkRect GetViewRect(int view_id) const; private: + static const int kMaxLayerAllocations = 2; + // The number of frames the rasterizer task runner will continue // to run on the platform thread after no platform view is rendered. // diff --git a/shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc b/shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc index f3f36264c71e0..9bbad774d3f1c 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc @@ -477,108 +477,6 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame) { } } -TEST(AndroidExternalViewEmbedder, SubmitFrame__overlayComposition) { - auto jni_mock = std::make_shared(); - auto android_context = - std::make_shared(AndroidRenderingAPI::kSoftware); - - auto window = fml::MakeRefCounted(nullptr); - auto gr_context = GrDirectContext::MakeMock(nullptr); - auto frame_size = SkISize::Make(1000, 1000); - auto surface_factory = std::make_shared( - [&android_context, gr_context, window, frame_size]() { - auto surface_frame_1 = std::make_unique( - SkSurface::MakeNull(1000, 1000), false, - [](const SurfaceFrame& surface_frame, SkCanvas* canvas) { - return true; - }); - - auto surface_mock = std::make_unique(); - 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(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( - *android_context, jni_mock, surface_factory); - - auto raster_thread_merger = - GetThreadMergerFromPlatformThread(/*merged=*/true); - - EXPECT_CALL(*jni_mock, FlutterViewBeginFrame()); - embedder->BeginFrame(frame_size, nullptr, 1.5, raster_thread_merger); - - { - // Add first Android view. - SkMatrix matrix; - MutatorsStack stack; - stack.PushTransform(SkMatrix::Translate(0, 0)); - - embedder->PrerollCompositeEmbeddedView( - 0, std::make_unique(matrix, SkSize::Make(200, 200), - stack)); - EXPECT_CALL(*jni_mock, FlutterViewOnDisplayPlatformView(0, 0, 0, 200, 200, - 300, 300, 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 first Android view. - embedder->CompositeEmbeddedView(0)->drawRect( - SkRect::MakeXYWH(25, 25, 80, 150), rect_paint); - - { - // Add second Android view. - SkMatrix matrix; - MutatorsStack stack; - stack.PushTransform(SkMatrix::Translate(0, 100)); - - embedder->PrerollCompositeEmbeddedView( - 1, std::make_unique(matrix, SkSize::Make(100, 100), - stack)); - EXPECT_CALL(*jni_mock, FlutterViewOnDisplayPlatformView(1, 0, 0, 100, 100, - 150, 150, stack)); - } - // This simulates Flutter UI that intersects with the first and second Android - // views. - embedder->CompositeEmbeddedView(1)->drawRect(SkRect::MakeXYWH(25, 25, 80, 50), - rect_paint); - - embedder->CompositeEmbeddedView(1)->drawRect( - SkRect::MakeXYWH(75, 75, 30, 100), rect_paint); - - EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface()) - .WillRepeatedly([&]() { - return std::make_unique( - 1, window); - }); - - EXPECT_CALL(*jni_mock, FlutterViewDisplayOverlaySurface(1, 25, 25, 80, 150)) - .Times(2); - - auto surface_frame = std::make_unique( - SkSurface::MakeNull(1000, 1000), false, - [](const SurfaceFrame& surface_frame, SkCanvas* canvas) mutable { - return true; - }); - - embedder->SubmitFrame(gr_context.get(), std::move(surface_frame), nullptr); - - EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); - embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); -} - TEST(AndroidExternalViewEmbedder, DoesNotCallJNIPlatformThreadOnlyMethods) { auto jni_mock = std::make_shared();