diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 5c9e0ac454d9c..c05267758d76d 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -592,6 +592,8 @@ FILE: ../../../flutter/shell/common/shell_io_manager.cc FILE: ../../../flutter/shell/common/shell_io_manager.h FILE: ../../../flutter/shell/common/shell_test.cc FILE: ../../../flutter/shell/common/shell_test.h +FILE: ../../../flutter/shell/common/shell_test_external_view_embedder.cc +FILE: ../../../flutter/shell/common/shell_test_external_view_embedder.h FILE: ../../../flutter/shell/common/shell_test_platform_view.cc FILE: ../../../flutter/shell/common/shell_test_platform_view.h FILE: ../../../flutter/shell/common/shell_test_platform_view_gl.cc diff --git a/flow/embedded_views.h b/flow/embedded_views.h index b80f7b4ba5d54..14dd00cf5e8c8 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -253,6 +253,18 @@ class ExternalViewEmbedder { // This is called after submitting the embedder frame and the surface frame. virtual void FinishFrame(); + // This should only be called after |SubmitFrame|. + // This method provides the embedder a way to do additional tasks after + // |SubmitFrame|. After invoking this method, the current task on the + // TaskRunner should end immediately. + // + // For example on the iOS embedder, threads are merged in this call. + // A new frame on the platform thread starts immediately. If the GPU thread + // still has some task running, there could be two frames being rendered + // concurrently, which causes undefined behaviors. + virtual void EndFrame( + fml::RefPtr raster_thread_merger) {} + FML_DISALLOW_COPY_AND_ASSIGN(ExternalViewEmbedder); }; // ExternalViewEmbedder diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index 66684679c3de3..3fab3317b094b 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -191,6 +191,8 @@ if (enable_unittests) { "pipeline_unittests.cc", "shell_test.cc", "shell_test.h", + "shell_test_external_view_embedder.cc", + "shell_test_external_view_embedder.h", "shell_test_platform_view.cc", "shell_test_platform_view.h", "shell_unittests.cc", diff --git a/shell/common/animator_unittests.cc b/shell/common/animator_unittests.cc index ed54ea5272be2..bf2ac9f5a9cf1 100644 --- a/shell/common/animator_unittests.cc +++ b/shell/common/animator_unittests.cc @@ -55,7 +55,7 @@ TEST_F(ShellTest, VSyncTargetTime) { return ShellTestPlatformView::Create( shell, shell.GetTaskRunners(), vsync_clock, std::move(create_vsync_waiter), - ShellTestPlatformView::BackendType::kDefaultBackend); + ShellTestPlatformView::BackendType::kDefaultBackend, nullptr); }, [](Shell& shell) { return std::make_unique(shell, shell.GetTaskRunners()); diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index bce1b2bf036cb..ce456925c69ac 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -136,6 +136,16 @@ void Rasterizer::Draw(fml::RefPtr> pipeline) { consume_result = PipelineConsumeResult::MoreAvailable; } + // Merging the thread as we know the next `Draw` should be run on the platform + // thread. + if (raster_status == RasterStatus::kResubmit) { + auto* external_view_embedder = surface_->GetExternalViewEmbedder(); + // We know only the `external_view_embedder` can + // causes|RasterStatus::kResubmit|. Check to make sure. + FML_DCHECK(external_view_embedder != nullptr); + external_view_embedder->EndFrame(raster_thread_merger_); + } + // Consume as many pipeline items as possible. But yield the event loop // between successive tries. switch (consume_result) { diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 378375c5eac1e..cf7f9ed8d801b 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -260,9 +260,12 @@ std::unique_ptr ShellTest::CreateShell(Settings settings, simulate_vsync); } -std::unique_ptr ShellTest::CreateShell(Settings settings, - TaskRunners task_runners, - bool simulate_vsync) { +std::unique_ptr ShellTest::CreateShell( + Settings settings, + TaskRunners task_runners, + bool simulate_vsync, + std::shared_ptr + shell_test_external_view_embedder) { const auto vsync_clock = std::make_shared(); CreateVsyncWaiter create_vsync_waiter = [&]() { if (simulate_vsync) { @@ -275,17 +278,18 @@ std::unique_ptr ShellTest::CreateShell(Settings settings, }; return Shell::Create( task_runners, settings, - [vsync_clock, &create_vsync_waiter](Shell& shell) { + [vsync_clock, &create_vsync_waiter, + shell_test_external_view_embedder](Shell& shell) { return ShellTestPlatformView::Create( shell, shell.GetTaskRunners(), vsync_clock, std::move(create_vsync_waiter), - ShellTestPlatformView::BackendType::kDefaultBackend); + ShellTestPlatformView::BackendType::kDefaultBackend, + shell_test_external_view_embedder); }, [](Shell& shell) { return std::make_unique(shell, shell.GetTaskRunners()); }); } - void ShellTest::DestroyShell(std::unique_ptr shell) { DestroyShell(std::move(shell), GetTaskRunnersForFixture()); } diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 680db7d349c1d..9f02aa35e68b3 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -15,6 +15,7 @@ #include "flutter/lib/ui/window/platform_message.h" #include "flutter/shell/common/run_configuration.h" #include "flutter/shell/common/shell.h" +#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/thread_host.h" #include "flutter/shell/common/vsync_waiters_test.h" #include "flutter/testing/elf_loader.h" @@ -31,9 +32,12 @@ class ShellTest : public ThreadTest { Settings CreateSettingsForFixture(); std::unique_ptr CreateShell(Settings settings, bool simulate_vsync = false); - std::unique_ptr CreateShell(Settings settings, - TaskRunners task_runners, - bool simulate_vsync = false); + std::unique_ptr CreateShell( + Settings settings, + TaskRunners task_runners, + bool simulate_vsync = false, + std::shared_ptr + shell_test_external_view_embedder = nullptr); void DestroyShell(std::unique_ptr shell); void DestroyShell(std::unique_ptr shell, TaskRunners task_runners); TaskRunners GetTaskRunnersForFixture(); diff --git a/shell/common/shell_test_external_view_embedder.cc b/shell/common/shell_test_external_view_embedder.cc new file mode 100644 index 0000000000000..683e21ad73c91 --- /dev/null +++ b/shell/common/shell_test_external_view_embedder.cc @@ -0,0 +1,55 @@ +#include "shell_test_external_view_embedder.h" + +namespace flutter { + +// |ExternalViewEmbedder| +void ShellTestExternalViewEmbedder::CancelFrame() {} + +// |ExternalViewEmbedder| +void ShellTestExternalViewEmbedder::BeginFrame(SkISize frame_size, + GrContext* context, + double device_pixel_ratio) {} + +// |ExternalViewEmbedder| +void ShellTestExternalViewEmbedder::PrerollCompositeEmbeddedView( + int view_id, + std::unique_ptr params) {} + +// |ExternalViewEmbedder| +PostPrerollResult ShellTestExternalViewEmbedder::PostPrerollAction( + fml::RefPtr raster_thread_merger) { + FML_DCHECK(raster_thread_merger); + return post_preroll_result_; +} + +// |ExternalViewEmbedder| +std::vector ShellTestExternalViewEmbedder::GetCurrentCanvases() { + return {}; +} + +// |ExternalViewEmbedder| +SkCanvas* ShellTestExternalViewEmbedder::CompositeEmbeddedView(int view_id) { + return nullptr; +} + +// |ExternalViewEmbedder| +bool ShellTestExternalViewEmbedder::SubmitFrame(GrContext* context, + SkCanvas* background_canvas) { + return true; +} + +// |ExternalViewEmbedder| +void ShellTestExternalViewEmbedder::FinishFrame() {} + +// |ExternalViewEmbedder| +void ShellTestExternalViewEmbedder::EndFrame( + fml::RefPtr raster_thread_merger) { + end_frame_call_back_(); +} + +// |ExternalViewEmbedder| +SkCanvas* ShellTestExternalViewEmbedder::GetRootCanvas() { + return nullptr; +} + +} // namespace flutter diff --git a/shell/common/shell_test_external_view_embedder.h b/shell/common/shell_test_external_view_embedder.h new file mode 100644 index 0000000000000..56d9123ea8a7a --- /dev/null +++ b/shell/common/shell_test_external_view_embedder.h @@ -0,0 +1,72 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_TEST_EXTERNAL_VIEW_EMBEDDER_H_ +#define FLUTTER_SHELL_TEST_EXTERNAL_VIEW_EMBEDDER_H_ + +#include "flutter/flow/embedded_views.h" +#include "flutter/fml/raster_thread_merger.h" + +namespace flutter { + +//------------------------------------------------------------------------------ +/// @brief The external view embedder used by |ShellTestPlatformViewGL| +/// +class ShellTestExternalViewEmbedder final : public ExternalViewEmbedder { + public: + using EndFrameCallBack = std::function; + + ShellTestExternalViewEmbedder(const EndFrameCallBack& end_frame_call_back, + PostPrerollResult post_preroll_result) + : end_frame_call_back_(end_frame_call_back), + post_preroll_result_(post_preroll_result) {} + + ~ShellTestExternalViewEmbedder() = default; + + private: + // |ExternalViewEmbedder| + void CancelFrame() override; + + // |ExternalViewEmbedder| + void BeginFrame(SkISize frame_size, + GrContext* context, + double device_pixel_ratio) override; + + // |ExternalViewEmbedder| + void PrerollCompositeEmbeddedView( + int view_id, + std::unique_ptr params) override; + + // |ExternalViewEmbedder| + PostPrerollResult PostPrerollAction( + fml::RefPtr raster_thread_merger) override; + + // |ExternalViewEmbedder| + std::vector GetCurrentCanvases() override; + + // |ExternalViewEmbedder| + SkCanvas* CompositeEmbeddedView(int view_id) override; + + // |ExternalViewEmbedder| + bool SubmitFrame(GrContext* context, SkCanvas* background_canvas) override; + + // |ExternalViewEmbedder| + void FinishFrame() override; + + // |ExternalViewEmbedder| + void EndFrame( + fml::RefPtr raster_thread_merger) override; + + // |ExternalViewEmbedder| + SkCanvas* GetRootCanvas() override; + + const EndFrameCallBack end_frame_call_back_; + const PostPrerollResult post_preroll_result_; + + FML_DISALLOW_COPY_AND_ASSIGN(ShellTestExternalViewEmbedder); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_TEST_EXTERNAL_VIEW_EMBEDDER_H_ diff --git a/shell/common/shell_test_platform_view.cc b/shell/common/shell_test_platform_view.cc index cfd43308bfc10..29bd24d3134ff 100644 --- a/shell/common/shell_test_platform_view.cc +++ b/shell/common/shell_test_platform_view.cc @@ -19,7 +19,9 @@ std::unique_ptr ShellTestPlatformView::Create( TaskRunners task_runners, std::shared_ptr vsync_clock, CreateVsyncWaiter create_vsync_waiter, - BackendType backend) { + BackendType backend, + std::shared_ptr + shell_test_external_view_embedder) { // TODO(gw280): https://github.com/flutter/flutter/issues/50298 // Make this fully runtime configurable switch (backend) { @@ -27,12 +29,14 @@ std::unique_ptr ShellTestPlatformView::Create( #ifdef SHELL_ENABLE_GL case BackendType::kGLBackend: return std::make_unique( - delegate, task_runners, vsync_clock, create_vsync_waiter); + delegate, task_runners, vsync_clock, create_vsync_waiter, + shell_test_external_view_embedder); #endif // SHELL_ENABLE_GL #ifdef SHELL_ENABLE_VULKAN case BackendType::kVulkanBackend: return std::make_unique( - delegate, task_runners, vsync_clock, create_vsync_waiter); + delegate, task_runners, vsync_clock, create_vsync_waiter, + shell_test_external_view_embedder); #endif // SHELL_ENABLE_VULKAN default: FML_LOG(FATAL) << "No backends supported for ShellTestPlatformView"; diff --git a/shell/common/shell_test_platform_view.h b/shell/common/shell_test_platform_view.h index c7aefe86b33bc..9a850b1d481d6 100644 --- a/shell/common/shell_test_platform_view.h +++ b/shell/common/shell_test_platform_view.h @@ -6,6 +6,7 @@ #define FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_H_ #include "flutter/shell/common/platform_view.h" +#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/vsync_waiters_test.h" namespace flutter { @@ -24,7 +25,9 @@ class ShellTestPlatformView : public PlatformView { TaskRunners task_runners, std::shared_ptr vsync_clock, CreateVsyncWaiter create_vsync_waiter, - BackendType backend); + BackendType backend, + std::shared_ptr + shell_test_external_view_embedder); virtual void SimulateVSync() = 0; diff --git a/shell/common/shell_test_platform_view_gl.cc b/shell/common/shell_test_platform_view_gl.cc index 2bd597575e523..c980c53168645 100644 --- a/shell/common/shell_test_platform_view_gl.cc +++ b/shell/common/shell_test_platform_view_gl.cc @@ -12,11 +12,14 @@ ShellTestPlatformViewGL::ShellTestPlatformViewGL( PlatformView::Delegate& delegate, TaskRunners task_runners, std::shared_ptr vsync_clock, - CreateVsyncWaiter create_vsync_waiter) + CreateVsyncWaiter create_vsync_waiter, + std::shared_ptr + shell_test_external_view_embedder) : ShellTestPlatformView(delegate, std::move(task_runners)), gl_surface_(SkISize::Make(800, 600)), create_vsync_waiter_(std::move(create_vsync_waiter)), - vsync_clock_(vsync_clock) {} + vsync_clock_(vsync_clock), + shell_test_external_view_embedder_(shell_test_external_view_embedder) {} ShellTestPlatformViewGL::~ShellTestPlatformViewGL() = default; @@ -70,7 +73,7 @@ ShellTestPlatformViewGL::GetGLProcResolver() const { // |GPUSurfaceGLDelegate| ExternalViewEmbedder* ShellTestPlatformViewGL::GetExternalViewEmbedder() { - return nullptr; + return shell_test_external_view_embedder_.get(); } } // namespace testing diff --git a/shell/common/shell_test_platform_view_gl.h b/shell/common/shell_test_platform_view_gl.h index 03db7910873e0..cac2fdc649781 100644 --- a/shell/common/shell_test_platform_view_gl.h +++ b/shell/common/shell_test_platform_view_gl.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_GL_H_ #define FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_GL_H_ +#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/shell_test_platform_view.h" #include "flutter/shell/gpu/gpu_surface_gl_delegate.h" #include "flutter/testing/test_gl_surface.h" @@ -18,7 +19,9 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView, ShellTestPlatformViewGL(PlatformView::Delegate& delegate, TaskRunners task_runners, std::shared_ptr vsync_clock, - CreateVsyncWaiter create_vsync_waiter); + CreateVsyncWaiter create_vsync_waiter, + std::shared_ptr + shell_test_external_view_embedder); virtual ~ShellTestPlatformViewGL() override; @@ -31,6 +34,9 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView, std::shared_ptr vsync_clock_; + std::shared_ptr + shell_test_external_view_embedder_; + // |PlatformView| std::unique_ptr CreateRenderingSurface() override; diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index e9f41484d9881..4c21c2dfba36e 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -11,11 +11,14 @@ ShellTestPlatformViewVulkan::ShellTestPlatformViewVulkan( PlatformView::Delegate& delegate, TaskRunners task_runners, std::shared_ptr vsync_clock, - CreateVsyncWaiter create_vsync_waiter) + CreateVsyncWaiter create_vsync_waiter, + std::shared_ptr + shell_test_external_view_embedder) : ShellTestPlatformView(delegate, std::move(task_runners)), create_vsync_waiter_(std::move(create_vsync_waiter)), vsync_clock_(vsync_clock), - proc_table_(fml::MakeRefCounted()) {} + proc_table_(fml::MakeRefCounted()), + shell_test_external_view_embedder_(shell_test_external_view_embedder) {} ShellTestPlatformViewVulkan::~ShellTestPlatformViewVulkan() = default; @@ -29,7 +32,8 @@ void ShellTestPlatformViewVulkan::SimulateVSync() { // |PlatformView| std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingSurface() { - return std::make_unique(proc_table_); + return std::make_unique(proc_table_, + shell_test_external_view_embedder_); } // |PlatformView| @@ -44,8 +48,12 @@ PointerDataDispatcherMaker ShellTestPlatformViewVulkan::GetDispatcherMaker() { // We need to merge this functionality back into //vulkan. // https://github.com/flutter/flutter/issues/51132 ShellTestPlatformViewVulkan::OffScreenSurface::OffScreenSurface( - fml::RefPtr vk) - : valid_(false), vk_(std::move(vk)) { + fml::RefPtr vk, + std::shared_ptr + shell_test_external_view_embedder) + : valid_(false), + vk_(std::move(vk)), + shell_test_external_view_embedder_(shell_test_external_view_embedder) { if (!vk_ || !vk_->HasAcquiredMandatoryProcAddresses()) { FML_DLOG(ERROR) << "Proc table has not acquired mandatory proc addresses."; return; @@ -170,5 +178,10 @@ SkMatrix ShellTestPlatformViewVulkan::OffScreenSurface::GetRootTransformation() return matrix; } +flutter::ExternalViewEmbedder* +ShellTestPlatformViewVulkan::OffScreenSurface::GetExternalViewEmbedder() { + return shell_test_external_view_embedder_.get(); +} + } // namespace testing } // namespace flutter diff --git a/shell/common/shell_test_platform_view_vulkan.h b/shell/common/shell_test_platform_view_vulkan.h index 38bce87fa5414..31757f39a30b0 100644 --- a/shell/common/shell_test_platform_view_vulkan.h +++ b/shell/common/shell_test_platform_view_vulkan.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_VULKAN_H_ #define FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_VULKAN_H_ +#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/shell_test_platform_view.h" #include "flutter/shell/gpu/gpu_surface_vulkan_delegate.h" #include "flutter/vulkan/vulkan_application.h" @@ -18,7 +19,9 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { ShellTestPlatformViewVulkan(PlatformView::Delegate& delegate, TaskRunners task_runners, std::shared_ptr vsync_clock, - CreateVsyncWaiter create_vsync_waiter); + CreateVsyncWaiter create_vsync_waiter, + std::shared_ptr + shell_test_external_view_embedder); ~ShellTestPlatformViewVulkan() override; @@ -27,7 +30,9 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { private: class OffScreenSurface : public flutter::Surface { public: - OffScreenSurface(fml::RefPtr vk); + OffScreenSurface(fml::RefPtr vk, + std::shared_ptr + shell_test_external_view_embedder); ~OffScreenSurface() override; @@ -42,9 +47,13 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { // |Surface| GrContext* GetContext() override; + flutter::ExternalViewEmbedder* GetExternalViewEmbedder() override; + private: bool valid_; fml::RefPtr vk_; + std::shared_ptr + shell_test_external_view_embedder_; std::unique_ptr application_; std::unique_ptr logical_device_; sk_sp context_; @@ -61,6 +70,9 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { fml::RefPtr proc_table_; + std::shared_ptr + shell_test_external_view_embedder_; + // |PlatformView| std::unique_ptr CreateRenderingSurface() override; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index c9c1345541fcc..720666ae1bb69 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -23,6 +23,7 @@ #include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/shell_test.h" +#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/shell_test_platform_view.h" #include "flutter/shell/common/switches.h" #include "flutter/shell/common/thread_host.h" @@ -139,7 +140,7 @@ TEST_F(ShellTest, return static_cast>( std::make_unique(task_runners)); }, - ShellTestPlatformView::BackendType::kDefaultBackend); + ShellTestPlatformView::BackendType::kDefaultBackend, nullptr); }, [](Shell& shell) { return std::make_unique(shell, shell.GetTaskRunners()); @@ -466,6 +467,51 @@ TEST_F(ShellTest, FrameRasterizedCallbackIsCalled) { DestroyShell(std::move(shell)); } +TEST_F(ShellTest, + ExternalEmbedderEndFrameIsCalledWhenPostPrerollResultIsResubmit) { + auto settings = CreateSettingsForFixture(); + fml::AutoResetWaitableEvent endFrameLatch; + bool end_frame_called = false; + auto end_frame_callback = [&] { + end_frame_called = true; + endFrameLatch.Signal(); + }; + auto external_view_embedder = std::make_shared( + end_frame_callback, PostPrerollResult::kResubmitFrame); + auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(), + false, external_view_embedder); + + // Create the surface needed by rasterizer + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("emptyMain"); + + RunEngine(shell.get(), std::move(configuration)); + + LayerTreeBuilder builder = [&](std::shared_ptr root) { + SkPictureRecorder recorder; + SkCanvas* recording_canvas = + recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80)); + recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80), + SkPaint(SkColor4f::FromColor(SK_ColorRED))); + auto sk_picture = recorder.finishRecordingAsPicture(); + fml::RefPtr queue = fml::MakeRefCounted( + this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); + auto picture_layer = std::make_shared( + SkPoint::Make(10, 10), + flutter::SkiaGPUObject({sk_picture, queue}), false, false); + root->Add(picture_layer); + }; + + PumpOneFrame(shell.get(), 100, 100, builder); + endFrameLatch.Wait(); + + ASSERT_TRUE(end_frame_called); + + DestroyShell(std::move(shell)); +} + TEST(SettingsTest, FrameTimingSetsAndGetsProperly) { // Ensure that all phases are in kPhases. ASSERT_EQ(sizeof(FrameTiming::kPhases), diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 3fbaa7dd8a935..2c2a91f4836a6 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -248,7 +248,7 @@ } void FlutterPlatformViewsController::CancelFrame() { - composition_order_.clear(); + composition_order_ = active_composition_order_; } bool FlutterPlatformViewsController::HasPendingViewOperations() { @@ -267,8 +267,9 @@ if (raster_thread_merger->IsMerged()) { raster_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration); } else { + // Wait until |EndFrame| to merge the threads. + merge_threads_ = true; CancelFrame(); - raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); return PostPrerollResult::kResubmitFrame; } } @@ -468,6 +469,21 @@ bool FlutterPlatformViewsController::SubmitFrame(GrContext* gr_context, std::shared_ptr ios_context, SkCanvas* background_canvas) { + if (merge_threads_) { + // Threads are about to be merged, we drop everything from this frame + // and possibly resubmit the same layer tree in the next frame. + // Before merging thread, we know the code is not running on the main thread. Assert that + FML_DCHECK(![[NSThread currentThread] isMainThread]); + picture_recorders_.clear(); + composition_order_.clear(); + return true; + } + + // Any UIKit related code has to run on main thread. + // When on a non-main thread, we only allow the rest of the method to run if there is no platform + // view. + FML_DCHECK([[NSThread currentThread] isMainThread] || views_to_dispose_.empty()); + DisposeViews(); // Resolve all pending GPU operations before allocating a new surface. @@ -578,6 +594,14 @@ } } +void FlutterPlatformViewsController::EndFrame( + fml::RefPtr raster_thread_merger) { + if (merge_threads_) { + raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); + merge_threads_ = false; + } +} + std::shared_ptr FlutterPlatformViewsController::GetLayer( GrContext* gr_context, std::shared_ptr ios_context, @@ -642,6 +666,8 @@ return; } + FML_DCHECK([[NSThread currentThread] isMainThread]); + for (int64_t viewId : views_to_dispose_) { UIView* root_view = root_views_[viewId].get(); [root_view removeFromSuperview]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index e998640777c96..6532be8ca14c2 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -134,6 +134,8 @@ class FlutterPlatformViewsController { void SetFrameSize(SkISize frame_size); + // Indicates that we don't compisite any platform views or overlays during this frame. + // Also reverts the composition_order_ to its original state at the begining of the frame. void CancelFrame(); void PrerollCompositeEmbeddedView(int view_id, @@ -163,6 +165,11 @@ class FlutterPlatformViewsController { std::shared_ptr ios_context, SkCanvas* background_canvas); + // Invoked at the very end of a frame. + // After invoking this method, nothing should happen on the current TaskRunner during the same + // frame. + void EndFrame(fml::RefPtr raster_thread_merger); + void OnMethodCall(FlutterMethodCall* call, FlutterResult& result); private: @@ -273,6 +280,10 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view); void CompositeWithParams(int view_id, const EmbeddedViewParams& params); + // Default to `false`. + // If `true`, gpu thread and platform thread should be merged during |EndFrame|. + // Always resets to `false` right after the threads are merged. + bool merge_threads_; // Allocates a new FlutterPlatformViewLayer if needed, draws the pixels within the rect from // the picture on the layer's canvas. std::shared_ptr GetLayer(GrContext* gr_context, @@ -282,6 +293,7 @@ class FlutterPlatformViewsController { int64_t view_id, int64_t overlay_id); // Removes overlay views and platform views that aren't needed in the current frame. + // Must run on the platform thread. void RemoveUnusedLayers(); // Appends the overlay views and platform view and sets their z index based on the composition // order. diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index fd1bc7abb0b48..eae39031d77ce 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -83,6 +83,9 @@ class IOSSurface : public ExternalViewEmbedder { // |ExternalViewEmbedder| void FinishFrame() override; + // |ExternalViewEmbedder| + void EndFrame(fml::RefPtr raster_thread_merger) override; + public: FML_DISALLOW_COPY_AND_ASSIGN(IOSSurface); }; diff --git a/shell/platform/darwin/ios/ios_surface.mm b/shell/platform/darwin/ios/ios_surface.mm index cbe2cb4fb8c2c..5f2725273e0dd 100644 --- a/shell/platform/darwin/ios/ios_surface.mm +++ b/shell/platform/darwin/ios/ios_surface.mm @@ -90,7 +90,12 @@ bool IsIosEmbeddedViewsPreviewEnabled() { platform_views_controller_->CancelFrame(); // Committing the current transaction as |BeginFrame| will create a nested // CATransaction otherwise. - [CATransaction commit]; + if ([[NSThread currentThread] isMainThread]) { + // The only time we need to commit the `CATranscation` is when + // there are platform views in the scene, which has to be run on the + // main thread. + [CATransaction commit]; + } } // |ExternalViewEmbedder| @@ -98,7 +103,12 @@ bool IsIosEmbeddedViewsPreviewEnabled() { TRACE_EVENT0("flutter", "IOSSurface::BeginFrame"); FML_CHECK(platform_views_controller_ != nullptr); platform_views_controller_->SetFrameSize(frame_size); - [CATransaction begin]; + if ([[NSThread currentThread] isMainThread]) { + // The only time we need to commit the `CATranscation` is when + // there are platform views in the scene, which has to be run on the + // main thread. + [CATransaction begin]; + } } // |ExternalViewEmbedder| @@ -140,10 +150,22 @@ bool IsIosEmbeddedViewsPreviewEnabled() { return submitted; } +// |ExternalViewEmbedder| +void IOSSurface::EndFrame(fml::RefPtr raster_thread_merger) { + TRACE_EVENT0("flutter", "IOSSurface::EndFrame"); + FML_CHECK(platform_views_controller_ != nullptr); + return platform_views_controller_->EndFrame(raster_thread_merger); +} + // |ExternalViewEmbedder| void IOSSurface::FinishFrame() { TRACE_EVENT0("flutter", "IOSSurface::DidSubmitFrame"); + if (![[NSThread currentThread] isMainThread]) { + return; + } + // The only time we need to commit the `CATranscation` is when + // there are platform views in the scene, which has to be run on the + // main thread. [CATransaction commit]; } - } // namespace flutter