diff --git a/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.cc b/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.cc index 8be88832bb470..8794bf45ab06d 100644 --- a/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.cc +++ b/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.cc @@ -471,7 +471,7 @@ bool KHRSwapchainImplVK::Present( break; default: VALIDATION_LOG << "Could not present queue: " << vk::to_string(result); - break; + return false; } return true; diff --git a/shell/common/animator.cc b/shell/common/animator.cc index e8329c11906e2..032a0048cb758 100644 --- a/shell/common/animator.cc +++ b/shell/common/animator.cc @@ -28,21 +28,9 @@ Animator::Animator(Delegate& delegate, : delegate_(delegate), task_runners_(task_runners), waiter_(std::move(waiter)), -#if SHELL_ENABLE_METAL layer_tree_pipeline_(std::make_shared(2)), -#else // SHELL_ENABLE_METAL - // TODO(dnfield): We should remove this logic and set the pipeline depth - // back to 2 in this case. See - // https://github.com/flutter/engine/pull/9132 for discussion. - layer_tree_pipeline_(std::make_shared( - task_runners.GetPlatformTaskRunner() == - task_runners.GetRasterTaskRunner() - ? 1 - : 2)), -#endif // SHELL_ENABLE_METAL pending_frame_semaphore_(1), - weak_factory_(this) { -} + weak_factory_(this) {} Animator::~Animator() = default; diff --git a/shell/platform/android/android_shell_holder_unittests.cc b/shell/platform/android/android_shell_holder_unittests.cc index 0a1f1ed3fcf5d..c740be3d6d10d 100644 --- a/shell/platform/android/android_shell_holder_unittests.cc +++ b/shell/platform/android/android_shell_holder_unittests.cc @@ -7,108 +7,11 @@ #include "flutter/shell/platform/android/android_shell_holder.h" #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "shell/platform/android/jni/platform_view_android_jni.h" +#include "shell/platform/android/jni/jni_mock.h" namespace flutter { namespace testing { namespace { -class MockPlatformViewAndroidJNI : public PlatformViewAndroidJNI { - public: - MOCK_METHOD(void, - FlutterViewHandlePlatformMessage, - (std::unique_ptr message, - int responseId), - (override)); - MOCK_METHOD(void, - FlutterViewHandlePlatformMessageResponse, - (int responseId, std::unique_ptr data), - (override)); - MOCK_METHOD(void, - FlutterViewUpdateSemantics, - (std::vector buffer, - std::vector strings, - std::vector> string_attribute_args), - (override)); - MOCK_METHOD(void, - FlutterViewUpdateCustomAccessibilityActions, - (std::vector actions_buffer, - std::vector strings), - (override)); - MOCK_METHOD(void, FlutterViewOnFirstFrame, (), (override)); - MOCK_METHOD(void, FlutterViewOnPreEngineRestart, (), (override)); - MOCK_METHOD(void, - SurfaceTextureAttachToGLContext, - (JavaLocalRef surface_texture, int textureId), - (override)); - MOCK_METHOD(bool, - SurfaceTextureShouldUpdate, - (JavaLocalRef surface_texture), - (override)); - MOCK_METHOD(void, - SurfaceTextureUpdateTexImage, - (JavaLocalRef surface_texture), - (override)); - MOCK_METHOD(SkM44, - SurfaceTextureGetTransformMatrix, - (JavaLocalRef surface_texture), - (override)); - MOCK_METHOD(void, - SurfaceTextureDetachFromGLContext, - (JavaLocalRef surface_texture), - (override)); - MOCK_METHOD(JavaLocalRef, - ImageProducerTextureEntryAcquireLatestImage, - (JavaLocalRef image_texture_entry), - (override)); - MOCK_METHOD(JavaLocalRef, - ImageGetHardwareBuffer, - (JavaLocalRef image), - (override)); - MOCK_METHOD(void, ImageClose, (JavaLocalRef image), (override)); - MOCK_METHOD(void, - HardwareBufferClose, - (JavaLocalRef hardware_buffer), - (override)); - MOCK_METHOD(void, - FlutterViewOnDisplayPlatformView, - (int view_id, - int x, - int y, - int width, - int height, - int viewWidth, - int viewHeight, - MutatorsStack mutators_stack), - (override)); - MOCK_METHOD(void, - FlutterViewDisplayOverlaySurface, - (int surface_id, int x, int y, int width, int height), - (override)); - MOCK_METHOD(void, FlutterViewBeginFrame, (), (override)); - MOCK_METHOD(void, FlutterViewEndFrame, (), (override)); - MOCK_METHOD(std::unique_ptr, - FlutterViewCreateOverlaySurface, - (), - (override)); - MOCK_METHOD(void, FlutterViewDestroyOverlaySurfaces, (), (override)); - MOCK_METHOD(std::unique_ptr>, - FlutterViewComputePlatformResolvedLocale, - (std::vector supported_locales_data), - (override)); - MOCK_METHOD(double, GetDisplayRefreshRate, (), (override)); - MOCK_METHOD(double, GetDisplayWidth, (), (override)); - MOCK_METHOD(double, GetDisplayHeight, (), (override)); - MOCK_METHOD(double, GetDisplayDensity, (), (override)); - MOCK_METHOD(bool, - RequestDartDeferredLibrary, - (int loading_unit_id), - (override)); - MOCK_METHOD(double, - FlutterViewGetScaledFontSize, - (double font_size, int configuration_id), - (const, override)); -}; - class MockPlatformMessageResponse : public PlatformMessageResponse { public: static fml::RefPtr Create() { @@ -122,7 +25,7 @@ class MockPlatformMessageResponse : public PlatformMessageResponse { TEST(AndroidShellHolder, Create) { Settings settings; settings.enable_software_rendering = false; - auto jni = std::make_shared(); + auto jni = std::make_shared(); auto holder = std::make_unique(settings, jni); EXPECT_NE(holder.get(), nullptr); EXPECT_TRUE(holder->IsValid()); @@ -135,7 +38,7 @@ TEST(AndroidShellHolder, Create) { TEST(AndroidShellHolder, HandlePlatformMessage) { Settings settings; settings.enable_software_rendering = false; - auto jni = std::make_shared(); + auto jni = std::make_shared(); auto holder = std::make_unique(settings, jni); EXPECT_NE(holder.get(), nullptr); EXPECT_TRUE(holder->IsValid()); @@ -164,7 +67,7 @@ TEST(AndroidShellHolder, HandlePlatformMessage) { TEST(AndroidShellHolder, CreateWithMergedPlatformAndUIThread) { Settings settings; settings.merged_platform_ui_thread = true; - auto jni = std::make_shared(); + auto jni = std::make_shared(); auto holder = std::make_unique(settings, jni); auto window = fml::MakeRefCounted( nullptr, /*is_fake_window=*/true); @@ -178,7 +81,7 @@ TEST(AndroidShellHolder, CreateWithMergedPlatformAndUIThread) { TEST(AndroidShellHolder, CreateWithUnMergedPlatformAndUIThread) { Settings settings; settings.merged_platform_ui_thread = false; - auto jni = std::make_shared(); + auto jni = std::make_shared(); auto holder = std::make_unique(settings, jni); auto window = fml::MakeRefCounted( nullptr, /*is_fake_window=*/true); 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 a957df9a54042..010f3bc9b80ae 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder.cc @@ -3,10 +3,19 @@ // found in the LICENSE file. #include "flutter/shell/platform/android/external_view_embedder/external_view_embedder.h" + +#include +#include +#include + +#include "flow/surface_frame.h" #include "flow/view_slicer.h" #include "flutter/common/constants.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/fml/trace_event.h" +#include "fml/synchronization/count_down_latch.h" +#include "fml/task_runner.h" +#include "shell/platform/android/external_view_embedder/surface_pool.h" namespace flutter { @@ -74,10 +83,11 @@ void AndroidExternalViewEmbedder::SubmitFlutterView( // Properly support multi-view in the future. FML_DCHECK(flutter_view_id == kFlutterImplicitViewId); - if (!FrameHasPlatformLayers()) { + if (!FrameHasPlatformLayers() && !had_platform_views_) { frame->Submit(); return; } + had_platform_views_ = FrameHasPlatformLayers(); std::unordered_map view_rects; for (auto platform_id : composition_order_) { @@ -101,64 +111,122 @@ void AndroidExternalViewEmbedder::SubmitFlutterView( frame->Submit(); } - for (int64_t view_id : composition_order_) { - SkRect view_rect = GetViewRect(view_id); - const EmbeddedViewParams& params = view_params_.at(view_id); - // Display the platform view. If it's already displayed, then it's - // just positioned and sized. - jni_facade_->FlutterViewOnDisplayPlatformView( - view_id, // - view_rect.x(), // - view_rect.y(), // - view_rect.width(), // - view_rect.height(), // - params.sizePoints().width() * device_pixel_ratio_, - params.sizePoints().height() * device_pixel_ratio_, - params.mutatorsStack() // - ); - std::unordered_map::const_iterator overlay = - overlay_layers.find(view_id); - if (overlay == overlay_layers.end()) { + // Layers must be created and destroyed on the platform thread. If there are + // not sufficient overlays, or if the overlay properties changed then a task + // must be posted to the platform thread to construct them. + bool destroy_all_layers = + surface_pool_->CheckLayerProperties(context, frame_size_); + std::vector> + overlays; + if (destroy_all_layers || surface_pool_->size() < overlay_layers.size()) { + auto latch = std::make_shared(1u); + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetPlatformTaskRunner(), [&]() { + if (destroy_all_layers) { + jni_facade_->FlutterViewDestroyOverlaySurfaces(); + } + for (auto i = surface_pool_->size(); i < overlay_layers.size(); i++) { + overlays.push_back(jni_facade_->FlutterViewCreateOverlaySurface()); + } + latch->CountDown(); + }); + if (!task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()) { + latch->Wait(); + } + if (destroy_all_layers) { + surface_pool_->DestroyLayers(); + } + for (auto& overlay : overlays) { + surface_pool_->CreateLayer(context, // + android_context_, // + std::move(overlay), // + surface_factory_ // + ); + } + } + + std::unordered_map> layers; + for (const auto& [view_id, rect] : overlay_layers) { + auto overlay_layer = surface_pool_->GetNextLayer(); + if (!overlay_layer) { continue; } + std::unique_ptr frame = - CreateSurfaceIfNeeded(context, // - view_id, // - slices_.at(view_id).get(), // - overlay->second // - ); - if (should_submit_current_frame) { - frame->Submit(); + overlay_layer->surface->AcquireFrame(frame_size_); + if (!frame) { + continue; } + + DlCanvas* overlay_canvas = frame->Canvas(); + + // Offset the picture since its absolute position on the scene is + // determined by the position of the overlay view. + overlay_canvas->Clear(DlColor::kTransparent()); + overlay_canvas->Translate(-rect.x(), -rect.y()); + slices_[view_id]->render_into(overlay_canvas); + + frame->set_submit_info({.frame_boundary = false}); + frame->Submit(); + layers[view_id] = overlay_layer; } -} -// |ExternalViewEmbedder| -std::unique_ptr -AndroidExternalViewEmbedder::CreateSurfaceIfNeeded(GrDirectContext* context, - int64_t view_id, - EmbedderViewSlice* slice, - const SkRect& rect) { - std::shared_ptr layer = surface_pool_->GetLayer( - context, android_context_, jni_facade_, surface_factory_); - - std::unique_ptr frame = - layer->surface->AcquireFrame(frame_size_); - // Display the overlay surface. If it's already displayed, then it's - // just positioned and sized. - jni_facade_->FlutterViewDisplayOverlaySurface(layer->id, // - rect.x(), // - rect.y(), // - rect.width(), // - rect.height() // - ); - DlCanvas* overlay_canvas = frame->Canvas(); - overlay_canvas->Clear(DlColor::kTransparent()); - // Offset the picture since its absolute position on the scene is determined - // by the position of the overlay view. - overlay_canvas->Translate(-rect.x(), -rect.y()); - slice->render_into(overlay_canvas); - return frame; + surface_pool_->RecycleLayers(); + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetPlatformTaskRunner(), + [&, composition_order = composition_order_, view_params = view_params_, + overlay_layers = std::move(overlay_layers), + layers = std::move(layers)]() { + if (!jni_facade_->IsAttachedToView()) { + return; + } + + TRACE_EVENT0("flutter", + "AndroidExternalViewEmbedder::RenderNativeViews"); + jni_facade_->FlutterViewBeginFrame(); + + for (int64_t view_id : composition_order) { + const EmbeddedViewParams& params = view_params.at(view_id); + auto view_rect = + SkRect::MakeXYWH(params.finalBoundingRect().x(), // + params.finalBoundingRect().y(), // + params.finalBoundingRect().width(), // + params.finalBoundingRect().height() // + ); + + // Display the platform view. If it's already displayed, then it's + // just positioned and sized. + jni_facade_->FlutterViewOnDisplayPlatformView( + view_id, // + view_rect.x(), // + view_rect.y(), // + view_rect.width(), // + view_rect.height(), // + params.sizePoints().width() * device_pixel_ratio_, + params.sizePoints().height() * device_pixel_ratio_, + params.mutatorsStack() // + ); + + auto overlay_rect = overlay_layers.find(view_id); + if (overlay_rect == overlay_layers.end()) { + continue; + } + SkRect rect = overlay_rect->second; + auto maybe_layer = layers.find(view_id); + if (maybe_layer == layers.end()) { + continue; + } + jni_facade_->FlutterViewDisplayOverlaySurface( + maybe_layer->second->id, // + rect.x(), // + rect.y(), // + rect.width(), // + rect.height() // + ); + } + + jni_facade_->FlutterViewEndFrame(); + }); } // |ExternalViewEmbedder| @@ -167,22 +235,7 @@ PostPrerollResult AndroidExternalViewEmbedder::PostPrerollAction( if (!FrameHasPlatformLayers()) { return PostPrerollResult::kSuccess; } - if (!raster_thread_merger->IsMerged()) { - // The raster thread merger may be disabled if the rasterizer is being - // created or teared down. - // - // In such cases, the current frame is dropped, and a new frame is attempted - // with the same layer tree. - // - // Eventually, the frame is submitted once this method returns `kSuccess`. - // At that point, the raster tasks are handled on the platform thread. - CancelFrame(); - raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); - return PostPrerollResult::kSkipAndRetryFrame; - } - raster_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration); - // Surface switch requires to resubmit the frame. - // TODO(egarciad): https://github.com/flutter/flutter/issues/65652 + if (previous_frame_view_count_ == 0) { return PostPrerollResult::kResubmitFrame; } @@ -209,26 +262,13 @@ void AndroidExternalViewEmbedder::Reset() { // |ExternalViewEmbedder| void AndroidExternalViewEmbedder::BeginFrame( GrDirectContext* context, - const fml::RefPtr& raster_thread_merger) { - // JNI method must be called on the platform thread. - if (raster_thread_merger->IsOnPlatformThread()) { - jni_facade_->FlutterViewBeginFrame(); - } -} + const fml::RefPtr& raster_thread_merger) {} // |ExternalViewEmbedder| void AndroidExternalViewEmbedder::PrepareFlutterView( SkISize frame_size, double device_pixel_ratio) { Reset(); - - // The surface size changed. Therefore, destroy existing surfaces as - // the existing surfaces in the pool can't be recycled. - if (frame_size_ != frame_size) { - DestroySurfaces(); - } - surface_pool_->SetFrameSize(frame_size); - frame_size_ = frame_size; device_pixel_ratio_ = device_pixel_ratio; } @@ -241,17 +281,11 @@ void AndroidExternalViewEmbedder::CancelFrame() { // |ExternalViewEmbedder| void AndroidExternalViewEmbedder::EndFrame( bool should_resubmit_frame, - const fml::RefPtr& raster_thread_merger) { - surface_pool_->RecycleLayers(); - // JNI method must be called on the platform thread. - if (raster_thread_merger->IsOnPlatformThread()) { - jni_facade_->FlutterViewEndFrame(); - } -} + const fml::RefPtr& raster_thread_merger) {} // |ExternalViewEmbedder| bool AndroidExternalViewEmbedder::SupportsDynamicThreadMerging() { - return true; + return false; } // |ExternalViewEmbedder| @@ -261,16 +295,17 @@ void AndroidExternalViewEmbedder::Teardown() { // |ExternalViewEmbedder| void AndroidExternalViewEmbedder::DestroySurfaces() { - if (!surface_pool_->HasLayers()) { + if (surface_pool_->size() == 0) { return; } - fml::AutoResetWaitableEvent latch; - fml::TaskRunner::RunNowOrPostTask(task_runners_.GetPlatformTaskRunner(), - [&]() { - surface_pool_->DestroyLayers(jni_facade_); - latch.Signal(); - }); - latch.Wait(); + surface_pool_->DestroyLayers(); + auto latch = std::make_shared(1u); + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetPlatformTaskRunner(), [&]() { + jni_facade_->FlutterViewDestroyOverlaySurfaces(); + latch->CountDown(); + }); + latch->Wait(); } } // namespace flutter 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 ab00870276ffc..6fafa7ece605d 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.h +++ b/shell/platform/android/external_view_embedder/external_view_embedder.h @@ -115,6 +115,7 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder { // The order of composition. Each entry contains a unique id for the platform // view. std::vector composition_order_; + bool had_platform_views_ = false; // The |EmbedderViewSlice| implementation keyed off the platform view id, // which contains any subsequent operations until the next platform view or @@ -138,13 +139,6 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder { // Whether the layer tree in the current frame has platform layers. bool FrameHasPlatformLayers(); - - // Creates a Surface when needed or recycles an existing one. - // Finally, draws the picture on the frame's canvas. - std::unique_ptr CreateSurfaceIfNeeded(GrDirectContext* context, - int64_t view_id, - EmbedderViewSlice* slice, - const SkRect& rect); }; } // namespace flutter 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 20282ba848333..0e11f9d5336bb 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 @@ -134,61 +134,6 @@ TEST(AndroidExternalViewEmbedder, CancelFrame) { ASSERT_EQ(embedder->CompositeEmbeddedView(0), nullptr); } -TEST(AndroidExternalViewEmbedder, RasterizerRunsOnPlatformThread) { - auto jni_mock = std::make_shared(); - auto android_context = AndroidContext(AndroidRenderingAPI::kSoftware); - auto embedder = std::make_unique( - android_context, jni_mock, nullptr, GetTaskRunnersForFixture()); - - fml::Thread rasterizer_thread("rasterizer"); - auto raster_thread_merger = - GetThreadMergerFromPlatformThread(&rasterizer_thread); - ASSERT_FALSE(raster_thread_merger->IsMerged()); - - EXPECT_CALL(*jni_mock, FlutterViewBeginFrame()); - embedder->BeginFrame(nullptr, raster_thread_merger); - embedder->PrepareFlutterView(SkISize::Make(10, 20), 1.0); - - // Push a platform view. - embedder->PrerollCompositeEmbeddedView( - 0, std::make_unique()); - - auto postpreroll_result = embedder->PostPrerollAction(raster_thread_merger); - ASSERT_EQ(PostPrerollResult::kSkipAndRetryFrame, postpreroll_result); - - EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); - embedder->EndFrame(/*should_resubmit_frame=*/true, raster_thread_merger); - - ASSERT_TRUE(raster_thread_merger->IsMerged()); - - int pending_frames = 0; - while (raster_thread_merger->IsMerged()) { - raster_thread_merger->DecrementLease(); - pending_frames++; - } - ASSERT_EQ(10, pending_frames); // kDefaultMergedLeaseDuration -} - -TEST(AndroidExternalViewEmbedder, RasterizerRunsOnRasterizerThread) { - auto jni_mock = std::make_shared(); - auto android_context = AndroidContext(AndroidRenderingAPI::kSoftware); - auto embedder = std::make_unique( - android_context, jni_mock, nullptr, GetTaskRunnersForFixture()); - - fml::Thread rasterizer_thread("rasterizer"); - auto raster_thread_merger = - GetThreadMergerFromPlatformThread(&rasterizer_thread); - ASSERT_FALSE(raster_thread_merger->IsMerged()); - - PostPrerollResult result = embedder->PostPrerollAction(raster_thread_merger); - ASSERT_EQ(PostPrerollResult::kSuccess, result); - - EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); - embedder->EndFrame(/*should_resubmit_frame=*/true, raster_thread_merger); - - ASSERT_FALSE(raster_thread_merger->IsMerged()); -} - TEST(AndroidExternalViewEmbedder, PlatformViewRect) { auto jni_mock = std::make_shared(); @@ -199,7 +144,6 @@ TEST(AndroidExternalViewEmbedder, PlatformViewRect) { auto raster_thread_merger = GetThreadMergerFromPlatformThread(&rasterizer_thread); - EXPECT_CALL(*jni_mock, FlutterViewBeginFrame()); embedder->BeginFrame(nullptr, raster_thread_merger); embedder->PrepareFlutterView(SkISize::Make(100, 100), 1.5); @@ -227,7 +171,6 @@ TEST(AndroidExternalViewEmbedder, PlatformViewRectChangedParams) { auto raster_thread_merger = GetThreadMergerFromPlatformThread(&rasterizer_thread); - EXPECT_CALL(*jni_mock, FlutterViewBeginFrame()); embedder->BeginFrame(nullptr, raster_thread_merger); embedder->PrepareFlutterView(SkISize::Make(100, 100), 1.5); @@ -327,7 +270,6 @@ TEST(AndroidExternalViewEmbedder, SubmitFlutterView) { auto postpreroll_result = embedder->PostPrerollAction(raster_thread_merger); ASSERT_EQ(PostPrerollResult::kSuccess, postpreroll_result); - EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); } @@ -466,7 +408,6 @@ TEST(AndroidExternalViewEmbedder, SubmitFlutterView) { auto postpreroll_result = embedder->PostPrerollAction(raster_thread_merger); ASSERT_EQ(PostPrerollResult::kSuccess, postpreroll_result); - EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); } } @@ -569,10 +510,10 @@ TEST(AndroidExternalViewEmbedder, OverlayCoverTwoPlatformViews) { [](const SurfaceFrame& surface_frame) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); + EXPECT_CALL(*jni_mock, FlutterViewEndFrame()).Times(1); embedder->SubmitFlutterView(kImplicitViewId, gr_context.get(), nullptr, std::move(surface_frame)); - EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); } @@ -676,10 +617,10 @@ TEST(AndroidExternalViewEmbedder, SubmitFrameOverlayComposition) { [](const SurfaceFrame& surface_frame) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); + EXPECT_CALL(*jni_mock, FlutterViewEndFrame()).Times(1); embedder->SubmitFlutterView(kImplicitViewId, gr_context.get(), nullptr, std::move(surface_frame)); - EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); } @@ -748,10 +689,11 @@ TEST(AndroidExternalViewEmbedder, SubmitFramePlatformViewWithoutAnyOverlay) { [](const SurfaceFrame& surface_frame) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); + EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); + embedder->SubmitFlutterView(kImplicitViewId, gr_context.get(), nullptr, std::move(surface_frame)); - EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); } @@ -823,8 +765,6 @@ TEST(AndroidExternalViewEmbedder, DestroyOverlayLayersOnSizeChange) { // Add an Android view. MutatorsStack stack1; - // TODO(egarciad): Investigate why Flow applies the device pixel ratio to - // the offsetPixels, but not the sizePoints. auto view_params_1 = std::make_unique( SkMatrix(), SkSize::Make(200, 200), stack1); @@ -853,149 +793,34 @@ TEST(AndroidExternalViewEmbedder, DestroyOverlayLayersOnSizeChange) { }, [](const SurfaceFrame& surface_frame) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); + + EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); embedder->SubmitFlutterView(kImplicitViewId, gr_context.get(), nullptr, std::move(surface_frame)); - EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); } - EXPECT_CALL(*jni_mock, FlutterViewDestroyOverlaySurfaces()); - EXPECT_CALL(*jni_mock, FlutterViewBeginFrame()); // Change the frame size. embedder->BeginFrame(nullptr, raster_thread_merger); embedder->PrepareFlutterView(SkISize::Make(30, 40), 1.0); -} - -TEST(AndroidExternalViewEmbedder, DoesNotDestroyOverlayLayersOnSizeChange) { - 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); - SurfaceFrame::FramebufferInfo framebuffer_info; - auto surface_factory = std::make_shared( - [gr_context, window, frame_size, framebuffer_info]() { - auto surface_frame_1 = std::make_unique( - SkSurfaces::Null(1000, 1000), framebuffer_info, - [](const SurfaceFrame& surface_frame, DlCanvas* canvas) { - return true; - }, - [](const SurfaceFrame& surface_frame) { return true; }, - /*frame_size=*/SkISize::Make(800, 600)); - - auto surface_mock = std::make_unique(); - EXPECT_CALL(*surface_mock, AcquireFrame(frame_size)) - .WillOnce(Return(ByMove(std::move(surface_frame_1)))); - - auto android_surface_mock = std::make_unique(); - 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, GetTaskRunnersForFixture()); - // ------------------ First frame ------------------ // { - fml::Thread rasterizer_thread("rasterizer"); - auto raster_thread_merger = - GetThreadMergerFromPlatformThread(&rasterizer_thread); - EXPECT_CALL(*jni_mock, FlutterViewBeginFrame()); - embedder->BeginFrame(nullptr, raster_thread_merger); - embedder->PrepareFlutterView(frame_size, 1.5); - - // Add an Android view. - MutatorsStack stack1; - // TODO(egarciad): Investigate why Flow applies the device pixel ratio to - // the offsetPixels, but not the sizePoints. - auto view_params_1 = std::make_unique( - SkMatrix(), SkSize::Make(200, 200), stack1); - - embedder->PrerollCompositeEmbeddedView(0, std::move(view_params_1)); - - // This simulates Flutter UI that intersects with the Android view. - embedder->CompositeEmbeddedView(0)->DrawRect( - SkRect::MakeXYWH(50, 50, 200, 200), DlPaint()); - - // Create a new overlay surface. - EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface()) - .WillOnce(Return( - ByMove(std::make_unique( - 0, window)))); - // The JNI call to display the Android view. - EXPECT_CALL(*jni_mock, FlutterViewOnDisplayPlatformView(0, 0, 0, 200, 200, - 300, 300, stack1)); - EXPECT_CALL(*jni_mock, - FlutterViewDisplayOverlaySurface(0, 50, 50, 150, 150)); - + SurfaceFrame::FramebufferInfo framebuffer_info; auto surface_frame = std::make_unique( - SkSurfaces::Null(1000, 1000), framebuffer_info, + SkSurfaces::Null(30, 40), framebuffer_info, [](const SurfaceFrame& surface_frame, DlCanvas* canvas) { return true; }, [](const SurfaceFrame& surface_frame) { return true; }, - /*frame_size=*/SkISize::Make(800, 600)); - embedder->SubmitFlutterView(kImplicitViewId, gr_context.get(), nullptr, - std::move(surface_frame)); + /*frame_size=*/SkISize::Make(30, 40)); + EXPECT_CALL(*jni_mock, FlutterViewDestroyOverlaySurfaces()); + EXPECT_CALL(*jni_mock, FlutterViewBeginFrame()); EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); - embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); + embedder->SubmitFlutterView(kImplicitViewId, gr_context.get(), nullptr, + std::move(surface_frame)); } - - EXPECT_CALL(*jni_mock, FlutterViewDestroyOverlaySurfaces()).Times(1); - EXPECT_CALL(*jni_mock, FlutterViewBeginFrame()).Times(0); - - fml::Thread platform_thread("platform"); - embedder->BeginFrame(nullptr, - GetThreadMergerFromRasterThread(&platform_thread)); - embedder->PrepareFlutterView(SkISize::Make(30, 40), 1.0); -} - -TEST(AndroidExternalViewEmbedder, SupportsDynamicThreadMerging) { - auto jni_mock = std::make_shared(); - auto android_context = AndroidContext(AndroidRenderingAPI::kSoftware); - auto embedder = std::make_unique( - android_context, jni_mock, nullptr, GetTaskRunnersForFixture()); - ASSERT_TRUE(embedder->SupportsDynamicThreadMerging()); -} - -TEST(AndroidExternalViewEmbedder, DisableThreadMerger) { - auto jni_mock = std::make_shared(); - auto android_context = AndroidContext(AndroidRenderingAPI::kSoftware); - auto embedder = std::make_unique( - android_context, jni_mock, nullptr, GetTaskRunnersForFixture()); - - fml::Thread platform_thread("platform"); - auto raster_thread_merger = GetThreadMergerFromRasterThread(&platform_thread); - ASSERT_FALSE(raster_thread_merger->IsMerged()); - - // The shell may disable the thread merger during `OnPlatformViewDestroyed`. - raster_thread_merger->Disable(); - - EXPECT_CALL(*jni_mock, FlutterViewBeginFrame()).Times(0); - - embedder->BeginFrame(nullptr, raster_thread_merger); - embedder->PrepareFlutterView(SkISize::Make(10, 20), 1.0); - // Push a platform view. - embedder->PrerollCompositeEmbeddedView( - 0, std::make_unique()); - - auto postpreroll_result = embedder->PostPrerollAction(raster_thread_merger); - ASSERT_EQ(PostPrerollResult::kSkipAndRetryFrame, postpreroll_result); - - EXPECT_CALL(*jni_mock, FlutterViewEndFrame()).Times(0); - embedder->EndFrame(/*should_resubmit_frame=*/true, raster_thread_merger); - - ASSERT_FALSE(raster_thread_merger->IsMerged()); } TEST(AndroidExternalViewEmbedder, Teardown) { diff --git a/shell/platform/android/external_view_embedder/surface_pool.cc b/shell/platform/android/external_view_embedder/surface_pool.cc index 6e71e97247c9b..0e358325e5e54 100644 --- a/shell/platform/android/external_view_embedder/surface_pool.cc +++ b/shell/platform/android/external_view_embedder/surface_pool.cc @@ -21,88 +21,66 @@ SurfacePool::SurfacePool() = default; SurfacePool::~SurfacePool() = default; -std::shared_ptr SurfacePool::GetLayer( - GrDirectContext* gr_context, - const AndroidContext& android_context, - const std::shared_ptr& jni_facade, - const std::shared_ptr& surface_factory) { - std::lock_guard lock(mutex_); - // Destroy current layers in the pool if the frame size has changed. - if (requested_frame_size_ != current_frame_size_) { - DestroyLayersLocked(jni_facade); - } +bool SurfacePool::CheckLayerProperties(GrDirectContext* gr_context, + SkISize frame_size) { intptr_t gr_context_key = reinterpret_cast(gr_context); - // Allocate a new surface if there isn't one available. - if (available_layer_index_ >= layers_.size()) { - std::unique_ptr android_surface = - surface_factory->CreateSurface(); - - FML_CHECK(android_surface && android_surface->IsValid()) - << "Could not create an OpenGL, Vulkan or Software surface to set up " - "rendering."; - - std::unique_ptr java_metadata = - jni_facade->FlutterViewCreateOverlaySurface(); - - FML_CHECK(java_metadata->window); - android_surface->SetNativeWindow(java_metadata->window); - - std::unique_ptr surface = - android_surface->CreateGPUSurface(gr_context); + bool destroy_all_layers = (gr_context_key != gr_context_key_ || + frame_size != current_frame_size_) && + layers_.size() > 0; + current_frame_size_ = frame_size; + gr_context_key_ = gr_context_key; + return destroy_all_layers; +} - std::shared_ptr layer = - std::make_shared(java_metadata->id, // - std::move(android_surface), // - std::move(surface) // - ); - layer->gr_context_key = gr_context_key; - layers_.push_back(layer); +std::shared_ptr SurfacePool::GetNextLayer() { + if (available_layer_index_ >= layers_.size()) { + return nullptr; } + return layers_[available_layer_index_++]; +} - std::shared_ptr layer = layers_[available_layer_index_]; - // Since the surfaces are recycled, it's possible that the GrContext is - // different. - if (gr_context_key != layer->gr_context_key) { - layer->gr_context_key = gr_context_key; - // The overlay already exists, but the GrContext was changed so we need to - // recreate the rendering surface with the new GrContext. - std::unique_ptr surface = - layer->android_surface->CreateGPUSurface(gr_context); - layer->surface = std::move(surface); - } - available_layer_index_++; - current_frame_size_ = requested_frame_size_; - return layer; +void SurfacePool::CreateLayer( + GrDirectContext* gr_context, + const AndroidContext& android_context, + std::unique_ptr overlay_metadata, + const std::shared_ptr& surface_factory) { + std::unique_ptr android_surface = + surface_factory->CreateSurface(); + + FML_CHECK(android_surface && android_surface->IsValid()) + << "Could not create an OpenGL, Vulkan or Software surface to set up " + "rendering."; + FML_CHECK(overlay_metadata->window); + android_surface->SetNativeWindow(overlay_metadata->window); + + std::unique_ptr surface = + android_surface->CreateGPUSurface(gr_context); + + std::shared_ptr layer = + std::make_shared(overlay_metadata->id, // + std::move(android_surface), // + std::move(surface) // + ); + layers_.push_back(layer); } void SurfacePool::RecycleLayers() { - std::lock_guard lock(mutex_); available_layer_index_ = 0; } -bool SurfacePool::HasLayers() { - std::lock_guard lock(mutex_); - return !layers_.empty(); -} - -void SurfacePool::DestroyLayers( - const std::shared_ptr& jni_facade) { - std::lock_guard lock(mutex_); - DestroyLayersLocked(jni_facade); -} - -void SurfacePool::DestroyLayersLocked( - const std::shared_ptr& jni_facade) { +void SurfacePool::DestroyLayers() { if (layers_.empty()) { return; } - jni_facade->FlutterViewDestroyOverlaySurfaces(); layers_.clear(); available_layer_index_ = 0; } +size_t SurfacePool::size() const { + return layers_.size(); +} + std::vector> SurfacePool::GetUnusedLayers() { - std::lock_guard lock(mutex_); std::vector> results; for (size_t i = available_layer_index_; i < layers_.size(); i++) { results.push_back(layers_[i]); @@ -110,9 +88,4 @@ std::vector> SurfacePool::GetUnusedLayers() { return results; } -void SurfacePool::SetFrameSize(SkISize frame_size) { - std::lock_guard lock(mutex_); - requested_frame_size_ = frame_size; -} - } // namespace flutter diff --git a/shell/platform/android/external_view_embedder/surface_pool.h b/shell/platform/android/external_view_embedder/surface_pool.h index 5f09d6db916d5..c9e3bc99331a7 100644 --- a/shell/platform/android/external_view_embedder/surface_pool.h +++ b/shell/platform/android/external_view_embedder/surface_pool.h @@ -5,8 +5,6 @@ #ifndef FLUTTER_SHELL_PLATFORM_ANDROID_EXTERNAL_VIEW_EMBEDDER_SURFACE_POOL_H_ #define FLUTTER_SHELL_PLATFORM_ANDROID_EXTERNAL_VIEW_EMBEDDER_SURFACE_POOL_H_ -#include - #include "flutter/flow/surface.h" #include "flutter/shell/platform/android/context/android_context.h" #include "flutter/shell/platform/android/surface/android_surface.h" @@ -34,13 +32,6 @@ struct OverlayLayer { // A GPU surface. This may change when the overlay is recycled. std::unique_ptr surface; - - // The `GrContext` that is currently used by the overlay surfaces. - // We track this to know when the GrContext for the Flutter app has changed - // so we can update the overlay with the new context. - // - // This may change when the overlay is recycled. - intptr_t gr_context_key; }; class SurfacePool { @@ -49,32 +40,40 @@ class SurfacePool { ~SurfacePool(); - // Gets a layer from the pool if available, or allocates a new one. - // Finally, it marks the layer as used. That is, it increments - // `available_layer_index_`. - std::shared_ptr GetLayer( + /// @brief Returns whether the cached layers are still valid. + /// + /// If the frame size or layer has changed, then all layers must be + /// destroyed and recreated. + bool CheckLayerProperties(GrDirectContext* gr_context, SkISize frame_size); + + /// @brief Gets a layer from the pool if available. + /// + /// The layer is marked as used until [RecycleLayers] is called. + std::shared_ptr GetNextLayer(); + + /// @brief Create a new overlay layer. + /// + /// This method can only be called on the Raster thread. + void CreateLayer( GrDirectContext* gr_context, const AndroidContext& android_context, - const std::shared_ptr& jni_facade, + std::unique_ptr overlay_metadata, const std::shared_ptr& surface_factory); - // Gets the layers in the pool that aren't currently used. - // This method doesn't mark the layers as unused. + /// @brief Removes unused layers from the pool. Returns the unused layers. std::vector> GetUnusedLayers(); - // Marks the layers in the pool as available for reuse. + /// @brief Marks the layers in the pool as available for reuse. void RecycleLayers(); - // Destroys all the layers in the pool. - void DestroyLayers(const std::shared_ptr& jni_facade); - - // Sets the frame size used by the layers in the pool. - // If the current layers in the pool have a different frame size, - // then they are deallocated as soon as |GetLayer| is called. - void SetFrameSize(SkISize frame_size); + /// @brief The count of layers currently in the pool. + size_t size() const; - // Returns true if the current pool has layers in use. - bool HasLayers(); + /// @brief Clears the state of the surface pool. + /// + /// Requires that the JNI method FlutterViewDestroyOverlaySurfaces is called + /// separately + void DestroyLayers(); private: // The index of the entry in the layers_ vector that determines the beginning @@ -97,14 +96,12 @@ class SurfacePool { // The frame size of the layers in the pool. SkISize current_frame_size_; - // The frame size to be used by future layers. - SkISize requested_frame_size_; - - // Used to guard public methods. - std::mutex mutex_; - - void DestroyLayersLocked( - const std::shared_ptr& jni_facade); + // The `GrContext` that is currently used by the overlay surfaces. + // We track this to know when the GrContext for the Flutter app has changed + // so we can update the overlay with the new context. + // + // This may change when the overlay is recycled. + intptr_t gr_context_key_; }; } // namespace flutter diff --git a/shell/platform/android/external_view_embedder/surface_pool_unittests.cc b/shell/platform/android/external_view_embedder/surface_pool_unittests.cc index d0c505675fe09..830bc73d4e6a3 100644 --- a/shell/platform/android/external_view_embedder/surface_pool_unittests.cc +++ b/shell/platform/android/external_view_embedder/surface_pool_unittests.cc @@ -5,11 +5,11 @@ #include #include "flutter/shell/platform/android/external_view_embedder/surface_pool.h" -#include "flutter/fml/make_copyable.h" #include "flutter/shell/platform/android/jni/jni_mock.h" #include "flutter/shell/platform/android/surface/android_surface_mock.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "include/core/SkSize.h" #include "third_party/skia/include/gpu/GrDirectContext.h" namespace flutter { @@ -58,13 +58,13 @@ TEST(SurfacePool, GetLayerAllocateOneLayer) { EXPECT_CALL(*android_surface_mock, IsValid()).WillOnce(Return(true)); return android_surface_mock; }); - auto layer = pool->GetLayer(gr_context.get(), *android_context, jni_mock, - surface_factory); + EXPECT_FALSE(pool->CheckLayerProperties(gr_context.get(), SkISize{100, 100})); + pool->CreateLayer(gr_context.get(), *android_context, jni_mock, + surface_factory); + auto layer = pool->GetNextLayer(); - ASSERT_TRUE(pool->HasLayers()); - ASSERT_NE(nullptr, layer); - ASSERT_EQ(reinterpret_cast(gr_context.get()), - layer->gr_context_key); + EXPECT_TRUE(pool->size() > 0); + EXPECT_NE(nullptr, layer); } TEST(SurfacePool, GetUnusedLayers) { @@ -89,15 +89,17 @@ TEST(SurfacePool, GetUnusedLayers) { EXPECT_CALL(*android_surface_mock, IsValid()).WillOnce(Return(true)); return android_surface_mock; }); - auto layer = pool->GetLayer(gr_context.get(), *android_context, jni_mock, - surface_factory); - ASSERT_EQ(0UL, pool->GetUnusedLayers().size()); + EXPECT_FALSE(pool->CheckLayerProperties(gr_context.get(), SkISize{100, 100})); + pool->CreateLayer(gr_context.get(), *android_context, jni_mock, + surface_factory); + auto layer = pool->GetNextLayer(); + EXPECT_EQ(0UL, pool->GetUnusedLayers().size()); pool->RecycleLayers(); - ASSERT_TRUE(pool->HasLayers()); - ASSERT_EQ(1UL, pool->GetUnusedLayers().size()); - ASSERT_EQ(layer, pool->GetUnusedLayers()[0]); + EXPECT_EQ(pool->size(), 1u); + EXPECT_EQ(1UL, pool->GetUnusedLayers().size()); + EXPECT_EQ(layer, pool->GetUnusedLayers()[0]); } TEST(SurfacePool, GetLayerRecycle) { @@ -107,42 +109,56 @@ TEST(SurfacePool, GetLayerRecycle) { auto jni_mock = std::make_shared(); auto window = fml::MakeRefCounted(nullptr); EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface()) + .Times(2) .WillOnce(Return( ByMove(std::make_unique( - 0, window)))); + 0, window)))) + .WillOnce(Return( + ByMove(std::make_unique( + 1, window)))); + EXPECT_CALL(*jni_mock, FlutterViewDestroyOverlaySurfaces()).Times(1); auto android_context = std::make_shared(AndroidRenderingAPI::kSoftware); + bool first = true; auto gr_context_2 = GrDirectContext::MakeMock(nullptr); auto surface_factory = std::make_shared( - [gr_context_1, gr_context_2, window]() { + [gr_context_1, gr_context_2, &first, window]() { auto android_surface_mock = std::make_unique(); // Allocate two GPU surfaces for each gr context. - EXPECT_CALL(*android_surface_mock, - CreateGPUSurface(gr_context_1.get())); - EXPECT_CALL(*android_surface_mock, - CreateGPUSurface(gr_context_2.get())); + if (first) { + EXPECT_CALL(*android_surface_mock, + CreateGPUSurface(gr_context_1.get())); + } else { + EXPECT_CALL(*android_surface_mock, + CreateGPUSurface(gr_context_2.get())); + } + first = false; // Set the native window once. EXPECT_CALL(*android_surface_mock, SetNativeWindow(window)); EXPECT_CALL(*android_surface_mock, IsValid()).WillOnce(Return(true)); return android_surface_mock; }); - auto layer_1 = pool->GetLayer(gr_context_1.get(), *android_context, jni_mock, - surface_factory); + EXPECT_FALSE( + pool->CheckLayerProperties(gr_context_1.get(), SkISize{100, 100})); + pool->CreateLayer(gr_context_1.get(), *android_context, jni_mock, + surface_factory); + auto layer_1 = pool->GetNextLayer(); pool->RecycleLayers(); - auto layer_2 = pool->GetLayer(gr_context_2.get(), *android_context, jni_mock, - surface_factory); + EXPECT_TRUE( + pool->CheckLayerProperties(gr_context_2.get(), SkISize{100, 100})); + pool->DestroyLayers(jni_mock); - ASSERT_TRUE(pool->HasLayers()); - ASSERT_NE(nullptr, layer_1); - ASSERT_EQ(layer_1, layer_2); - ASSERT_EQ(reinterpret_cast(gr_context_2.get()), - layer_1->gr_context_key); - ASSERT_EQ(reinterpret_cast(gr_context_2.get()), - layer_2->gr_context_key); + pool->CreateLayer(gr_context_2.get(), *android_context, jni_mock, + surface_factory); + auto layer_2 = pool->GetNextLayer(); + + EXPECT_TRUE(pool->size() > 0); + EXPECT_NE(nullptr, layer_1); + EXPECT_NE(layer_1, layer_2); } TEST(SurfacePool, GetLayerAllocateTwoLayers) { @@ -171,17 +187,21 @@ TEST(SurfacePool, GetLayerAllocateTwoLayers) { EXPECT_CALL(*android_surface_mock, IsValid()).WillOnce(Return(true)); return android_surface_mock; }); - auto layer_1 = pool->GetLayer(gr_context.get(), *android_context, jni_mock, - surface_factory); - auto layer_2 = pool->GetLayer(gr_context.get(), *android_context, jni_mock, - surface_factory); - - ASSERT_TRUE(pool->HasLayers()); - ASSERT_NE(nullptr, layer_1); - ASSERT_NE(nullptr, layer_2); - ASSERT_NE(layer_1, layer_2); - ASSERT_EQ(0, layer_1->id); - ASSERT_EQ(1, layer_2->id); + + EXPECT_FALSE(pool->CheckLayerProperties(gr_context.get(), SkISize{100, 100})); + for (auto i = 0; i < 2; i++) { + pool->CreateLayer(gr_context.get(), *android_context, jni_mock, + surface_factory); + } + auto layer_1 = pool->GetNextLayer(); + auto layer_2 = pool->GetNextLayer(); + + EXPECT_EQ(pool->size(), 2u); + EXPECT_NE(nullptr, layer_1); + EXPECT_NE(nullptr, layer_2); + EXPECT_NE(layer_1, layer_2); + EXPECT_EQ(0, layer_1->id); + EXPECT_EQ(1, layer_2->id); } TEST(SurfacePool, DestroyLayers) { @@ -210,14 +230,16 @@ TEST(SurfacePool, DestroyLayers) { EXPECT_CALL(*android_surface_mock, IsValid()).WillOnce(Return(true)); return android_surface_mock; }); - pool->GetLayer(gr_context.get(), *android_context, jni_mock, surface_factory); + EXPECT_FALSE(pool->CheckLayerProperties(gr_context.get(), SkISize{100, 100})); + pool->CreateLayer(gr_context.get(), *android_context, jni_mock, + surface_factory); EXPECT_CALL(*jni_mock, FlutterViewDestroyOverlaySurfaces()); - ASSERT_TRUE(pool->HasLayers()); + EXPECT_EQ(pool->size(), 1u); pool->DestroyLayers(jni_mock); - ASSERT_FALSE(pool->HasLayers()); + EXPECT_EQ(pool->size(), 0u); ASSERT_TRUE(pool->GetUnusedLayers().empty()); } @@ -239,31 +261,36 @@ TEST(SurfacePool, DestroyLayersFrameSizeChanged) { EXPECT_CALL(*android_surface_mock, IsValid()).WillOnce(Return(true)); return android_surface_mock; }); - pool->SetFrameSize(SkISize::Make(10, 10)); - EXPECT_CALL(*jni_mock, FlutterViewDestroyOverlaySurfaces()).Times(0); + EXPECT_FALSE(pool->CheckLayerProperties(gr_context.get(), SkISize{10, 10})); + + EXPECT_CALL(*jni_mock, FlutterViewDestroyOverlaySurfaces()).Times(1); EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface()) .Times(1) .WillOnce(Return( ByMove(std::make_unique( 0, window)))); - ASSERT_FALSE(pool->HasLayers()); - - pool->GetLayer(gr_context.get(), *android_context, jni_mock, surface_factory); + EXPECT_EQ(pool->size(), 0u); + pool->CreateLayer(gr_context.get(), *android_context, jni_mock, + surface_factory); - ASSERT_TRUE(pool->HasLayers()); + EXPECT_EQ(pool->size(), 1u); + EXPECT_TRUE(pool->CheckLayerProperties(gr_context.get(), SkISize{20, 20})); + pool->DestroyLayers(jni_mock); - pool->SetFrameSize(SkISize::Make(20, 20)); - EXPECT_CALL(*jni_mock, FlutterViewDestroyOverlaySurfaces()).Times(1); + // EXPECT_CALL(*jni_mock, FlutterViewDestroyOverlaySurfaces()).Times(1); EXPECT_CALL(*jni_mock, FlutterViewCreateOverlaySurface()) .Times(1) .WillOnce(Return( ByMove(std::make_unique( 1, window)))); - pool->GetLayer(gr_context.get(), *android_context, jni_mock, surface_factory); - ASSERT_TRUE(pool->GetUnusedLayers().empty()); - ASSERT_TRUE(pool->HasLayers()); + pool->CreateLayer(gr_context.get(), *android_context, jni_mock, + surface_factory); + pool->GetNextLayer(); + + EXPECT_TRUE(pool->GetUnusedLayers().empty()); + EXPECT_EQ(pool->size(), 1u); } } // namespace testing diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index e24953bd8ee1b..107d5a5b56684 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -1356,7 +1356,6 @@ public void requestDartDeferredLibrary(int loadingUnitId) { if (deferredComponentManager != null) { deferredComponentManager.installDeferredComponent(loadingUnitId, null); } else { - // TODO(garyq): Add link to setup/instructions guide wiki. Log.e( TAG, "No DeferredComponentManager found. Android setup must be completed before using split AOT deferred components."); @@ -1459,7 +1458,15 @@ public void onDisplayPlatformView( viewId, x, y, width, height, viewWidth, viewHeight, mutatorsStack); } - // TODO(mattcarroll): determine if this is nonull or nullable + @UiThread + public boolean isAttachedToView() { + ensureRunningOnMainThread(); + if (platformViewsController == null) { + return false; + } + return platformViewsController.isAttachedToView(); + } + @UiThread public Bitmap getBitmap() { ensureRunningOnMainThread(); @@ -1467,7 +1474,6 @@ public Bitmap getBitmap() { return nativeGetBitmap(nativeShellHolderId); } - // TODO(mattcarroll): determine if this is nonull or nullable private native Bitmap nativeGetBitmap(long nativeShellHolderId); /** diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java index 9c78a3f030135..cd4ccfd7a0d90 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java @@ -599,7 +599,7 @@ public long configureForTextureLayerComposition( @NonNull PlatformView platformView, @NonNull PlatformViewsChannel.PlatformViewCreationRequest request) { // This mode attaches the view to the Android view hierarchy and record its drawing - // operations, so they can be forwarded to a GL texture that is composed by the + // operations, so they can be forwarded to a GL texture that is composited by the // Flutter engine. // API level 23 is required to use Surface#lockHardwareCanvas(). @@ -801,12 +801,17 @@ public void detach() { if (platformViewsChannel != null) { platformViewsChannel.setPlatformViewsHandler(null); } - destroyOverlaySurfaces(); + // destroyOverlaySurfaces(); platformViewsChannel = null; context = null; textureRegistry = null; } + /** Whether the platform view controller is attached to an engine. */ + public boolean isAttachedToView() { + return flutterView != null; + } + /** * Attaches the controller to a {@link FlutterView}. * @@ -851,7 +856,7 @@ public void detachFromView() { flutterView.removeView(view); } - destroyOverlaySurfaces(); + // destroyOverlaySurfaces(); removeOverlaySurfaces(); flutterView = null; flutterViewConvertedToImageView = false; @@ -987,16 +992,16 @@ private static PlatformViewRenderTarget makePlatformViewRenderTarget( TextureRegistry textureRegistry) { if (enableSurfaceProducerRenderTarget && Build.VERSION.SDK_INT >= API_LEVELS.API_29) { final TextureRegistry.SurfaceProducer textureEntry = textureRegistry.createSurfaceProducer(); - Log.i(TAG, "PlatformView is using SurfaceProducer backend"); + Log.v(TAG, "PlatformView is using SurfaceProducer backend"); return new SurfaceProducerPlatformViewRenderTarget(textureEntry); } if (enableImageRenderTarget && Build.VERSION.SDK_INT >= API_LEVELS.API_29) { final TextureRegistry.ImageTextureEntry textureEntry = textureRegistry.createImageTexture(); - Log.i(TAG, "PlatformView is using ImageReader backend"); + Log.v(TAG, "PlatformView is using ImageReader backend"); return new ImageReaderPlatformViewRenderTarget(textureEntry); } final TextureRegistry.SurfaceTextureEntry textureEntry = textureRegistry.createSurfaceTexture(); - Log.i(TAG, "PlatformView is using SurfaceTexture backend"); + Log.v(TAG, "PlatformView is using SurfaceTexture backend"); return new SurfaceTexturePlatformViewRenderTarget(textureEntry); } @@ -1269,9 +1274,9 @@ public void onEndFrame() { // If one of the surfaces doesn't have an image, the frame may be incomplete and must be // dropped. // For example, a toolbar widget painted by Flutter may not be rendered. - final boolean isFrameRenderedUsingImageReaders = - flutterViewConvertedToImageView && flutterView.acquireLatestImageViewFrame(); - finishFrame(isFrameRenderedUsingImageReaders); + flutterView.acquireLatestImageViewFrame(); + + finishFrame(flutterViewConvertedToImageView); } private void finishFrame(boolean isFrameRenderedUsingImageReaders) { @@ -1281,8 +1286,7 @@ private void finishFrame(boolean isFrameRenderedUsingImageReaders) { if (currentFrameUsedOverlayLayerIds.contains(overlayId)) { flutterView.attachOverlaySurfaceToRender(overlayView); - final boolean didAcquireOverlaySurfaceImage = overlayView.acquireLatestImage(); - isFrameRenderedUsingImageReaders &= didAcquireOverlaySurfaceImage; + isFrameRenderedUsingImageReaders |= overlayView.acquireLatestImage(); } else { // If the background surface isn't rendered by the image view, then the // overlay surfaces can be detached from the rendered. diff --git a/shell/platform/android/jni/jni_mock.h b/shell/platform/android/jni/jni_mock.h index cf5b329256e6c..b9f636a630b01 100644 --- a/shell/platform/android/jni/jni_mock.h +++ b/shell/platform/android/jni/jni_mock.h @@ -102,7 +102,7 @@ class JNIMock final : public PlatformViewAndroidJNI { FlutterViewDisplayOverlaySurface, (int surface_id, int x, int y, int width, int height), (override)); - + MOCK_METHOD(bool, IsAttachedToView, (), (override)); MOCK_METHOD(void, FlutterViewBeginFrame, (), (override)); MOCK_METHOD(void, FlutterViewEndFrame, (), (override)); diff --git a/shell/platform/android/jni/platform_view_android_jni.h b/shell/platform/android/jni/platform_view_android_jni.h index 356b6776fc631..2b2a8c0b6c900 100644 --- a/shell/platform/android/jni/platform_view_android_jni.h +++ b/shell/platform/android/jni/platform_view_android_jni.h @@ -84,6 +84,12 @@ class PlatformViewAndroidJNI { /// virtual void FlutterViewOnPreEngineRestart() = 0; + //---------------------------------------------------------------------------- + /// @brief Whether the platform view controller is actively connected to + /// a FlutterView instance. + /// + virtual bool IsAttachedToView() = 0; + //---------------------------------------------------------------------------- /// @brief Attach the SurfaceTexture to the OpenGL ES context that is /// current on the calling thread. diff --git a/shell/platform/android/platform_view_android_jni_impl.cc b/shell/platform/android/platform_view_android_jni_impl.cc index 8a3f0d7bd5989..cb6497f00e2cf 100644 --- a/shell/platform/android/platform_view_android_jni_impl.cc +++ b/shell/platform/android/platform_view_android_jni_impl.cc @@ -132,10 +132,10 @@ static jmethodID g_request_dart_deferred_library_method = nullptr; // Called By Java static jmethodID g_on_display_platform_view_method = nullptr; -// static jmethodID g_on_composite_platform_view_method = nullptr; - static jmethodID g_on_display_overlay_surface_method = nullptr; +static jmethodID g_is_flutter_view_attached_method = nullptr; + static jmethodID g_overlay_surface_id_method = nullptr; static jmethodID g_overlay_surface_surface_method = nullptr; @@ -1109,6 +1109,13 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { return false; } + g_is_flutter_view_attached_method = + env->GetMethodID(g_flutter_jni_class->obj(), "isAttachedToView", "()Z"); + if (g_is_flutter_view_attached_method == nullptr) { + FML_LOG(ERROR) << "Could not locate isAttachedToView method"; + return false; + } + g_on_end_frame_method = env->GetMethodID(g_flutter_jni_class->obj(), "onEndFrame", "()V"); @@ -1618,6 +1625,20 @@ void PlatformViewAndroidJNIImpl::HardwareBufferClose( FML_CHECK(fml::jni::CheckException(env)); } +bool PlatformViewAndroidJNIImpl::IsAttachedToView() { + JNIEnv* env = fml::jni::AttachCurrentThread(); + + auto java_object = java_object_.get(env); + if (java_object.is_null()) { + return false; + } + + jboolean result = env->CallBooleanMethod(java_object.obj(), + g_is_flutter_view_attached_method); + FML_CHECK(fml::jni::CheckException(env)); + return result; +} + void PlatformViewAndroidJNIImpl::FlutterViewOnDisplayPlatformView( int view_id, int x, diff --git a/shell/platform/android/platform_view_android_jni_impl.h b/shell/platform/android/platform_view_android_jni_impl.h index 73cb6a43cffdb..d6ac550db9d41 100644 --- a/shell/platform/android/platform_view_android_jni_impl.h +++ b/shell/platform/android/platform_view_android_jni_impl.h @@ -81,6 +81,8 @@ class PlatformViewAndroidJNIImpl final : public PlatformViewAndroidJNI { void FlutterViewEndFrame() override; + bool IsAttachedToView() override; + std::unique_ptr FlutterViewCreateOverlaySurface() override; diff --git a/shell/platform/android/surface/android_surface.cc b/shell/platform/android/surface/android_surface.cc index e301491b5fce0..f7f0be6361417 100644 --- a/shell/platform/android/surface/android_surface.cc +++ b/shell/platform/android/surface/android_surface.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "flutter/shell/platform/android/surface/android_surface.h" -#include "flutter/fml/logging.h" namespace flutter {