From 79e470d970b5506b515e43d105ed07dadc4edc39 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 6 Mar 2023 17:33:57 -0800 Subject: [PATCH 001/147] Move context out --- shell/common/shell_test_platform_view_gl.cc | 2 +- shell/gpu/gpu_surface_gl_skia.cc | 6 ------ shell/gpu/gpu_surface_gl_skia.h | 3 --- shell/platform/embedder/embedder_surface_gl.cc | 4 ++++ shell/platform/embedder/embedder_surface_gl.h | 1 + 5 files changed, 6 insertions(+), 10 deletions(-) diff --git a/shell/common/shell_test_platform_view_gl.cc b/shell/common/shell_test_platform_view_gl.cc index 98323108fac44..ce9ba2ae292c7 100644 --- a/shell/common/shell_test_platform_view_gl.cc +++ b/shell/common/shell_test_platform_view_gl.cc @@ -38,7 +38,7 @@ void ShellTestPlatformViewGL::SimulateVSync() { // |PlatformView| std::unique_ptr ShellTestPlatformViewGL::CreateRenderingSurface() { - return std::make_unique(this, true); + return std::make_unique(GPUSurfaceGLSkia::MakeGLContext(this), this, true); } // |PlatformView| diff --git a/shell/gpu/gpu_surface_gl_skia.cc b/shell/gpu/gpu_surface_gl_skia.cc index cdd8a24455d9f..ab8b62c4995ab 100644 --- a/shell/gpu/gpu_surface_gl_skia.cc +++ b/shell/gpu/gpu_surface_gl_skia.cc @@ -62,12 +62,6 @@ sk_sp GPUSurfaceGLSkia::MakeGLContext( return context; } -GPUSurfaceGLSkia::GPUSurfaceGLSkia(GPUSurfaceGLDelegate* delegate, - bool render_to_surface) - : GPUSurfaceGLSkia(MakeGLContext(delegate), delegate, render_to_surface) { - context_owner_ = true; -} - GPUSurfaceGLSkia::GPUSurfaceGLSkia(const sk_sp& gr_context, GPUSurfaceGLDelegate* delegate, bool render_to_surface) diff --git a/shell/gpu/gpu_surface_gl_skia.h b/shell/gpu/gpu_surface_gl_skia.h index 6aef2096f94df..2b40e53f27055 100644 --- a/shell/gpu/gpu_surface_gl_skia.h +++ b/shell/gpu/gpu_surface_gl_skia.h @@ -24,9 +24,6 @@ class GPUSurfaceGLSkia : public Surface { public: static sk_sp MakeGLContext(GPUSurfaceGLDelegate* delegate); - GPUSurfaceGLSkia(GPUSurfaceGLDelegate* delegate, bool render_to_surface); - - // Creates a new GL surface reusing an existing GrDirectContext. GPUSurfaceGLSkia(const sk_sp& gr_context, GPUSurfaceGLDelegate* delegate, bool render_to_surface); diff --git a/shell/platform/embedder/embedder_surface_gl.cc b/shell/platform/embedder/embedder_surface_gl.cc index 44b1fddf808fb..a5dff2c28c31a 100644 --- a/shell/platform/embedder/embedder_surface_gl.cc +++ b/shell/platform/embedder/embedder_surface_gl.cc @@ -95,8 +95,12 @@ SurfaceFrame::FramebufferInfo EmbedderSurfaceGL::GLContextFramebufferInfo() // |EmbedderSurface| std::unique_ptr EmbedderSurfaceGL::CreateGPUSurface() { + if (!main_context_) { + main_context_ = GPUSurfaceGLSkia::MakeGLContext(this); + } const bool render_to_surface = !external_view_embedder_; return std::make_unique( + main_context_, this, // GPU surface GL delegate render_to_surface // render to surface ); diff --git a/shell/platform/embedder/embedder_surface_gl.h b/shell/platform/embedder/embedder_surface_gl.h index 695aeea84b860..10d3c8c75b671 100644 --- a/shell/platform/embedder/embedder_surface_gl.h +++ b/shell/platform/embedder/embedder_surface_gl.h @@ -38,6 +38,7 @@ class EmbedderSurfaceGL final : public EmbedderSurface, bool valid_ = false; GLDispatchTable gl_dispatch_table_; bool fbo_reset_after_present_; + sk_sp main_context_; std::shared_ptr external_view_embedder_; From 3e211d5db0f62aea470aa8a5aa1774e77b8a9f78 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 6 Mar 2023 17:34:25 -0800 Subject: [PATCH 002/147] Format --- shell/common/shell_test_platform_view_gl.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/common/shell_test_platform_view_gl.cc b/shell/common/shell_test_platform_view_gl.cc index ce9ba2ae292c7..3173e2946b83c 100644 --- a/shell/common/shell_test_platform_view_gl.cc +++ b/shell/common/shell_test_platform_view_gl.cc @@ -38,7 +38,8 @@ void ShellTestPlatformViewGL::SimulateVSync() { // |PlatformView| std::unique_ptr ShellTestPlatformViewGL::CreateRenderingSurface() { - return std::make_unique(GPUSurfaceGLSkia::MakeGLContext(this), this, true); + return std::make_unique( + GPUSurfaceGLSkia::MakeGLContext(this), this, true); } // |PlatformView| From c59b47d5e436c32e22367a3a0dcbace214a0f831 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 13 Mar 2023 12:07:22 -0700 Subject: [PATCH 003/147] Release main_context_ --- shell/gpu/gpu_surface_gl_skia.cc | 3 --- shell/platform/embedder/embedder_surface_gl.cc | 6 +++++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/shell/gpu/gpu_surface_gl_skia.cc b/shell/gpu/gpu_surface_gl_skia.cc index ab8b62c4995ab..e5662366b1ea6 100644 --- a/shell/gpu/gpu_surface_gl_skia.cc +++ b/shell/gpu/gpu_surface_gl_skia.cc @@ -95,9 +95,6 @@ GPUSurfaceGLSkia::~GPUSurfaceGLSkia() { onscreen_surface_ = nullptr; fbo_id_ = 0; - if (context_owner_) { - context_->releaseResourcesAndAbandonContext(); - } context_ = nullptr; delegate_->GLContextClearCurrent(); diff --git a/shell/platform/embedder/embedder_surface_gl.cc b/shell/platform/embedder/embedder_surface_gl.cc index a5dff2c28c31a..9742dd0e4779b 100644 --- a/shell/platform/embedder/embedder_surface_gl.cc +++ b/shell/platform/embedder/embedder_surface_gl.cc @@ -29,7 +29,11 @@ EmbedderSurfaceGL::EmbedderSurfaceGL( valid_ = true; } -EmbedderSurfaceGL::~EmbedderSurfaceGL() = default; +EmbedderSurfaceGL::~EmbedderSurfaceGL() { + if (main_context_ != nullptr) { + main_context_->releaseResourcesAndAbandonContext(); + } +} // |EmbedderSurface| bool EmbedderSurfaceGL::IsValid() const { From 691e9437ac11ccde7053d8c6268921c69e724622 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 13 Mar 2023 12:46:31 -0700 Subject: [PATCH 004/147] Split out CreateSurface --- shell/common/platform_view.cc | 16 ++++++++++------ shell/common/platform_view.h | 13 +++++-------- shell/common/shell.cc | 4 +++- shell/common/shell.h | 2 +- shell/common/shell_unittests.cc | 2 +- .../Source/FlutterEnginePlatformViewTest.mm | 2 +- .../framework/Source/FlutterPlatformViewsTest.mm | 2 +- .../Source/accessibility_bridge_test.mm | 2 +- .../embedder/platform_view_embedder_unittests.cc | 2 +- .../fuchsia/flutter/platform_view_unittest.cc | 8 +------- .../tests/flatland_platform_view_unittest.cc | 11 +++-------- 11 files changed, 28 insertions(+), 36 deletions(-) diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index 7cb0b091d5bcd..ee0b2d1850c15 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -56,6 +56,14 @@ void PlatformView::SetViewportMetrics(const ViewportMetrics& metrics) { } void PlatformView::NotifyCreated() { + delegate_.OnPlatformViewCreated(); +} + +void PlatformView::NotifyDestroyed() { + delegate_.OnPlatformViewDestroyed(); +} + +std::unique_ptr PlatformView::CreateSurface() { std::unique_ptr surface; // Threading: We want to use the platform view on the non-platform thread. // Using the weak pointer is illegal. But, we are going to introduce a latch @@ -73,13 +81,9 @@ void PlatformView::NotifyCreated() { latch.Wait(); if (!surface) { FML_LOG(ERROR) << "Failed to create platform view rendering surface"; - return; + return nullptr; } - delegate_.OnPlatformViewCreated(std::move(surface)); -} - -void PlatformView::NotifyDestroyed() { - delegate_.OnPlatformViewDestroyed(); + return surface; } void PlatformView::ScheduleFrame() { diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 9ec848e34568d..35625a1383c8a 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -60,15 +60,10 @@ class PlatformView { public: using KeyDataResponse = std::function; //-------------------------------------------------------------------------- - /// @brief Notifies the delegate that the platform view was created - /// with the given render surface. This surface is platform - /// (iOS, Android) and client-rendering API (OpenGL, Software, - /// Metal, Vulkan) specific. This is usually a sign to the - /// rasterizer to set up and begin rendering to that surface. + /// @brief Notifies the delegate that the platform view was created. + /// This is usually a sign to the rasterizer to set up. /// - /// @param[in] surface The surface - /// - virtual void OnPlatformViewCreated(std::unique_ptr surface) = 0; + virtual void OnPlatformViewCreated() = 0; //-------------------------------------------------------------------------- /// @brief Notifies the delegate that the platform view was destroyed. @@ -498,6 +493,8 @@ class PlatformView { /// virtual void NotifyDestroyed(); + std::unique_ptr CreateSurface(); + //---------------------------------------------------------------------------- /// @brief Used by embedders to schedule a frame. In response to this /// call, the framework may need to start generating a new frame. diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 32a777d3583ba..86fe018df6a31 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -745,7 +745,7 @@ DartVM* Shell::GetDartVM() { } // |PlatformView::Delegate| -void Shell::OnPlatformViewCreated(std::unique_ptr surface) { +void Shell::OnPlatformViewCreated() { TRACE_EVENT0("flutter", "Shell::OnPlatformViewCreated"); FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); @@ -774,6 +774,8 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { const bool should_post_raster_task = !task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread(); + std::unique_ptr surface = platform_view_->CreateSurface(); + fml::AutoResetWaitableEvent latch; auto raster_task = fml::MakeCopyable([&waiting_for_first_frame = waiting_for_first_frame_, diff --git a/shell/common/shell.h b/shell/common/shell.h index 2ecf71dac6559..6a1c9852e233b 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -524,7 +524,7 @@ class Shell final : public PlatformView::Delegate, void ReportTimings(); // |PlatformView::Delegate| - void OnPlatformViewCreated(std::unique_ptr surface) override; + void OnPlatformViewCreated() override; // |PlatformView::Delegate| void OnPlatformViewDestroyed() override; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 2d1b4f01f9259..2a6533b5caff5 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -62,7 +62,7 @@ using ::testing::Return; namespace { class MockPlatformViewDelegate : public PlatformView::Delegate { - MOCK_METHOD1(OnPlatformViewCreated, void(std::unique_ptr surface)); + MOCK_METHOD0(OnPlatformViewCreated, void()); MOCK_METHOD0(OnPlatformViewDestroyed, void()); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm index e34a4ffc05af4..61c3fdcb6ef52 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm @@ -20,7 +20,7 @@ class FakeDelegate : public PlatformView::Delegate { public: - void OnPlatformViewCreated(std::unique_ptr surface) override {} + void OnPlatformViewCreated() override {} void OnPlatformViewDestroyed() override {} void OnPlatformViewScheduleFrame() override {} void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override {} diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index ebae0c59d2e9c..729bdae523d4a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -89,7 +89,7 @@ @implementation FlutterPlatformViewsTestMockFlutterPlatformFactory namespace flutter { namespace { class FlutterPlatformViewsTestMockPlatformViewDelegate : public PlatformView::Delegate { - void OnPlatformViewCreated(std::unique_ptr surface) override {} + void OnPlatformViewCreated() override {} void OnPlatformViewDestroyed() override {} void OnPlatformViewScheduleFrame() override {} void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override {} diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm index 67dc27901fe16..4f90606a9cfd4 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm @@ -73,7 +73,7 @@ @implementation MockFlutterPlatformFactory namespace flutter { namespace { class MockDelegate : public PlatformView::Delegate { - void OnPlatformViewCreated(std::unique_ptr surface) override {} + void OnPlatformViewCreated() override {} void OnPlatformViewDestroyed() override {} void OnPlatformViewScheduleFrame() override {} void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override {} diff --git a/shell/platform/embedder/platform_view_embedder_unittests.cc b/shell/platform/embedder/platform_view_embedder_unittests.cc index 3e795f1fb530f..d98dcaa195555 100644 --- a/shell/platform/embedder/platform_view_embedder_unittests.cc +++ b/shell/platform/embedder/platform_view_embedder_unittests.cc @@ -16,7 +16,7 @@ namespace flutter { namespace testing { namespace { class MockDelegate : public PlatformView::Delegate { - MOCK_METHOD1(OnPlatformViewCreated, void(std::unique_ptr)); + MOCK_METHOD0(OnPlatformViewCreated, void()); MOCK_METHOD0(OnPlatformViewDestroyed, void()); MOCK_METHOD0(OnPlatformViewScheduleFrame, void()); MOCK_METHOD1(OnPlatformViewSetNextFrameCallback, diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index 624ad33b51699..032a7544ba2e9 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -76,11 +76,7 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { } // |flutter::PlatformView::Delegate| - void OnPlatformViewCreated(std::unique_ptr surface) { - ASSERT_EQ(surface_.get(), nullptr); - - surface_ = std::move(surface); - } + void OnPlatformViewCreated() {} // |flutter::PlatformView::Delegate| void OnPlatformViewDestroyed() {} // |flutter::PlatformView::Delegate| @@ -144,7 +140,6 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { std::unique_ptr updated_asset_resolver, flutter::AssetResolver::AssetResolverType type) {} - flutter::Surface* surface() const { return surface_.get(); } flutter::PlatformMessage* message() const { return message_.get(); } const flutter::ViewportMetrics& metrics() const { return metrics_; } int32_t semantics_features() const { return semantics_features_; } @@ -161,7 +156,6 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { } private: - std::unique_ptr surface_; std::unique_ptr message_; flutter::ViewportMetrics metrics_; std::vector> pointer_packets_; diff --git a/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc b/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc index c1ffbcd96ab66..262ab27af1700 100644 --- a/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc @@ -75,11 +75,7 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { } // |flutter::PlatformView::Delegate| - void OnPlatformViewCreated(std::unique_ptr surface) { - ASSERT_EQ(surface_.get(), nullptr); - - surface_ = std::move(surface); - } + void OnPlatformViewCreated() {} // |flutter::PlatformView::Delegate| void OnPlatformViewDestroyed() {} // |flutter::PlatformView::Delegate| @@ -147,7 +143,6 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { std::unique_ptr updated_asset_resolver, flutter::AssetResolver::AssetResolverType type) {} - flutter::Surface* surface() const { return surface_.get(); } flutter::PlatformMessage* message() const { return message_.get(); } const flutter::ViewportMetrics& metrics() const { return metrics_; } int32_t semantics_features() const { return semantics_features_; } @@ -164,7 +159,6 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { } private: - std::unique_ptr surface_; std::unique_ptr message_; flutter::ViewportMetrics metrics_; std::vector> pointer_packets_; @@ -630,7 +624,8 @@ TEST_F(FlatlandPlatformViewTests, CreateSurfaceTest) { RunLoopUntilIdle(); - EXPECT_EQ(gr_context.get(), delegate.surface()->GetContext()); + // TODO(dkwingsmt) + // EXPECT_EQ(gr_context.get(), delegate.surface()->GetContext()); EXPECT_EQ(external_view_embedder.get(), platform_view.CreateExternalViewEmbedder().get()); } From 7d95498ddaadee9fdd4239759861dc977ecbe782 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 13 Mar 2023 12:59:21 -0700 Subject: [PATCH 005/147] Fix fuchsia test --- shell/platform/fuchsia/flutter/platform_view_unittest.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index 032a7544ba2e9..64902116300ac 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -506,7 +506,8 @@ TEST_F(PlatformViewTests, CreateSurfaceTest) { RunLoopUntilIdle(); - EXPECT_EQ(gr_context.get(), delegate.surface()->GetContext()); + // TODO(dkwingsmt) + // EXPECT_EQ(gr_context.get(), delegate.surface()->GetContext()); EXPECT_EQ(external_view_embedder.get(), platform_view.CreateExternalViewEmbedder().get()); } From 3a3ad87a999eea483423b34c43dc23fbe3e3c9ca Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 14 Mar 2023 00:48:41 -0700 Subject: [PATCH 006/147] Fix windows test --- shell/common/shell.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 86fe018df6a31..3c468d6195cb9 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -750,6 +750,13 @@ void Shell::OnPlatformViewCreated() { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + std::unique_ptr surface = platform_view_->CreateSurface(); + if (surface == nullptr) { + // TODO(dkwingsmt): This case is observed in windows unit tests. Anyway, + // we're probably not creating the surface in this callback eventually. + return; + } + // Prevent any request to change the thread configuration for raster and // platform queues while the platform view is being created. // From e3ce91751e13afa07fb1dc25867c7580f7795120 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 14 Mar 2023 10:45:36 -0700 Subject: [PATCH 007/147] Update shell.cc --- shell/common/shell.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 3c468d6195cb9..01c8d123d6c9f 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -781,8 +781,6 @@ void Shell::OnPlatformViewCreated() { const bool should_post_raster_task = !task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread(); - std::unique_ptr surface = platform_view_->CreateSurface(); - fml::AutoResetWaitableEvent latch; auto raster_task = fml::MakeCopyable([&waiting_for_first_frame = waiting_for_first_frame_, From caac3d9723216f22d453fdf0458296638f9d08e9 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 14 Mar 2023 16:42:31 -0700 Subject: [PATCH 008/147] Remove unused CreateSnapshotSurface --- shell/common/rasterizer_unittests.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/shell/common/rasterizer_unittests.cc b/shell/common/rasterizer_unittests.cc index 6ab77b53401ba..a9ebaff8f2f0c 100644 --- a/shell/common/rasterizer_unittests.cc +++ b/shell/common/rasterizer_unittests.cc @@ -37,7 +37,6 @@ class MockDelegate : public Rasterizer::Delegate { const fml::RefPtr()); MOCK_CONST_METHOD0(GetIsGpuDisabledSyncSwitch, std::shared_ptr()); - MOCK_METHOD0(CreateSnapshotSurface, std::unique_ptr()); MOCK_CONST_METHOD0(GetSettings, const Settings&()); }; From 98d358796c52818680da3c5bfbf971fea78fef23 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 15 Mar 2023 16:50:43 -0700 Subject: [PATCH 009/147] Extract studio and platformview's ctor --- shell/common/platform_view.cc | 6 +- shell/common/platform_view.h | 2 +- shell/platform/embedder/embedder.cc | 117 ++++++++++-------- shell/platform/embedder/embedder_studio.h | 34 +++++ .../embedder/platform_view_embedder.cc | 59 +-------- .../embedder/platform_view_embedder.h | 11 +- 6 files changed, 116 insertions(+), 113 deletions(-) create mode 100644 shell/platform/embedder/embedder_studio.h diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index ee0b2d1850c15..4e6df69e823d2 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -63,6 +63,8 @@ void PlatformView::NotifyDestroyed() { delegate_.OnPlatformViewDestroyed(); } +constexpr int64_t kFlutterDefaultViewId = 0; + std::unique_ptr PlatformView::CreateSurface() { std::unique_ptr surface; // Threading: We want to use the platform view on the non-platform thread. @@ -72,7 +74,7 @@ std::unique_ptr PlatformView::CreateSurface() { fml::ManualResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask( task_runners_.GetRasterTaskRunner(), [platform_view, &surface, &latch]() { - surface = platform_view->CreateRenderingSurface(); + surface = platform_view->CreateRenderingSurface(kFlutterDefaultViewId); if (surface && !surface->IsValid()) { surface.reset(); } @@ -138,7 +140,7 @@ void PlatformView::MarkTextureFrameAvailable(int64_t texture_id) { delegate_.OnPlatformViewMarkTextureFrameAvailable(texture_id); } -std::unique_ptr PlatformView::CreateRenderingSurface() { +std::unique_ptr PlatformView::CreateRenderingSurface(int64_t view_id) { // We have a default implementation because tests create a platform view but // never a rendering surface. FML_DCHECK(false) << "This platform does not provide a rendering surface but " diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 35625a1383c8a..cd8c15a838162 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -830,7 +830,7 @@ class PlatformView { protected: // This is the only method called on the raster task runner. - virtual std::unique_ptr CreateRenderingSurface(); + virtual std::unique_ptr CreateRenderingSurface(int64_t view_id); PlatformView::Delegate& delegate_; const TaskRunners task_runners_; diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 39815333f7ba0..4ff2fbe2fa24f 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -435,6 +435,14 @@ InferOpenGLPlatformViewCreationCallback( shell.GetTaskRunners(), // task runners gl_dispatch_table, // embedder GL dispatch table fbo_reset_after_present, // fbo reset after present + std::make_unique( + [gl_dispatch_table, fbo_reset_after_present]( + std::shared_ptr + external_view_embedder) { + return std::make_unique( + gl_dispatch_table, fbo_reset_after_present, + external_view_embedder); + }), platform_dispatch_table, // embedder platform dispatch table std::move(external_view_embedder) // external view embedder ); @@ -490,29 +498,29 @@ InferMetalPlatformViewCreationCallback( .get_texture = metal_get_texture, }; - std::shared_ptr view_embedder = - std::move(external_view_embedder); - - std::unique_ptr embedder_surface = - std::make_unique( - const_cast(config->metal.device), - const_cast( - config->metal.present_command_queue), - metal_dispatch_table, view_embedder); - // The static leak checker gets confused by the use of fml::MakeCopyable. // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) - return fml::MakeCopyable( - [embedder_surface = std::move(embedder_surface), platform_dispatch_table, - external_view_embedder = view_embedder](flutter::Shell& shell) mutable { - return std::make_unique( - shell, // delegate - shell.GetTaskRunners(), // task runners - std::move(embedder_surface), // embedder surface - platform_dispatch_table, // platform dispatch table - std::move(external_view_embedder) // external view embedder - ); - }); + return fml::MakeCopyable([embedder_surface = std::move(embedder_surface), + platform_dispatch_table, + external_view_embedder = + view_embedder](flutter::Shell& shell) mutable { + return std::make_unique( + shell, // delegate + shell.GetTaskRunners(), // task runners + std::make_unique( + [config, + metal_dispatch_table](std::shared_ptr + external_view_embedder) { + return std::make_unique( + const_cast(config->metal.device), + const_cast( + config->metal.present_command_queue), + metal_dispatch_table, external_view_embedder); + }), + platform_dispatch_table, // platform dispatch table + std::move(external_view_embedder) // external view embedder + ); + }); #else return nullptr; #endif @@ -572,34 +580,33 @@ InferVulkanPlatformViewCreationCallback( .present_image = vulkan_present_image_callback, }; - std::shared_ptr view_embedder = - std::move(external_view_embedder); - - std::unique_ptr embedder_surface = - std::make_unique( - config->vulkan.version, vk_instance, - config->vulkan.enabled_instance_extension_count, - config->vulkan.enabled_instance_extensions, - config->vulkan.enabled_device_extension_count, - config->vulkan.enabled_device_extensions, - static_cast(config->vulkan.physical_device), - static_cast(config->vulkan.device), - config->vulkan.queue_family_index, - static_cast(config->vulkan.queue), vulkan_dispatch_table, - view_embedder); - - return fml::MakeCopyable( - [embedder_surface = std::move(embedder_surface), platform_dispatch_table, - external_view_embedder = - std::move(view_embedder)](flutter::Shell& shell) mutable { - return std::make_unique( - shell, // delegate - shell.GetTaskRunners(), // task runners - std::move(embedder_surface), // embedder surface - platform_dispatch_table, // platform dispatch table - std::move(external_view_embedder) // external view embedder - ); - }); + return fml::MakeCopyable([embedder_surface = std::move(embedder_surface), + platform_dispatch_table, + external_view_embedder = std::move(view_embedder)]( + flutter::Shell& shell) mutable { + return std::make_unique( + shell, // delegate + shell.GetTaskRunners(), // task runners + std::make_unique( + [config, vulkan_dispatch_table]( + std::shared_ptr + external_view_embedder) { + return std::make_unique( + config->vulkan.version, vk_instance, + config->vulkan.enabled_instance_extension_count, + config->vulkan.enabled_instance_extensions, + config->vulkan.enabled_device_extension_count, + config->vulkan.enabled_device_extensions, + static_cast(config->vulkan.physical_device), + static_cast(config->vulkan.device), + config->vulkan.queue_family_index, + static_cast(config->vulkan.queue), + vulkan_dispatch_table, external_view_embedder); + }), + platform_dispatch_table, // platform dispatch table + std::move(external_view_embedder) // external view embedder + ); + }); #else return nullptr; #endif @@ -633,9 +640,15 @@ InferSoftwarePlatformViewCreationCallback( external_view_embedder = std::move(external_view_embedder)](flutter::Shell& shell) mutable { return std::make_unique( - shell, // delegate - shell.GetTaskRunners(), // task runners - software_dispatch_table, // software dispatch table + shell, // delegate + shell.GetTaskRunners(), // task runners + std::make_unique( + [software_dispatch_table]( + std::shared_ptr + external_view_embedder) { + return std::make_unique( + software_dispatch_table, external_view_embedder); + }), platform_dispatch_table, // platform dispatch table std::move(external_view_embedder) // external view embedder ); diff --git a/shell/platform/embedder/embedder_studio.h b/shell/platform/embedder/embedder_studio.h new file mode 100644 index 0000000000000..01e51ccd3e40d --- /dev/null +++ b/shell/platform/embedder/embedder_studio.h @@ -0,0 +1,34 @@ +// 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_PLATFORM_EMBEDDER_EMBEDDER_STUDIO_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_STUDIO_H_ + +#include + +#include "flutter/flow/external_view_embedder.h" +#include "flutter/shell/platform/embedder/embedder_surface.h" + +namespace flutter { + +class EmbedderStudio { + public: + using Builder = std::function( + std::shared_ptr external_view_embedder)>; + + EmbedderStudio(Builder builder) : builder_(std::move(builder)) {} + + virtual ~EmbedderStudio() = default; + + virtual std::unique_ptr CreateSurface(std::shared_ptr external_view_embedder) { + return builder_(external_view_embedder); + } + + private: + Builder builder_; +} + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_STUDIO_H_ diff --git a/shell/platform/embedder/platform_view_embedder.cc b/shell/platform/embedder/platform_view_embedder.cc index 1cedc22810a50..3b712e4073d4e 100644 --- a/shell/platform/embedder/platform_view_embedder.cc +++ b/shell/platform/embedder/platform_view_embedder.cc @@ -48,72 +48,17 @@ class PlatformViewEmbedder::EmbedderPlatformMessageHandler PlatformViewEmbedder::PlatformViewEmbedder( PlatformView::Delegate& delegate, const flutter::TaskRunners& task_runners, - const EmbedderSurfaceSoftware::SoftwareDispatchTable& - software_dispatch_table, + std::unique_ptr embedder_studio, PlatformDispatchTable platform_dispatch_table, std::shared_ptr external_view_embedder) : PlatformView(delegate, task_runners), external_view_embedder_(std::move(external_view_embedder)), - embedder_surface_( - std::make_unique(software_dispatch_table, - external_view_embedder_)), + embedder_studio_(std::move(embedder_studio_)), platform_message_handler_(new EmbedderPlatformMessageHandler( GetWeakPtr(), task_runners.GetPlatformTaskRunner())), platform_dispatch_table_(std::move(platform_dispatch_table)) {} -#ifdef SHELL_ENABLE_GL -PlatformViewEmbedder::PlatformViewEmbedder( - PlatformView::Delegate& delegate, - const flutter::TaskRunners& task_runners, - const EmbedderSurfaceGL::GLDispatchTable& gl_dispatch_table, - bool fbo_reset_after_present, - PlatformDispatchTable platform_dispatch_table, - std::shared_ptr external_view_embedder) - : PlatformView(delegate, task_runners), - external_view_embedder_(std::move(external_view_embedder)), - embedder_surface_( - std::make_unique(gl_dispatch_table, - fbo_reset_after_present, - external_view_embedder_)), - platform_message_handler_(new EmbedderPlatformMessageHandler( - GetWeakPtr(), - task_runners.GetPlatformTaskRunner())), - platform_dispatch_table_(std::move(platform_dispatch_table)) {} -#endif - -#ifdef SHELL_ENABLE_METAL -PlatformViewEmbedder::PlatformViewEmbedder( - PlatformView::Delegate& delegate, - const flutter::TaskRunners& task_runners, - std::unique_ptr embedder_surface, - PlatformDispatchTable platform_dispatch_table, - std::shared_ptr external_view_embedder) - : PlatformView(delegate, task_runners), - external_view_embedder_(std::move(external_view_embedder)), - embedder_surface_(std::move(embedder_surface)), - platform_message_handler_(new EmbedderPlatformMessageHandler( - GetWeakPtr(), - task_runners.GetPlatformTaskRunner())), - platform_dispatch_table_(std::move(platform_dispatch_table)) {} -#endif - -#ifdef SHELL_ENABLE_VULKAN -PlatformViewEmbedder::PlatformViewEmbedder( - PlatformView::Delegate& delegate, - const flutter::TaskRunners& task_runners, - std::unique_ptr embedder_surface, - PlatformDispatchTable platform_dispatch_table, - std::shared_ptr external_view_embedder) - : PlatformView(delegate, task_runners), - external_view_embedder_(std::move(external_view_embedder)), - embedder_surface_(std::move(embedder_surface)), - platform_message_handler_(new EmbedderPlatformMessageHandler( - GetWeakPtr(), - task_runners.GetPlatformTaskRunner())), - platform_dispatch_table_(std::move(platform_dispatch_table)) {} -#endif - PlatformViewEmbedder::~PlatformViewEmbedder() = default; void PlatformViewEmbedder::UpdateSemantics( diff --git a/shell/platform/embedder/platform_view_embedder.h b/shell/platform/embedder/platform_view_embedder.h index 20f4dc139875b..f4ae8f0406b19 100644 --- a/shell/platform/embedder/platform_view_embedder.h +++ b/shell/platform/embedder/platform_view_embedder.h @@ -6,6 +6,7 @@ #define FLUTTER_SHELL_PLATFORM_EMBEDDER_PLATFORM_VIEW_EMBEDDER_H_ #include +#include #include "flow/embedded_views.h" #include "flutter/fml/macros.h" @@ -51,6 +52,12 @@ class PlatformViewEmbedder final : public PlatformView { OnPreEngineRestartCallback on_pre_engine_restart_callback; // optional }; + PlatformViewEmbedder( + PlatformView::Delegate& delegate, + const flutter::TaskRunners& task_runners, + std::unique_ptr embedder_studio, + std::shared_ptr external_view_embedder); + // Create a platform view that sets up a software rasterizer. PlatformViewEmbedder( PlatformView::Delegate& delegate, @@ -108,7 +115,9 @@ class PlatformViewEmbedder final : public PlatformView { private: class EmbedderPlatformMessageHandler; std::shared_ptr external_view_embedder_; - std::unique_ptr embedder_surface_; + std::unique_ptr embedder_studio_; + std::unordered_map> + embedder_surfaces_; std::shared_ptr platform_message_handler_; PlatformDispatchTable platform_dispatch_table_; From f27fe7057e180261f4a61685edf6835d743e271f Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 16 Mar 2023 15:19:43 -0700 Subject: [PATCH 010/147] Create studio, compilable --- shell/common/shell_test_platform_view_gl.cc | 3 +- shell/common/shell_test_platform_view_gl.h | 2 +- shell/common/shell_test_platform_view_metal.h | 2 +- .../common/shell_test_platform_view_metal.mm | 2 +- .../common/shell_test_platform_view_vulkan.cc | 3 +- .../common/shell_test_platform_view_vulkan.h | 2 +- shell/common/shell_unittests.cc | 18 +-- .../platform/android/platform_view_android.cc | 3 +- .../platform/android/platform_view_android.h | 2 +- shell/platform/darwin/ios/platform_view_ios.h | 2 +- .../platform/darwin/ios/platform_view_ios.mm | 2 +- shell/platform/embedder/embedder.cc | 143 +++++++++--------- shell/platform/embedder/embedder_studio.h | 12 +- .../embedder/platform_view_embedder.cc | 20 +-- .../embedder/platform_view_embedder.h | 42 +---- .../platform_view_embedder_unittests.cc | 57 ++++--- .../platform/fuchsia/flutter/platform_view.cc | 3 +- .../platform/fuchsia/flutter/platform_view.h | 3 +- shell/testing/tester_main.cc | 2 +- 19 files changed, 156 insertions(+), 167 deletions(-) diff --git a/shell/common/shell_test_platform_view_gl.cc b/shell/common/shell_test_platform_view_gl.cc index 3173e2946b83c..170b285604c5b 100644 --- a/shell/common/shell_test_platform_view_gl.cc +++ b/shell/common/shell_test_platform_view_gl.cc @@ -37,7 +37,8 @@ void ShellTestPlatformViewGL::SimulateVSync() { } // |PlatformView| -std::unique_ptr ShellTestPlatformViewGL::CreateRenderingSurface() { +std::unique_ptr ShellTestPlatformViewGL::CreateRenderingSurface( + int64_t view_id) { return std::make_unique( GPUSurfaceGLSkia::MakeGLContext(this), this, true); } diff --git a/shell/common/shell_test_platform_view_gl.h b/shell/common/shell_test_platform_view_gl.h index 4dca801da3c34..e403e380a5d24 100644 --- a/shell/common/shell_test_platform_view_gl.h +++ b/shell/common/shell_test_platform_view_gl.h @@ -40,7 +40,7 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView, shell_test_external_view_embedder_; // |PlatformView| - std::unique_ptr CreateRenderingSurface() override; + std::unique_ptr CreateRenderingSurface(int64_t view_id) override; // |PlatformView| std::shared_ptr CreateExternalViewEmbedder() override; diff --git a/shell/common/shell_test_platform_view_metal.h b/shell/common/shell_test_platform_view_metal.h index be9dc9ebd4de2..84f8e07f92240 100644 --- a/shell/common/shell_test_platform_view_metal.h +++ b/shell/common/shell_test_platform_view_metal.h @@ -47,7 +47,7 @@ class ShellTestPlatformViewMetal final : public ShellTestPlatformView, PointerDataDispatcherMaker GetDispatcherMaker() override; // |PlatformView| - std::unique_ptr CreateRenderingSurface() override; + std::unique_ptr CreateRenderingSurface(int64_t view_id) override; // |GPUSurfaceMetalDelegate| GPUCAMetalLayerHandle GetCAMetalLayer( diff --git a/shell/common/shell_test_platform_view_metal.mm b/shell/common/shell_test_platform_view_metal.mm index 814e67e6f3933..77c54ce4e4c2f 100644 --- a/shell/common/shell_test_platform_view_metal.mm +++ b/shell/common/shell_test_platform_view_metal.mm @@ -92,7 +92,7 @@ GPUMTLTextureInfo offscreen_texture_info() const { } // |PlatformView| -std::unique_ptr ShellTestPlatformViewMetal::CreateRenderingSurface() { +std::unique_ptr ShellTestPlatformViewMetal::CreateRenderingSurface(int64_t view_id) { return std::make_unique(this, [metal_context_->context() mainContext], MsaaSampleCount::kNone); } diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index 210bd0837a6a1..ce40fcb496c1f 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -52,7 +52,8 @@ void ShellTestPlatformViewVulkan::SimulateVSync() { } // |PlatformView| -std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingSurface() { +std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingSurface( + int64_t view_id) { return std::make_unique(proc_table_, shell_test_external_view_embedder_); } diff --git a/shell/common/shell_test_platform_view_vulkan.h b/shell/common/shell_test_platform_view_vulkan.h index 718f7fe6dec06..323112ceba3ad 100644 --- a/shell/common/shell_test_platform_view_vulkan.h +++ b/shell/common/shell_test_platform_view_vulkan.h @@ -74,7 +74,7 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { shell_test_external_view_embedder_; // |PlatformView| - std::unique_ptr CreateRenderingSurface() override; + std::unique_ptr CreateRenderingSurface(int64_t view_id) override; // |PlatformView| std::shared_ptr CreateExternalViewEmbedder() override; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 2a6533b5caff5..07d6d500b3d7b 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -138,7 +138,7 @@ class MockPlatformView : public PlatformView { MockPlatformView(MockPlatformViewDelegate& delegate, const TaskRunners& task_runners) : PlatformView(delegate, task_runners) {} - MOCK_METHOD0(CreateRenderingSurface, std::unique_ptr()); + MOCK_METHOD1(CreateRenderingSurface, std::unique_ptr(int64_t)); MOCK_CONST_METHOD0(GetPlatformMessageHandler, std::shared_ptr()); }; @@ -147,7 +147,7 @@ class TestPlatformView : public PlatformView { public: TestPlatformView(Shell& shell, const TaskRunners& task_runners) : PlatformView(shell, task_runners) {} - MOCK_METHOD0(CreateRenderingSurface, std::unique_ptr()); + MOCK_METHOD1(CreateRenderingSurface, std::unique_ptr(int64_t)); }; class MockPlatformMessageHandler : public PlatformMessageHandler { @@ -1596,7 +1596,7 @@ TEST_F(ShellTest, MultipleFluttersSetResourceCacheBytes) { Shell::CreateCallback platform_view_create_callback = [task_runners, main_context](flutter::Shell& shell) { auto result = std::make_unique(shell, task_runners); - ON_CALL(*result, CreateRenderingSurface()) + ON_CALL(*result, CreateRenderingSurface(0ll)) .WillByDefault(::testing::Invoke([main_context] { auto surface = std::make_unique(); ON_CALL(*surface, GetContext()) @@ -3050,7 +3050,7 @@ TEST_F(ShellTest, Spawn) { [&platform_view_delegate](Shell& shell) { auto result = std::make_unique( platform_view_delegate, shell.GetTaskRunners()); - ON_CALL(*result, CreateRenderingSurface()) + ON_CALL(*result, CreateRenderingSurface(0ll)) .WillByDefault(::testing::Invoke( [] { return std::make_unique(); })); return result; @@ -3162,7 +3162,7 @@ TEST_F(ShellTest, SpawnWithDartEntrypointArgs) { [&platform_view_delegate](Shell& shell) { auto result = std::make_unique( platform_view_delegate, shell.GetTaskRunners()); - ON_CALL(*result, CreateRenderingSurface()) + ON_CALL(*result, CreateRenderingSurface(0ll)) .WillByDefault(::testing::Invoke( [] { return std::make_unique(); })); return result; @@ -3225,7 +3225,7 @@ TEST_F(ShellTest, IOManagerIsSharedBetweenParentAndSpawnedShell) { [&platform_view_delegate](Shell& shell) { auto result = std::make_unique( platform_view_delegate, shell.GetTaskRunners()); - ON_CALL(*result, CreateRenderingSurface()) + ON_CALL(*result, CreateRenderingSurface(0ll)) .WillByDefault(::testing::Invoke( [] { return std::make_unique(); })); return result; @@ -3279,7 +3279,7 @@ TEST_F(ShellTest, IOManagerInSpawnedShellIsNotNullAfterParentShellDestroyed) { [&platform_view_delegate](Shell& shell) { auto result = std::make_unique( platform_view_delegate, shell.GetTaskRunners()); - ON_CALL(*result, CreateRenderingSurface()) + ON_CALL(*result, CreateRenderingSurface(0ll)) .WillByDefault(::testing::Invoke( [] { return std::make_unique(); })); return result; @@ -3327,7 +3327,7 @@ TEST_F(ShellTest, ImageGeneratorRegistryNotNullAfterParentShellDestroyed) { [&platform_view_delegate](Shell& shell) { auto result = std::make_unique( platform_view_delegate, shell.GetTaskRunners()); - ON_CALL(*result, CreateRenderingSurface()) + ON_CALL(*result, CreateRenderingSurface(0ll)) .WillByDefault(::testing::Invoke( [] { return std::make_unique(); })); return result; @@ -3825,7 +3825,7 @@ TEST_F(ShellTest, SpawnWorksWithOnError) { auto result = std::make_unique<::testing::NiceMock>( platform_view_delegate, shell.GetTaskRunners()); - ON_CALL(*result, CreateRenderingSurface()) + ON_CALL(*result, CreateRenderingSurface(0ll)) .WillByDefault(::testing::Invoke([] { return std::make_unique<::testing::NiceMock>(); })); diff --git a/shell/platform/android/platform_view_android.cc b/shell/platform/android/platform_view_android.cc index 1b9d440a2d4bd..4ba82e801df6d 100644 --- a/shell/platform/android/platform_view_android.cc +++ b/shell/platform/android/platform_view_android.cc @@ -281,7 +281,8 @@ std::unique_ptr PlatformViewAndroid::CreateVSyncWaiter() { } // |PlatformView| -std::unique_ptr PlatformViewAndroid::CreateRenderingSurface() { +std::unique_ptr PlatformViewAndroid::CreateRenderingSurface( + int64_t view_id) { if (!android_surface_) { return nullptr; } diff --git a/shell/platform/android/platform_view_android.h b/shell/platform/android/platform_view_android.h index 536cb1d20f722..1bcd8a6482631 100644 --- a/shell/platform/android/platform_view_android.h +++ b/shell/platform/android/platform_view_android.h @@ -143,7 +143,7 @@ class PlatformViewAndroid final : public PlatformView { std::unique_ptr CreateVSyncWaiter() override; // |PlatformView| - std::unique_ptr CreateRenderingSurface() override; + std::unique_ptr CreateRenderingSurface(int64_t view_id) override; // |PlatformView| std::shared_ptr CreateExternalViewEmbedder() override; diff --git a/shell/platform/darwin/ios/platform_view_ios.h b/shell/platform/darwin/ios/platform_view_ios.h index e9129350a4277..ba036bd6aa341 100644 --- a/shell/platform/darwin/ios/platform_view_ios.h +++ b/shell/platform/darwin/ios/platform_view_ios.h @@ -149,7 +149,7 @@ class PlatformViewIOS final : public PlatformView { void HandlePlatformMessage(std::unique_ptr message) override; // |PlatformView| - std::unique_ptr CreateRenderingSurface() override; + std::unique_ptr CreateRenderingSurface(int64_t view_id) override; // |PlatformView| std::shared_ptr CreateExternalViewEmbedder() override; diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index 219dab4ec9c98..851e5ecdbad00 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -138,7 +138,7 @@ new PlatformMessageHandlerIos(task_runners.GetPlatformTaskRunner())) {} } // |PlatformView| -std::unique_ptr PlatformViewIOS::CreateRenderingSurface() { +std::unique_ptr PlatformViewIOS::CreateRenderingSurface(int64_t view_id) { FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); std::lock_guard guard(ios_surface_mutex_); if (!ios_surface_) { diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 4ff2fbe2fa24f..9b7e895eb02db 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -426,23 +426,22 @@ InferOpenGLPlatformViewCreationCallback( gl_populate_existing_damage, // gl_populate_existing_damage }; + auto surface_builder = + [gl_dispatch_table, fbo_reset_after_present]( + std::shared_ptr + external_view_embedder) { + return std::make_unique( + gl_dispatch_table, fbo_reset_after_present, external_view_embedder); + }; + return fml::MakeCopyable( - [gl_dispatch_table, fbo_reset_after_present, platform_dispatch_table, + [surface_builder, platform_dispatch_table, external_view_embedder = std::move(external_view_embedder)](flutter::Shell& shell) mutable { return std::make_unique( - shell, // delegate - shell.GetTaskRunners(), // task runners - gl_dispatch_table, // embedder GL dispatch table - fbo_reset_after_present, // fbo reset after present - std::make_unique( - [gl_dispatch_table, fbo_reset_after_present]( - std::shared_ptr - external_view_embedder) { - return std::make_unique( - gl_dispatch_table, fbo_reset_after_present, - external_view_embedder); - }), + shell, // delegate + shell.GetTaskRunners(), // task runners + std::make_unique(surface_builder), platform_dispatch_table, // embedder platform dispatch table std::move(external_view_embedder) // external view embedder ); @@ -498,29 +497,31 @@ InferMetalPlatformViewCreationCallback( .get_texture = metal_get_texture, }; + auto surface_builder = + [config, metal_dispatch_table]( + std::shared_ptr + external_view_embedder) { + return std::make_unique( + const_cast(config->metal.device), + const_cast( + config->metal.present_command_queue), + metal_dispatch_table, external_view_embedder); + }; + // The static leak checker gets confused by the use of fml::MakeCopyable. // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) - return fml::MakeCopyable([embedder_surface = std::move(embedder_surface), - platform_dispatch_table, - external_view_embedder = - view_embedder](flutter::Shell& shell) mutable { - return std::make_unique( - shell, // delegate - shell.GetTaskRunners(), // task runners - std::make_unique( - [config, - metal_dispatch_table](std::shared_ptr - external_view_embedder) { - return std::make_unique( - const_cast(config->metal.device), - const_cast( - config->metal.present_command_queue), - metal_dispatch_table, external_view_embedder); - }), - platform_dispatch_table, // platform dispatch table - std::move(external_view_embedder) // external view embedder - ); - }); + return fml::MakeCopyable( + [surface_builder, platform_dispatch_table, + external_view_embedder = + std::move(external_view_embedder)](flutter::Shell& shell) mutable { + return std::make_unique( + shell, // delegate + shell.GetTaskRunners(), // task runners + std::make_unique(surface_builder), + platform_dispatch_table, // platform dispatch table + std::move(external_view_embedder) // external view embedder + ); + }); #else return nullptr; #endif @@ -580,33 +581,35 @@ InferVulkanPlatformViewCreationCallback( .present_image = vulkan_present_image_callback, }; - return fml::MakeCopyable([embedder_surface = std::move(embedder_surface), - platform_dispatch_table, - external_view_embedder = std::move(view_embedder)]( - flutter::Shell& shell) mutable { - return std::make_unique( - shell, // delegate - shell.GetTaskRunners(), // task runners - std::make_unique( - [config, vulkan_dispatch_table]( - std::shared_ptr - external_view_embedder) { - return std::make_unique( - config->vulkan.version, vk_instance, - config->vulkan.enabled_instance_extension_count, - config->vulkan.enabled_instance_extensions, - config->vulkan.enabled_device_extension_count, - config->vulkan.enabled_device_extensions, - static_cast(config->vulkan.physical_device), - static_cast(config->vulkan.device), - config->vulkan.queue_family_index, - static_cast(config->vulkan.queue), - vulkan_dispatch_table, external_view_embedder); - }), - platform_dispatch_table, // platform dispatch table - std::move(external_view_embedder) // external view embedder - ); - }); + auto surface_builder = + [config, vulkan_dispatch_table, + vk_instance](std::shared_ptr + external_view_embedder) { + return std::make_unique( + config->vulkan.version, vk_instance, + config->vulkan.enabled_instance_extension_count, + config->vulkan.enabled_instance_extensions, + config->vulkan.enabled_device_extension_count, + config->vulkan.enabled_device_extensions, + static_cast(config->vulkan.physical_device), + static_cast(config->vulkan.device), + config->vulkan.queue_family_index, + static_cast(config->vulkan.queue), vulkan_dispatch_table, + external_view_embedder); + }; + + return fml::MakeCopyable( + [surface_builder, platform_dispatch_table, + external_view_embedder = + std::move(external_view_embedder)](flutter::Shell& shell) mutable { + return std::make_unique( + shell, // delegate + shell.GetTaskRunners(), // task runners + std::make_unique(surface_builder), + platform_dispatch_table, // platform dispatch table + std::move(external_view_embedder) // external view embedder + ); + }); #else return nullptr; #endif @@ -635,20 +638,22 @@ InferSoftwarePlatformViewCreationCallback( software_present_backing_store, // required }; + auto surface_builder = + [software_dispatch_table]( + std::shared_ptr + external_view_embedder) { + return std::make_unique( + software_dispatch_table, external_view_embedder); + }; + return fml::MakeCopyable( - [software_dispatch_table, platform_dispatch_table, + [software_dispatch_table, surface_builder, platform_dispatch_table, external_view_embedder = std::move(external_view_embedder)](flutter::Shell& shell) mutable { return std::make_unique( shell, // delegate shell.GetTaskRunners(), // task runners - std::make_unique( - [software_dispatch_table]( - std::shared_ptr - external_view_embedder) { - return std::make_unique( - software_dispatch_table, external_view_embedder); - }), + std::make_unique(surface_builder), platform_dispatch_table, // platform dispatch table std::move(external_view_embedder) // external view embedder ); diff --git a/shell/platform/embedder/embedder_studio.h b/shell/platform/embedder/embedder_studio.h index 01e51ccd3e40d..d8ce7a6dbcbf9 100644 --- a/shell/platform/embedder/embedder_studio.h +++ b/shell/platform/embedder/embedder_studio.h @@ -7,7 +7,7 @@ #include -#include "flutter/flow/external_view_embedder.h" +#include "flutter/shell/platform/embedder/embedder_external_view_embedder.h" #include "flutter/shell/platform/embedder/embedder_surface.h" namespace flutter { @@ -21,13 +21,19 @@ class EmbedderStudio { virtual ~EmbedderStudio() = default; - virtual std::unique_ptr CreateSurface(std::shared_ptr external_view_embedder) { + virtual std::unique_ptr CreateSurface( + std::shared_ptr external_view_embedder) { return builder_(external_view_embedder); } + virtual sk_sp CreateResourceContext() const { + // TODO(dkwingsmt) + return nullptr; + } + private: Builder builder_; -} +}; } // namespace flutter diff --git a/shell/platform/embedder/platform_view_embedder.cc b/shell/platform/embedder/platform_view_embedder.cc index 3b712e4073d4e..b31914ee6743d 100644 --- a/shell/platform/embedder/platform_view_embedder.cc +++ b/shell/platform/embedder/platform_view_embedder.cc @@ -53,7 +53,7 @@ PlatformViewEmbedder::PlatformViewEmbedder( std::shared_ptr external_view_embedder) : PlatformView(delegate, task_runners), external_view_embedder_(std::move(external_view_embedder)), - embedder_studio_(std::move(embedder_studio_)), + embedder_studio_(std::move(embedder_studio)), platform_message_handler_(new EmbedderPlatformMessageHandler( GetWeakPtr(), task_runners.GetPlatformTaskRunner())), @@ -88,12 +88,16 @@ void PlatformViewEmbedder::HandlePlatformMessage( } // |PlatformView| -std::unique_ptr PlatformViewEmbedder::CreateRenderingSurface() { - if (embedder_surface_ == nullptr) { - FML_LOG(ERROR) << "Embedder surface was null."; +std::unique_ptr PlatformViewEmbedder::CreateRenderingSurface( + int64_t view_id) { + auto found_iter = embedder_surfaces_.find(view_id); + if (found_iter != embedder_surfaces_.end()) { + FML_LOG(ERROR) << "Embedder surface " << view_id << " already exists."; return nullptr; } - return embedder_surface_->CreateGPUSurface(); + auto& embedder_surface = embedder_surfaces_[view_id] = + embedder_studio_->CreateSurface(external_view_embedder_); + return embedder_surface->CreateGPUSurface(); } // |PlatformView| @@ -104,11 +108,7 @@ PlatformViewEmbedder::CreateExternalViewEmbedder() { // |PlatformView| sk_sp PlatformViewEmbedder::CreateResourceContext() const { - if (embedder_surface_ == nullptr) { - FML_LOG(ERROR) << "Embedder surface was null."; - return nullptr; - } - return embedder_surface_->CreateResourceContext(); + return embedder_studio_->CreateResourceContext(); } // |PlatformView| diff --git a/shell/platform/embedder/platform_view_embedder.h b/shell/platform/embedder/platform_view_embedder.h index f4ae8f0406b19..d870268cc4e7f 100644 --- a/shell/platform/embedder/platform_view_embedder.h +++ b/shell/platform/embedder/platform_view_embedder.h @@ -12,6 +12,7 @@ #include "flutter/fml/macros.h" #include "flutter/shell/common/platform_view.h" #include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/embedder/embedder_studio.h" #include "flutter/shell/platform/embedder/embedder_surface.h" #include "flutter/shell/platform/embedder/embedder_surface_software.h" #include "flutter/shell/platform/embedder/vsync_waiter_embedder.h" @@ -56,48 +57,9 @@ class PlatformViewEmbedder final : public PlatformView { PlatformView::Delegate& delegate, const flutter::TaskRunners& task_runners, std::unique_ptr embedder_studio, - std::shared_ptr external_view_embedder); - - // Create a platform view that sets up a software rasterizer. - PlatformViewEmbedder( - PlatformView::Delegate& delegate, - const flutter::TaskRunners& task_runners, - const EmbedderSurfaceSoftware::SoftwareDispatchTable& - software_dispatch_table, PlatformDispatchTable platform_dispatch_table, std::shared_ptr external_view_embedder); -#ifdef SHELL_ENABLE_GL - // Creates a platform view that sets up an OpenGL rasterizer. - PlatformViewEmbedder( - PlatformView::Delegate& delegate, - const flutter::TaskRunners& task_runners, - const EmbedderSurfaceGL::GLDispatchTable& gl_dispatch_table, - bool fbo_reset_after_present, - PlatformDispatchTable platform_dispatch_table, - std::shared_ptr external_view_embedder); -#endif - -#ifdef SHELL_ENABLE_METAL - // Creates a platform view that sets up an metal rasterizer. - PlatformViewEmbedder( - PlatformView::Delegate& delegate, - const flutter::TaskRunners& task_runners, - std::unique_ptr embedder_surface, - PlatformDispatchTable platform_dispatch_table, - std::shared_ptr external_view_embedder); -#endif - -#ifdef SHELL_ENABLE_VULKAN - // Creates a platform view that sets up an Vulkan rasterizer. - PlatformViewEmbedder( - PlatformView::Delegate& delegate, - const flutter::TaskRunners& task_runners, - std::unique_ptr embedder_surface, - PlatformDispatchTable platform_dispatch_table, - std::shared_ptr external_view_embedder); -#endif - ~PlatformViewEmbedder() override; // |PlatformView| @@ -122,7 +84,7 @@ class PlatformViewEmbedder final : public PlatformView { PlatformDispatchTable platform_dispatch_table_; // |PlatformView| - std::unique_ptr CreateRenderingSurface() override; + std::unique_ptr CreateRenderingSurface(int64_t view_id) override; // |PlatformView| std::shared_ptr CreateExternalViewEmbedder() override; diff --git a/shell/platform/embedder/platform_view_embedder_unittests.cc b/shell/platform/embedder/platform_view_embedder_unittests.cc index d98dcaa195555..b587cf96104a1 100644 --- a/shell/platform/embedder/platform_view_embedder_unittests.cc +++ b/shell/platform/embedder/platform_view_embedder_unittests.cc @@ -5,6 +5,7 @@ #include "flutter/shell/platform/embedder/platform_view_embedder.h" #include "flutter/shell/common/thread_host.h" +#include "flutter/shell/platform/embedder/embedder_studio.h" #include "flutter/testing/testing.h" #include "gmock/gmock.h" @@ -57,6 +58,17 @@ class MockResponse : public PlatformMessageResponse { MOCK_METHOD1(Complete, void(std::unique_ptr data)); MOCK_METHOD0(CompleteEmpty, void()); }; + +class MockStudio : public EmbedderStudio { + public: + MockStudio() : EmbedderStudio(nullptr) {} + + MOCK_METHOD1(CreateSurface, + std::unique_ptr( + std::shared_ptr + external_view_embedder)); + MOCK_CONST_METHOD0(CreateResourceContext, sk_sp()); +}; } // namespace TEST(PlatformViewEmbedderTest, HasPlatformMessageHandler) { @@ -72,7 +84,7 @@ TEST(PlatformViewEmbedderTest, HasPlatformMessageHandler) { PlatformViewEmbedder::PlatformDispatchTable platform_dispatch_table; std::shared_ptr external_view_embedder; auto embedder = std::make_unique( - delegate, task_runners, software_dispatch_table, + delegate, task_runners, std::make_unique(), platform_dispatch_table, external_view_embedder); ASSERT_TRUE(embedder->GetPlatformMessageHandler()); @@ -91,27 +103,26 @@ TEST(PlatformViewEmbedderTest, Dispatches) { std::unique_ptr embedder; { fml::AutoResetWaitableEvent latch; - task_runners.GetPlatformTaskRunner()->PostTask([&latch, task_runners, - &did_call, &embedder] { - MockDelegate delegate; - EmbedderSurfaceSoftware::SoftwareDispatchTable software_dispatch_table; - PlatformViewEmbedder::PlatformDispatchTable platform_dispatch_table; - platform_dispatch_table.platform_message_response_callback = - [&did_call](std::unique_ptr message) { - did_call = true; - }; - std::shared_ptr external_view_embedder; - embedder = std::make_unique( - delegate, task_runners, software_dispatch_table, - platform_dispatch_table, external_view_embedder); - auto platform_message_handler = embedder->GetPlatformMessageHandler(); - fml::RefPtr response = - fml::MakeRefCounted(); - std::unique_ptr message = - std::make_unique("foo", response); - platform_message_handler->HandlePlatformMessage(std::move(message)); - latch.Signal(); - }); + task_runners.GetPlatformTaskRunner()->PostTask( + [&latch, task_runners, &did_call, &embedder] { + MockDelegate delegate; + PlatformViewEmbedder::PlatformDispatchTable platform_dispatch_table; + platform_dispatch_table.platform_message_response_callback = + [&did_call](std::unique_ptr message) { + did_call = true; + }; + std::shared_ptr external_view_embedder; + embedder = std::make_unique( + delegate, task_runners, std::make_unique(), + platform_dispatch_table, external_view_embedder); + auto platform_message_handler = embedder->GetPlatformMessageHandler(); + fml::RefPtr response = + fml::MakeRefCounted(); + std::unique_ptr message = + std::make_unique("foo", response); + platform_message_handler->HandlePlatformMessage(std::move(message)); + latch.Signal(); + }); latch.Wait(); } { @@ -146,7 +157,7 @@ TEST(PlatformViewEmbedderTest, DeletionDisabledDispatch) { }; std::shared_ptr external_view_embedder; auto embedder = std::make_unique( - delegate, task_runners, software_dispatch_table, + delegate, task_runners, std::make_unique(), platform_dispatch_table, external_view_embedder); auto platform_message_handler = embedder->GetPlatformMessageHandler(); fml::RefPtr response = diff --git a/shell/platform/fuchsia/flutter/platform_view.cc b/shell/platform/fuchsia/flutter/platform_view.cc index 42e4ca01858ca..e0238148c3029 100644 --- a/shell/platform/fuchsia/flutter/platform_view.cc +++ b/shell/platform/fuchsia/flutter/platform_view.cc @@ -315,7 +315,8 @@ std::unique_ptr PlatformView::CreateVSyncWaiter() { } // |flutter::PlatformView| -std::unique_ptr PlatformView::CreateRenderingSurface() { +std::unique_ptr PlatformView::CreateRenderingSurface( + int64_t view_id) { return on_create_surface_callback_ ? on_create_surface_callback_() : nullptr; } diff --git a/shell/platform/fuchsia/flutter/platform_view.h b/shell/platform/fuchsia/flutter/platform_view.h index 7c6aa98108ebe..5ae674f8d8fee 100644 --- a/shell/platform/fuchsia/flutter/platform_view.h +++ b/shell/platform/fuchsia/flutter/platform_view.h @@ -106,7 +106,8 @@ class PlatformView : public flutter::PlatformView { std::unique_ptr CreateVSyncWaiter() override; // |flutter::PlatformView| - std::unique_ptr CreateRenderingSurface() override; + std::unique_ptr CreateRenderingSurface( + int64_t view_id) override; // |flutter::PlatformView| void HandlePlatformMessage( diff --git a/shell/testing/tester_main.cc b/shell/testing/tester_main.cc index 7fb8fa1d523d3..c2e470e66ad64 100644 --- a/shell/testing/tester_main.cc +++ b/shell/testing/tester_main.cc @@ -83,7 +83,7 @@ class TesterPlatformView : public PlatformView, : PlatformView(delegate, task_runners) {} // |PlatformView| - std::unique_ptr CreateRenderingSurface() override { + std::unique_ptr CreateRenderingSurface(int64_t view_id) override { auto surface = std::make_unique( this, true /* render to surface */); FML_DCHECK(surface->IsValid()); From be3a14cadc0bcf21cc04a1230a2a5b729a578128 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 17 Mar 2023 11:33:55 -0700 Subject: [PATCH 011/147] Remove the arg in the callback --- shell/platform/embedder/embedder.cc | 109 +++++++++--------- shell/platform/embedder/embedder_studio.h | 8 +- .../embedder/platform_view_embedder.cc | 2 +- .../platform_view_embedder_unittests.cc | 5 +- 4 files changed, 59 insertions(+), 65 deletions(-) diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 9b7e895eb02db..ce71900724097 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -426,24 +426,24 @@ InferOpenGLPlatformViewCreationCallback( gl_populate_existing_damage, // gl_populate_existing_damage }; - auto surface_builder = - [gl_dispatch_table, fbo_reset_after_present]( - std::shared_ptr - external_view_embedder) { - return std::make_unique( - gl_dispatch_table, fbo_reset_after_present, external_view_embedder); - }; + std::shared_ptr shared_view_embedder = + std::move(external_view_embedder); + + auto surface_builder = [gl_dispatch_table, fbo_reset_after_present, + shared_view_embedder]() { + return std::make_unique( + gl_dispatch_table, fbo_reset_after_present, shared_view_embedder); + }; return fml::MakeCopyable( [surface_builder, platform_dispatch_table, - external_view_embedder = - std::move(external_view_embedder)](flutter::Shell& shell) mutable { + shared_view_embedder](flutter::Shell& shell) mutable { return std::make_unique( shell, // delegate shell.GetTaskRunners(), // task runners std::make_unique(surface_builder), platform_dispatch_table, // embedder platform dispatch table - std::move(external_view_embedder) // external view embedder + shared_view_embedder // external view embedder ); }); #else @@ -497,29 +497,29 @@ InferMetalPlatformViewCreationCallback( .get_texture = metal_get_texture, }; - auto surface_builder = - [config, metal_dispatch_table]( - std::shared_ptr - external_view_embedder) { - return std::make_unique( - const_cast(config->metal.device), - const_cast( - config->metal.present_command_queue), - metal_dispatch_table, external_view_embedder); - }; + std::shared_ptr shared_view_embedder = + std::move(external_view_embedder); + + auto surface_builder = [config, metal_dispatch_table, + shared_view_embedder]() { + return std::make_unique( + const_cast(config->metal.device), + const_cast( + config->metal.present_command_queue), + metal_dispatch_table, shared_view_embedder); + }; // The static leak checker gets confused by the use of fml::MakeCopyable. // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) return fml::MakeCopyable( [surface_builder, platform_dispatch_table, - external_view_embedder = - std::move(external_view_embedder)](flutter::Shell& shell) mutable { + shared_view_embedder](flutter::Shell& shell) mutable { return std::make_unique( shell, // delegate shell.GetTaskRunners(), // task runners std::make_unique(surface_builder), - platform_dispatch_table, // platform dispatch table - std::move(external_view_embedder) // external view embedder + platform_dispatch_table, // platform dispatch table + shared_view_embedder // external view embedder ); }); #else @@ -581,33 +581,33 @@ InferVulkanPlatformViewCreationCallback( .present_image = vulkan_present_image_callback, }; - auto surface_builder = - [config, vulkan_dispatch_table, - vk_instance](std::shared_ptr - external_view_embedder) { - return std::make_unique( - config->vulkan.version, vk_instance, - config->vulkan.enabled_instance_extension_count, - config->vulkan.enabled_instance_extensions, - config->vulkan.enabled_device_extension_count, - config->vulkan.enabled_device_extensions, - static_cast(config->vulkan.physical_device), - static_cast(config->vulkan.device), - config->vulkan.queue_family_index, - static_cast(config->vulkan.queue), vulkan_dispatch_table, - external_view_embedder); - }; + std::shared_ptr shared_view_embedder = + std::move(external_view_embedder); + + auto surface_builder = [config, vulkan_dispatch_table, vk_instance, + shared_view_embedder]() { + return std::make_unique( + config->vulkan.version, vk_instance, + config->vulkan.enabled_instance_extension_count, + config->vulkan.enabled_instance_extensions, + config->vulkan.enabled_device_extension_count, + config->vulkan.enabled_device_extensions, + static_cast(config->vulkan.physical_device), + static_cast(config->vulkan.device), + config->vulkan.queue_family_index, + static_cast(config->vulkan.queue), vulkan_dispatch_table, + shared_view_embedder); + }; return fml::MakeCopyable( [surface_builder, platform_dispatch_table, - external_view_embedder = - std::move(external_view_embedder)](flutter::Shell& shell) mutable { + shared_view_embedder](flutter::Shell& shell) mutable { return std::make_unique( shell, // delegate shell.GetTaskRunners(), // task runners std::make_unique(surface_builder), - platform_dispatch_table, // platform dispatch table - std::move(external_view_embedder) // external view embedder + platform_dispatch_table, // platform dispatch table + shared_view_embedder // external view embedder ); }); #else @@ -638,24 +638,23 @@ InferSoftwarePlatformViewCreationCallback( software_present_backing_store, // required }; - auto surface_builder = - [software_dispatch_table]( - std::shared_ptr - external_view_embedder) { - return std::make_unique( - software_dispatch_table, external_view_embedder); - }; + std::shared_ptr shared_view_embedder = + std::move(external_view_embedder); + + auto surface_builder = [software_dispatch_table, shared_view_embedder]() { + return std::make_unique( + software_dispatch_table, shared_view_embedder); + }; return fml::MakeCopyable( [software_dispatch_table, surface_builder, platform_dispatch_table, - external_view_embedder = - std::move(external_view_embedder)](flutter::Shell& shell) mutable { + shared_view_embedder](flutter::Shell& shell) mutable { return std::make_unique( shell, // delegate shell.GetTaskRunners(), // task runners std::make_unique(surface_builder), - platform_dispatch_table, // platform dispatch table - std::move(external_view_embedder) // external view embedder + platform_dispatch_table, // platform dispatch table + shared_view_embedder // external view embedder ); }); } diff --git a/shell/platform/embedder/embedder_studio.h b/shell/platform/embedder/embedder_studio.h index d8ce7a6dbcbf9..195c769967fcf 100644 --- a/shell/platform/embedder/embedder_studio.h +++ b/shell/platform/embedder/embedder_studio.h @@ -14,16 +14,14 @@ namespace flutter { class EmbedderStudio { public: - using Builder = std::function( - std::shared_ptr external_view_embedder)>; + using Builder = std::function()>; EmbedderStudio(Builder builder) : builder_(std::move(builder)) {} virtual ~EmbedderStudio() = default; - virtual std::unique_ptr CreateSurface( - std::shared_ptr external_view_embedder) { - return builder_(external_view_embedder); + virtual std::unique_ptr CreateSurface() { + return builder_(); } virtual sk_sp CreateResourceContext() const { diff --git a/shell/platform/embedder/platform_view_embedder.cc b/shell/platform/embedder/platform_view_embedder.cc index b31914ee6743d..09ae49db8210b 100644 --- a/shell/platform/embedder/platform_view_embedder.cc +++ b/shell/platform/embedder/platform_view_embedder.cc @@ -96,7 +96,7 @@ std::unique_ptr PlatformViewEmbedder::CreateRenderingSurface( return nullptr; } auto& embedder_surface = embedder_surfaces_[view_id] = - embedder_studio_->CreateSurface(external_view_embedder_); + embedder_studio_->CreateSurface(); return embedder_surface->CreateGPUSurface(); } diff --git a/shell/platform/embedder/platform_view_embedder_unittests.cc b/shell/platform/embedder/platform_view_embedder_unittests.cc index b587cf96104a1..3735e97eca2ea 100644 --- a/shell/platform/embedder/platform_view_embedder_unittests.cc +++ b/shell/platform/embedder/platform_view_embedder_unittests.cc @@ -63,10 +63,7 @@ class MockStudio : public EmbedderStudio { public: MockStudio() : EmbedderStudio(nullptr) {} - MOCK_METHOD1(CreateSurface, - std::unique_ptr( - std::shared_ptr - external_view_embedder)); + MOCK_METHOD0(CreateSurface, std::unique_ptr()); MOCK_CONST_METHOD0(CreateResourceContext, sk_sp()); }; } // namespace From d5d29e60f95072ede375b9002e631a57a281b8eb Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 17 Mar 2023 14:17:32 -0700 Subject: [PATCH 012/147] Split studio, compilable --- ci/licenses_golden/licenses_flutter | 18 ++ shell/platform/embedder/BUILD.gn | 9 + shell/platform/embedder/embedder.cc | 97 ++++------ shell/platform/embedder/embedder_studio.h | 17 +- shell/platform/embedder/embedder_studio_gl.cc | 137 ++++++++++++++ shell/platform/embedder/embedder_studio_gl.h | 83 ++++++++ .../platform/embedder/embedder_studio_metal.h | 70 +++++++ .../embedder/embedder_studio_metal.mm | 75 ++++++++ .../embedder/embedder_studio_software.cc | 117 ++++++++++++ .../embedder/embedder_studio_software.h | 57 ++++++ .../embedder/embedder_studio_vulkan.cc | 178 ++++++++++++++++++ .../embedder/embedder_studio_vulkan.h | 88 +++++++++ .../platform/embedder/embedder_surface_gl.cc | 119 ++---------- shell/platform/embedder/embedder_surface_gl.h | 60 +----- .../embedder/embedder_surface_metal.h | 44 +---- .../embedder/embedder_surface_metal.mm | 48 +---- .../embedder/embedder_surface_software.cc | 78 +------- .../embedder/embedder_surface_software.h | 30 +-- .../embedder/embedder_surface_vulkan.cc | 153 ++------------- .../embedder/embedder_surface_vulkan.h | 49 +---- .../embedder/platform_view_embedder.h | 20 +- .../platform_view_embedder_unittests.cc | 47 +++-- 22 files changed, 988 insertions(+), 606 deletions(-) create mode 100644 shell/platform/embedder/embedder_studio_gl.cc create mode 100644 shell/platform/embedder/embedder_studio_gl.h create mode 100644 shell/platform/embedder/embedder_studio_metal.h create mode 100644 shell/platform/embedder/embedder_studio_metal.mm create mode 100644 shell/platform/embedder/embedder_studio_software.cc create mode 100644 shell/platform/embedder/embedder_studio_software.h create mode 100644 shell/platform/embedder/embedder_studio_vulkan.cc create mode 100644 shell/platform/embedder/embedder_studio_vulkan.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index fdb72da12350e..921e1f84a9df7 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -2666,6 +2666,15 @@ ORIGIN: ../../../flutter/shell/platform/embedder/embedder_render_target.h + ../. ORIGIN: ../../../flutter/shell/platform/embedder/embedder_render_target_cache.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/embedder/embedder_render_target_cache.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/embedder/embedder_struct_macros.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/embedder/embedder_studio.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/embedder/embedder_studio_gl.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/embedder/embedder_studio_gl.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/embedder/embedder_studio_metal.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/embedder/embedder_studio_metal.mm + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/embedder/embedder_studio_software.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/embedder/embedder_studio_software.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/embedder/embedder_studio_vulkan.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/embedder/embedder_studio_vulkan.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/embedder/embedder_surface.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/embedder/embedder_surface.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/embedder/embedder_surface_gl.cc + ../../../flutter/LICENSE @@ -5213,6 +5222,15 @@ FILE: ../../../flutter/shell/platform/embedder/embedder_render_target.h FILE: ../../../flutter/shell/platform/embedder/embedder_render_target_cache.cc FILE: ../../../flutter/shell/platform/embedder/embedder_render_target_cache.h FILE: ../../../flutter/shell/platform/embedder/embedder_struct_macros.h +FILE: ../../../flutter/shell/platform/embedder/embedder_studio.h +FILE: ../../../flutter/shell/platform/embedder/embedder_studio_gl.cc +FILE: ../../../flutter/shell/platform/embedder/embedder_studio_gl.h +FILE: ../../../flutter/shell/platform/embedder/embedder_studio_metal.h +FILE: ../../../flutter/shell/platform/embedder/embedder_studio_metal.mm +FILE: ../../../flutter/shell/platform/embedder/embedder_studio_software.cc +FILE: ../../../flutter/shell/platform/embedder/embedder_studio_software.h +FILE: ../../../flutter/shell/platform/embedder/embedder_studio_vulkan.cc +FILE: ../../../flutter/shell/platform/embedder/embedder_studio_vulkan.h FILE: ../../../flutter/shell/platform/embedder/embedder_surface.cc FILE: ../../../flutter/shell/platform/embedder/embedder_surface.h FILE: ../../../flutter/shell/platform/embedder/embedder_surface_gl.cc diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index ce4e1edea7880..4e5d46e6a03a2 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -76,6 +76,9 @@ template("embedder_source_set") { "embedder_render_target_cache.cc", "embedder_render_target_cache.h", "embedder_struct_macros.h", + "embedder_studio.h", + "embedder_studio_software.cc", + "embedder_studio_software.h", "embedder_surface.cc", "embedder_surface.h", "embedder_surface_software.cc", @@ -95,6 +98,8 @@ template("embedder_source_set") { sources += [ "embedder_external_texture_gl.cc", "embedder_external_texture_gl.h", + "embedder_studio_gl.cc", + "embedder_studio_gl.h", "embedder_surface_gl.cc", "embedder_surface_gl.h", ] @@ -120,6 +125,8 @@ template("embedder_source_set") { sources += [ "embedder_external_texture_metal.h", "embedder_external_texture_metal.mm", + "embedder_studio_metal.h", + "embedder_studio_metal.mm", "embedder_surface_metal.h", "embedder_surface_metal.mm", ] @@ -132,6 +139,8 @@ template("embedder_source_set") { if (embedder_enable_vulkan) { sources += [ + "embedder_studio_vulkan.cc", + "embedder_studio_vulkan.h", "embedder_surface_vulkan.cc", "embedder_surface_vulkan.h", ] diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index ce71900724097..b650355d4bcc1 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -54,6 +54,10 @@ extern const intptr_t kPlatformStrongDillSize; #include "flutter/shell/platform/embedder/embedder_platform_message_response.h" #include "flutter/shell/platform/embedder/embedder_render_target.h" #include "flutter/shell/platform/embedder/embedder_struct_macros.h" +#include "flutter/shell/platform/embedder/embedder_studio_gl.h" +#include "flutter/shell/platform/embedder/embedder_studio_metal.h" +#include "flutter/shell/platform/embedder/embedder_studio_software.h" +#include "flutter/shell/platform/embedder/embedder_studio_vulkan.h" #include "flutter/shell/platform/embedder/embedder_task_runner.h" #include "flutter/shell/platform/embedder/embedder_thread_host.h" #include "flutter/shell/platform/embedder/pixel_formats.h" @@ -415,7 +419,7 @@ InferOpenGLPlatformViewCreationCallback( bool fbo_reset_after_present = SAFE_ACCESS(open_gl_config, fbo_reset_after_present, false); - flutter::EmbedderSurfaceGL::GLDispatchTable gl_dispatch_table = { + flutter::EmbedderStudioGL::GLDispatchTable gl_dispatch_table = { gl_make_current, // gl_make_current_callback gl_clear_current, // gl_clear_current_callback gl_present, // gl_present_callback @@ -429,23 +433,18 @@ InferOpenGLPlatformViewCreationCallback( std::shared_ptr shared_view_embedder = std::move(external_view_embedder); - auto surface_builder = [gl_dispatch_table, fbo_reset_after_present, - shared_view_embedder]() { - return std::make_unique( - gl_dispatch_table, fbo_reset_after_present, shared_view_embedder); - }; - - return fml::MakeCopyable( - [surface_builder, platform_dispatch_table, - shared_view_embedder](flutter::Shell& shell) mutable { - return std::make_unique( - shell, // delegate - shell.GetTaskRunners(), // task runners - std::make_unique(surface_builder), - platform_dispatch_table, // embedder platform dispatch table - shared_view_embedder // external view embedder - ); - }); + return fml::MakeCopyable([gl_dispatch_table, fbo_reset_after_present, + platform_dispatch_table, shared_view_embedder]( + flutter::Shell& shell) mutable { + return std::make_unique( + shell, // delegate + shell.GetTaskRunners(), // task runners + std::make_unique( + gl_dispatch_table, fbo_reset_after_present, shared_view_embedder), + platform_dispatch_table, // embedder platform dispatch table + shared_view_embedder // external view embedder + ); + }); #else return nullptr; #endif @@ -492,7 +491,7 @@ InferMetalPlatformViewCreationCallback( return texture_info; }; - flutter::EmbedderSurfaceMetal::MetalDispatchTable metal_dispatch_table = { + flutter::EmbedderStudioMetal::MetalDispatchTable metal_dispatch_table = { .present = metal_present, .get_texture = metal_get_texture, }; @@ -500,24 +499,19 @@ InferMetalPlatformViewCreationCallback( std::shared_ptr shared_view_embedder = std::move(external_view_embedder); - auto surface_builder = [config, metal_dispatch_table, - shared_view_embedder]() { - return std::make_unique( - const_cast(config->metal.device), - const_cast( - config->metal.present_command_queue), - metal_dispatch_table, shared_view_embedder); - }; - // The static leak checker gets confused by the use of fml::MakeCopyable. // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) return fml::MakeCopyable( - [surface_builder, platform_dispatch_table, + [config, metal_dispatch_table, platform_dispatch_table, shared_view_embedder](flutter::Shell& shell) mutable { return std::make_unique( shell, // delegate shell.GetTaskRunners(), // task runners - std::make_unique(surface_builder), + std::make_unique( + const_cast(config->metal.device), + const_cast( + config->metal.present_command_queue), + metal_dispatch_table, shared_view_embedder), platform_dispatch_table, // platform dispatch table shared_view_embedder // external view embedder ); @@ -574,7 +568,7 @@ InferVulkanPlatformViewCreationCallback( auto proc_addr = vulkan_get_instance_proc_address(vk_instance, "vkGetInstanceProcAddr"); - flutter::EmbedderSurfaceVulkan::VulkanDispatchTable vulkan_dispatch_table = { + flutter::EmbedderStudioVulkan::VulkanDispatchTable vulkan_dispatch_table = { .get_instance_proc_address = reinterpret_cast(proc_addr), .get_next_image = vulkan_get_next_image, @@ -584,28 +578,23 @@ InferVulkanPlatformViewCreationCallback( std::shared_ptr shared_view_embedder = std::move(external_view_embedder); - auto surface_builder = [config, vulkan_dispatch_table, vk_instance, - shared_view_embedder]() { - return std::make_unique( - config->vulkan.version, vk_instance, - config->vulkan.enabled_instance_extension_count, - config->vulkan.enabled_instance_extensions, - config->vulkan.enabled_device_extension_count, - config->vulkan.enabled_device_extensions, - static_cast(config->vulkan.physical_device), - static_cast(config->vulkan.device), - config->vulkan.queue_family_index, - static_cast(config->vulkan.queue), vulkan_dispatch_table, - shared_view_embedder); - }; - return fml::MakeCopyable( - [surface_builder, platform_dispatch_table, + [config, vulkan_dispatch_table, vk_instance, platform_dispatch_table, shared_view_embedder](flutter::Shell& shell) mutable { return std::make_unique( shell, // delegate shell.GetTaskRunners(), // task runners - std::make_unique(surface_builder), + std::make_unique( + config->vulkan.version, vk_instance, + config->vulkan.enabled_instance_extension_count, + config->vulkan.enabled_instance_extensions, + config->vulkan.enabled_device_extension_count, + config->vulkan.enabled_device_extensions, + static_cast(config->vulkan.physical_device), + static_cast(config->vulkan.device), + config->vulkan.queue_family_index, + static_cast(config->vulkan.queue), + vulkan_dispatch_table, shared_view_embedder), platform_dispatch_table, // platform dispatch table shared_view_embedder // external view embedder ); @@ -633,7 +622,7 @@ InferSoftwarePlatformViewCreationCallback( return ptr(user_data, allocation, row_bytes, height); }; - flutter::EmbedderSurfaceSoftware::SoftwareDispatchTable + flutter::EmbedderStudioSoftware::SoftwareDispatchTable software_dispatch_table = { software_present_backing_store, // required }; @@ -641,18 +630,14 @@ InferSoftwarePlatformViewCreationCallback( std::shared_ptr shared_view_embedder = std::move(external_view_embedder); - auto surface_builder = [software_dispatch_table, shared_view_embedder]() { - return std::make_unique( - software_dispatch_table, shared_view_embedder); - }; - return fml::MakeCopyable( - [software_dispatch_table, surface_builder, platform_dispatch_table, + [software_dispatch_table, platform_dispatch_table, shared_view_embedder](flutter::Shell& shell) mutable { return std::make_unique( shell, // delegate shell.GetTaskRunners(), // task runners - std::make_unique(surface_builder), + std::make_unique( + software_dispatch_table, shared_view_embedder), platform_dispatch_table, // platform dispatch table shared_view_embedder // external view embedder ); diff --git a/shell/platform/embedder/embedder_studio.h b/shell/platform/embedder/embedder_studio.h index 195c769967fcf..8f50d5d42c677 100644 --- a/shell/platform/embedder/embedder_studio.h +++ b/shell/platform/embedder/embedder_studio.h @@ -14,23 +14,18 @@ namespace flutter { class EmbedderStudio { public: - using Builder = std::function()>; - - EmbedderStudio(Builder builder) : builder_(std::move(builder)) {} + EmbedderStudio() {} virtual ~EmbedderStudio() = default; - virtual std::unique_ptr CreateSurface() { - return builder_(); - } + virtual bool IsValid() const = 0; + + virtual std::unique_ptr CreateSurface() = 0; - virtual sk_sp CreateResourceContext() const { - // TODO(dkwingsmt) - return nullptr; - } + virtual sk_sp CreateResourceContext() const = 0; private: - Builder builder_; + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderStudio); }; } // namespace flutter diff --git a/shell/platform/embedder/embedder_studio_gl.cc b/shell/platform/embedder/embedder_studio_gl.cc new file mode 100644 index 0000000000000..d89a1a69bd1f0 --- /dev/null +++ b/shell/platform/embedder/embedder_studio_gl.cc @@ -0,0 +1,137 @@ +// 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. + +#include "flutter/shell/platform/embedder/embedder_studio_gl.h" + +#include + +#include "flutter/shell/platform/embedder/embedder_surface_gl.h" +#include "flutter/shell/common/shell_io_manager.h" + +namespace flutter { + +EmbedderStudioGL::EmbedderStudioGL( + GLDispatchTable gl_dispatch_table, + bool fbo_reset_after_present, + std::shared_ptr external_view_embedder) + : gl_dispatch_table_(std::move(gl_dispatch_table)), + fbo_reset_after_present_(fbo_reset_after_present), + external_view_embedder_(std::move(external_view_embedder)) { + // Make sure all required members of the dispatch table are checked. + if (!gl_dispatch_table_.gl_make_current_callback || + !gl_dispatch_table_.gl_clear_current_callback || + !gl_dispatch_table_.gl_present_callback || + !gl_dispatch_table_.gl_fbo_callback || + !gl_dispatch_table_.gl_populate_existing_damage) { + return; + } + + valid_ = true; +} + +EmbedderStudioGL::~EmbedderStudioGL() { + if (main_context_ != nullptr) { + main_context_->releaseResourcesAndAbandonContext(); + } +} + +// |EmbedderStudio| +bool EmbedderStudioGL::IsValid() const { + return valid_; +} + +// |GPUSurfaceGLDelegate| +std::unique_ptr EmbedderStudioGL::GLContextMakeCurrent() { + return std::make_unique( + gl_dispatch_table_.gl_make_current_callback()); +} + +// |GPUSurfaceGLDelegate| +bool EmbedderStudioGL::GLContextClearCurrent() { + return gl_dispatch_table_.gl_clear_current_callback(); +} + +// |GPUSurfaceGLDelegate| +bool EmbedderStudioGL::GLContextPresent(const GLPresentInfo& present_info) { + // Pass the present information to the embedder present callback. + return gl_dispatch_table_.gl_present_callback(present_info); +} + +// |GPUSurfaceGLDelegate| +GLFBOInfo EmbedderStudioGL::GLContextFBO(GLFrameInfo frame_info) const { + // Get the FBO ID using the gl_fbo_callback and then get exiting damage by + // passing that ID to the gl_populate_existing_damage. + return gl_dispatch_table_.gl_populate_existing_damage( + gl_dispatch_table_.gl_fbo_callback(frame_info)); +} + +// |GPUSurfaceGLDelegate| +bool EmbedderStudioGL::GLContextFBOResetAfterPresent() const { + return fbo_reset_after_present_; +} + +// |GPUSurfaceGLDelegate| +SkMatrix EmbedderStudioGL::GLContextSurfaceTransformation() const { + auto callback = gl_dispatch_table_.gl_surface_transformation_callback; + if (!callback) { + SkMatrix matrix; + matrix.setIdentity(); + return matrix; + } + return callback(); +} + +// |GPUSurfaceGLDelegate| +EmbedderStudioGL::GLProcResolver EmbedderStudioGL::GetGLProcResolver() const { + return gl_dispatch_table_.gl_proc_resolver; +} + +// |GPUSurfaceGLDelegate| +SurfaceFrame::FramebufferInfo EmbedderStudioGL::GLContextFramebufferInfo() + const { + // Enable partial repaint by default on the embedders. + auto info = SurfaceFrame::FramebufferInfo{}; + info.supports_readback = true; + info.supports_partial_repaint = + gl_dispatch_table_.gl_populate_existing_damage != nullptr; + return info; +} + +// |EmbedderStudio| +std::unique_ptr EmbedderStudioGL::CreateSurface() { + if (!main_context_) { + main_context_ = GPUSurfaceGLSkia::MakeGLContext(this); + } + const bool render_to_surface = !external_view_embedder_; + return std::make_unique( + main_context_, + this, // GPU surface GL delegate + render_to_surface // render to surface + ); +} + +// |EmbedderStudio| +sk_sp EmbedderStudioGL::CreateResourceContext() const { + auto callback = gl_dispatch_table_.gl_make_resource_current_callback; + if (callback && callback()) { + if (auto context = ShellIOManager::CreateCompatibleResourceLoadingContext( + GrBackend::kOpenGL_GrBackend, GetGLInterface())) { + return context; + } else { + FML_LOG(ERROR) + << "Internal error: Resource context available but could not create " + "a compatible Skia context."; + return nullptr; + } + } + + // The callback was not available or failed. + FML_LOG(ERROR) + << "Could not create a resource context for async texture uploads. " + "Expect degraded performance. Set a valid make_resource_current " + "callback on FlutterOpenGLRendererConfig."; + return nullptr; +} + +} // namespace flutter diff --git a/shell/platform/embedder/embedder_studio_gl.h b/shell/platform/embedder/embedder_studio_gl.h new file mode 100644 index 0000000000000..1a0b227ad918d --- /dev/null +++ b/shell/platform/embedder/embedder_studio_gl.h @@ -0,0 +1,83 @@ +// 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_PLATFORM_EMBEDDER_EMBEDDER_STUDIO_GL_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_STUDIO_GL_H_ + +#include "flutter/fml/macros.h" +#include "flutter/shell/gpu/gpu_surface_gl_skia.h" +#include "flutter/shell/platform/embedder/embedder_external_view_embedder.h" +#include "flutter/shell/platform/embedder/embedder_studio.h" + +namespace flutter { + +class EmbedderStudioGL final : public EmbedderStudio, + public GPUSurfaceGLDelegate { + public: + struct GLDispatchTable { + std::function gl_make_current_callback; // required + std::function gl_clear_current_callback; // required + std::function gl_present_callback; // required + std::function gl_fbo_callback; // required + std::function gl_make_resource_current_callback; // optional + std::function + gl_surface_transformation_callback; // optional + std::function gl_proc_resolver; // optional + std::function gl_populate_existing_damage; // required + }; + + EmbedderStudioGL( + GLDispatchTable gl_dispatch_table, + bool fbo_reset_after_present, + std::shared_ptr external_view_embedder); + + ~EmbedderStudioGL() override; + + // |EmbedderStudio| + bool IsValid() const override; + + // |EmbedderStudio| + std::unique_ptr CreateSurface() override; + + // |EmbedderStudio| + sk_sp CreateResourceContext() const override; + + // |GPUSurfaceGLDelegate| + std::unique_ptr GLContextMakeCurrent() override; + + // |GPUSurfaceGLDelegate| + bool GLContextClearCurrent() override; + + // |GPUSurfaceGLDelegate| + bool GLContextPresent(const GLPresentInfo& present_info) override; + + // |GPUSurfaceGLDelegate| + GLFBOInfo GLContextFBO(GLFrameInfo frame_info) const override; + + // |GPUSurfaceGLDelegate| + bool GLContextFBOResetAfterPresent() const override; + + // |GPUSurfaceGLDelegate| + SkMatrix GLContextSurfaceTransformation() const override; + + // |GPUSurfaceGLDelegate| + GLProcResolver GetGLProcResolver() const override; + + // |GPUSurfaceGLDelegate| + SurfaceFrame::FramebufferInfo GLContextFramebufferInfo() const override; + + private: + bool valid_ = false; + GLDispatchTable gl_dispatch_table_; + bool fbo_reset_after_present_; + sk_sp main_context_; + + std::shared_ptr external_view_embedder_; + + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderStudioGL); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_STUDIO_GL_H_ diff --git a/shell/platform/embedder/embedder_studio_metal.h b/shell/platform/embedder/embedder_studio_metal.h new file mode 100644 index 0000000000000..8dd0ab25b288c --- /dev/null +++ b/shell/platform/embedder/embedder_studio_metal.h @@ -0,0 +1,70 @@ +// 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_PLATFORM_EMBEDDER_EMBEDDER_STUDIO_METAL_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_STUDIO_METAL_H_ + +#include "flutter/fml/macros.h" +#include "flutter/shell/gpu/gpu_surface_metal_delegate.h" +#include "flutter/shell/gpu/gpu_surface_metal_skia.h" +#include "flutter/shell/platform/embedder/embedder_external_view_embedder.h" +#include "flutter/shell/platform/embedder/embedder_studio.h" + +#include "third_party/skia/include/core/SkSurface.h" + +namespace flutter { + +class EmbedderStudioMetal final : public EmbedderStudio, + public GPUSurfaceMetalDelegate { + public: + struct MetalDispatchTable { + std::function present; // required + std::function + get_texture; // required + }; + + EmbedderStudioMetal( + GPUMTLDeviceHandle device, + GPUMTLCommandQueueHandle command_queue, + MetalDispatchTable dispatch_table, + std::shared_ptr external_view_embedder); + + ~EmbedderStudioMetal() override; + + // |EmbedderStudio| + bool IsValid() const override; + + // |EmbedderStudio| + std::unique_ptr CreateSurface() override; + + // |EmbedderStudio| + sk_sp CreateResourceContext() const override; + + // |GPUSurfaceMetalDelegate| + GPUCAMetalLayerHandle GetCAMetalLayer( + const SkISize& frame_size) const override; + + // |GPUSurfaceMetalDelegate| + bool PresentDrawable(GrMTLHandle drawable) const override; + + // |GPUSurfaceMetalDelegate| + GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_size) const override; + + // |GPUSurfaceMetalDelegate| + bool PresentTexture(GPUMTLTextureInfo texture) const override; + + private: + bool valid_ = false; + MetalDispatchTable metal_dispatch_table_; + std::shared_ptr external_view_embedder_; + sk_sp surface_; + sk_sp main_context_; + sk_sp resource_context_; + + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderStudioMetal); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_STUDIO_METAL_H_ diff --git a/shell/platform/embedder/embedder_studio_metal.mm b/shell/platform/embedder/embedder_studio_metal.mm new file mode 100644 index 0000000000000..7400e454af1ff --- /dev/null +++ b/shell/platform/embedder/embedder_studio_metal.mm @@ -0,0 +1,75 @@ +// 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. + +#include "flutter/shell/platform/embedder/embedder_studio_metal.h" + +#include + +#include "flutter/fml/logging.h" +#include "flutter/shell/gpu/gpu_surface_metal_delegate.h" +#include "flutter/shell/platform/embedder/embedder_surface_metal.h" +#import "flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalSkia.h" +#include "third_party/skia/include/gpu/GrDirectContext.h" + +FLUTTER_ASSERT_NOT_ARC +namespace flutter { + +EmbedderStudioMetal::EmbedderStudioMetal( + GPUMTLDeviceHandle device, + GPUMTLCommandQueueHandle command_queue, + MetalDispatchTable metal_dispatch_table, + std::shared_ptr external_view_embedder) + : GPUSurfaceMetalDelegate(MTLRenderTargetType::kMTLTexture), + metal_dispatch_table_(std::move(metal_dispatch_table)), + external_view_embedder_(std::move(external_view_embedder)) { + main_context_ = + [FlutterDarwinContextMetalSkia createGrContext:(id)device + commandQueue:(id)command_queue]; + resource_context_ = + [FlutterDarwinContextMetalSkia createGrContext:(id)device + commandQueue:(id)command_queue]; + valid_ = main_context_ && resource_context_; +} + +EmbedderStudioMetal::~EmbedderStudioMetal() = default; + +bool EmbedderStudioMetal::IsValid() const { + return valid_; +} + +std::unique_ptr EmbedderStudioMetal::CreateSurface() { + if (!IsValid()) { + return nullptr; + } + const bool render_to_surface = !external_view_embedder_; + return std::make_unique( + main_context_, + this, // GPU surface GL delegate + render_to_surface // render to surface + ); +} + +sk_sp EmbedderStudioMetal::CreateResourceContext() const { + return resource_context_; +} + +GPUCAMetalLayerHandle EmbedderStudioMetal::GetCAMetalLayer(const SkISize& frame_info) const { + FML_CHECK(false) << "Only rendering to MTLTexture is supported."; + return nullptr; +} + +bool EmbedderStudioMetal::PresentDrawable(GrMTLHandle drawable) const { + FML_CHECK(false) << "Only rendering to MTLTexture is supported."; + return false; +} + +GPUMTLTextureInfo EmbedderStudioMetal::GetMTLTexture(const SkISize& frame_info) const { + return metal_dispatch_table_.get_texture(frame_info); +} + +bool EmbedderStudioMetal::PresentTexture(GPUMTLTextureInfo texture) const { + return metal_dispatch_table_.present(texture); +} + +} // namespace flutter diff --git a/shell/platform/embedder/embedder_studio_software.cc b/shell/platform/embedder/embedder_studio_software.cc new file mode 100644 index 0000000000000..a3d898b8c2b0d --- /dev/null +++ b/shell/platform/embedder/embedder_studio_software.cc @@ -0,0 +1,117 @@ +// 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. + +#include "flutter/shell/platform/embedder/embedder_studio_software.h" + +#include + +#include "flutter/fml/trace_event.h" + +#include "flutter/shell/platform/embedder/embedder_studio_software.h" +#include "flutter/shell/platform/embedder/embedder_surface_software.h" +#include "third_party/skia/include/core/SkColorSpace.h" +#include "third_party/skia/include/core/SkImageInfo.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/gpu/GrDirectContext.h" + +namespace flutter { + +EmbedderStudioSoftware::EmbedderStudioSoftware( + SoftwareDispatchTable software_dispatch_table, + std::shared_ptr external_view_embedder) + : software_dispatch_table_(std::move(software_dispatch_table)), + external_view_embedder_(std::move(external_view_embedder)) { + if (!software_dispatch_table_.software_present_backing_store) { + return; + } + valid_ = true; +} + +EmbedderStudioSoftware::~EmbedderStudioSoftware() = default; + +// |EmbedderStudio| +bool EmbedderStudioSoftware::IsValid() const { + return valid_; +} + +// |EmbedderStudio| +std::unique_ptr EmbedderStudioSoftware::CreateSurface() { + if (!IsValid()) { + return nullptr; + } + const bool render_to_surface = !external_view_embedder_; + auto surface = std::make_unique(this, render_to_surface); + + if (!surface->IsValid()) { + return nullptr; + } + + return surface; +} + +// |EmbedderStudio| +sk_sp EmbedderStudioSoftware::CreateResourceContext() const { + return nullptr; +} + +// |GPUSurfaceSoftwareDelegate| +sk_sp EmbedderStudioSoftware::AcquireBackingStore( + const SkISize& size) { + TRACE_EVENT0("flutter", "EmbedderStudioSoftware::AcquireBackingStore"); + if (!IsValid()) { + FML_LOG(ERROR) + << "Could not acquire backing store for the software surface."; + return nullptr; + } + + if (sk_surface_ != nullptr && + SkISize::Make(sk_surface_->width(), sk_surface_->height()) == size) { + // The old and new surface sizes are the same. Nothing to do here. + return sk_surface_; + } + + SkImageInfo info = SkImageInfo::MakeN32( + size.fWidth, size.fHeight, kPremul_SkAlphaType, SkColorSpace::MakeSRGB()); + sk_surface_ = SkSurface::MakeRaster(info, nullptr); + + if (sk_surface_ == nullptr) { + FML_LOG(ERROR) << "Could not create backing store for software rendering."; + return nullptr; + } + + return sk_surface_; +} + +// |GPUSurfaceSoftwareDelegate| +bool EmbedderStudioSoftware::PresentBackingStore( + sk_sp backing_store) { + if (!IsValid()) { + FML_LOG(ERROR) << "Tried to present an invalid software surface."; + return false; + } + + SkPixmap pixmap; + if (!backing_store->peekPixels(&pixmap)) { + FML_LOG(ERROR) << "Could not peek the pixels of the backing store."; + return false; + } + + // Some basic sanity checking. + uint64_t expected_pixmap_data_size = pixmap.width() * pixmap.height() * 4; + + const size_t pixmap_size = pixmap.computeByteSize(); + + if (expected_pixmap_data_size != pixmap_size) { + FML_LOG(ERROR) << "Software backing store had unexpected size."; + return false; + } + + return software_dispatch_table_.software_present_backing_store( + pixmap.addr(), // + pixmap.rowBytes(), // + pixmap.height() // + ); +} + +} // namespace flutter diff --git a/shell/platform/embedder/embedder_studio_software.h b/shell/platform/embedder/embedder_studio_software.h new file mode 100644 index 0000000000000..7be884601f292 --- /dev/null +++ b/shell/platform/embedder/embedder_studio_software.h @@ -0,0 +1,57 @@ +// 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_PLATFORM_EMBEDDER_EMBEDDER_STUDIO_SOFTWARE_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_STUDIO_SOFTWARE_H_ + +#include "flutter/fml/macros.h" +#include "flutter/shell/gpu/gpu_surface_software.h" +#include "flutter/shell/platform/embedder/embedder_external_view_embedder.h" +#include "flutter/shell/platform/embedder/embedder_studio.h" + +#include "third_party/skia/include/core/SkSurface.h" + +namespace flutter { + +class EmbedderStudioSoftware final : public EmbedderStudio, + public GPUSurfaceSoftwareDelegate { + public: + struct SoftwareDispatchTable { + std::function + software_present_backing_store; // required + }; + + EmbedderStudioSoftware( + SoftwareDispatchTable software_dispatch_table, + std::shared_ptr external_view_embedder); + + ~EmbedderStudioSoftware() override; + + // |EmbedderStudio| + bool IsValid() const override; + + // |EmbedderStudio| + std::unique_ptr CreateSurface() override; + + // |EmbedderStudio| + sk_sp CreateResourceContext() const override; + + // |GPUSurfaceSoftwareDelegate| + sk_sp AcquireBackingStore(const SkISize& size) override; + + // |GPUSurfaceSoftwareDelegate| + bool PresentBackingStore(sk_sp backing_store) override; + + private: + bool valid_ = false; + SoftwareDispatchTable software_dispatch_table_; + sk_sp sk_surface_; + std::shared_ptr external_view_embedder_; + + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderStudioSoftware); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_STUDIO_SOFTWARE_H_ diff --git a/shell/platform/embedder/embedder_studio_vulkan.cc b/shell/platform/embedder/embedder_studio_vulkan.cc new file mode 100644 index 0000000000000..dad77ce3b3ec5 --- /dev/null +++ b/shell/platform/embedder/embedder_studio_vulkan.cc @@ -0,0 +1,178 @@ +// 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. + +#include "flutter/shell/platform/embedder/embedder_studio_vulkan.h" + +#include + +#include "flutter/flutter_vma/flutter_skia_vma.h" +#include "flutter/shell/common/shell_io_manager.h" +#include "flutter/shell/gpu/gpu_surface_vulkan.h" +#include "flutter/shell/platform/embedder/embedder_surface_vulkan.h" +#include "flutter/shell/gpu/gpu_surface_vulkan_delegate.h" +#include "flutter/vulkan/vulkan_skia_proc_table.h" +#include "include/gpu/GrDirectContext.h" +#include "include/gpu/vk/GrVkBackendContext.h" +#include "include/gpu/vk/GrVkExtensions.h" + +namespace flutter { + +EmbedderStudioVulkan::EmbedderStudioVulkan( + uint32_t version, + VkInstance instance, + size_t instance_extension_count, + const char** instance_extensions, + size_t device_extension_count, + const char** device_extensions, + VkPhysicalDevice physical_device, + VkDevice device, + uint32_t queue_family_index, + VkQueue queue, + const VulkanDispatchTable& vulkan_dispatch_table, + std::shared_ptr external_view_embedder) + : vk_(fml::MakeRefCounted( + vulkan_dispatch_table.get_instance_proc_address)), + device_(*vk_, + vulkan::VulkanHandle{physical_device}, + vulkan::VulkanHandle{device}, + queue_family_index, + vulkan::VulkanHandle{queue}), + vulkan_dispatch_table_(vulkan_dispatch_table), + external_view_embedder_(std::move(external_view_embedder)) { + // Make sure all required members of the dispatch table are checked. + if (!vulkan_dispatch_table_.get_instance_proc_address || + !vulkan_dispatch_table_.get_next_image || + !vulkan_dispatch_table_.present_image) { + return; + } + + bool success = vk_->SetupInstanceProcAddresses( + vulkan::VulkanHandle{instance}); + if (!success) { + FML_LOG(ERROR) << "Could not setup instance proc addresses."; + return; + } + success = + vk_->SetupDeviceProcAddresses(vulkan::VulkanHandle{device}); + if (!success) { + FML_LOG(ERROR) << "Could not setup device proc addresses."; + return; + } + if (!vk_->IsValid()) { + FML_LOG(ERROR) << "VulkanProcTable invalid."; + return; + } + + main_context_ = CreateGrContext(instance, version, instance_extension_count, + instance_extensions, device_extension_count, + device_extensions, ContextType::kRender); + // TODO(96954): Add a second (optional) queue+family index to the Embedder API + // to allow embedders to specify a dedicated transfer queue for + // use by the resource context. Queue families with graphics + // capability can always be used for memory transferring, but it + // would be advantageous to use a dedicated transter queue here. + resource_context_ = CreateGrContext( + instance, version, instance_extension_count, instance_extensions, + device_extension_count, device_extensions, ContextType::kResource); + + valid_ = main_context_ && resource_context_; +} + +EmbedderStudioVulkan::~EmbedderStudioVulkan() { + if (main_context_) { + main_context_->releaseResourcesAndAbandonContext(); + } + if (resource_context_) { + resource_context_->releaseResourcesAndAbandonContext(); + } +} + +// |GPUSurfaceVulkanDelegate| +const vulkan::VulkanProcTable& EmbedderStudioVulkan::vk() { + return *vk_; +} + +// |GPUSurfaceVulkanDelegate| +FlutterVulkanImage EmbedderStudioVulkan::AcquireImage(const SkISize& size) { + return vulkan_dispatch_table_.get_next_image(size); +} + +// |GPUSurfaceVulkanDelegate| +bool EmbedderStudioVulkan::PresentImage(VkImage image, VkFormat format) { + return vulkan_dispatch_table_.present_image(image, format); +} + +// |EmbedderStudio| +bool EmbedderStudioVulkan::IsValid() const { + return valid_; +} + +// |EmbedderStudio| +std::unique_ptr EmbedderStudioVulkan::CreateSurface() { + const bool render_to_surface = !external_view_embedder_; + return std::make_unique(this, main_context_, + render_to_surface); +} + +// |EmbedderStudio| +sk_sp EmbedderStudioVulkan::CreateResourceContext() const { + return resource_context_; +} + +sk_sp EmbedderStudioVulkan::CreateGrContext( + VkInstance instance, + uint32_t version, + size_t instance_extension_count, + const char** instance_extensions, + size_t device_extension_count, + const char** device_extensions, + ContextType context_type) const { + uint32_t skia_features = 0; + if (!device_.GetPhysicalDeviceFeaturesSkia(&skia_features)) { + FML_LOG(ERROR) << "Failed to get physical device features."; + + return nullptr; + } + + auto get_proc = CreateSkiaGetProc(vk_); + if (get_proc == nullptr) { + FML_LOG(ERROR) << "Failed to create Vulkan getProc for Skia."; + return nullptr; + } + + GrVkExtensions extensions; + + GrVkBackendContext backend_context = {}; + backend_context.fInstance = instance; + backend_context.fPhysicalDevice = device_.GetPhysicalDeviceHandle(); + backend_context.fDevice = device_.GetHandle(); + backend_context.fQueue = device_.GetQueueHandle(); + backend_context.fGraphicsQueueIndex = device_.GetGraphicsQueueIndex(); + backend_context.fMinAPIVersion = version; + backend_context.fMaxAPIVersion = version; + backend_context.fFeatures = skia_features; + backend_context.fVkExtensions = &extensions; + backend_context.fGetProc = get_proc; + backend_context.fOwnsInstanceAndDevice = false; + + uint32_t vulkan_api_version = version; + sk_sp allocator = + flutter::FlutterSkiaVulkanMemoryAllocator::Make( + vulkan_api_version, instance, device_.GetPhysicalDeviceHandle(), + device_.GetHandle(), vk_, true); + + backend_context.fMemoryAllocator = allocator; + + extensions.init(backend_context.fGetProc, backend_context.fInstance, + backend_context.fPhysicalDevice, instance_extension_count, + instance_extensions, device_extension_count, + device_extensions); + + GrContextOptions options = + MakeDefaultContextOptions(context_type, GrBackendApi::kVulkan); + options.fReduceOpsTaskSplitting = GrContextOptions::Enable::kNo; + return GrDirectContext::MakeVulkan(backend_context, options); +} + +} // namespace flutter diff --git a/shell/platform/embedder/embedder_studio_vulkan.h b/shell/platform/embedder/embedder_studio_vulkan.h new file mode 100644 index 0000000000000..e161819591105 --- /dev/null +++ b/shell/platform/embedder/embedder_studio_vulkan.h @@ -0,0 +1,88 @@ +// 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_PLATFORM_EMBEDDER_EMBEDDER_STUDIO_VULKAN_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_STUDIO_VULKAN_H_ + +#include "flutter/fml/macros.h" +#include "flutter/shell/common/context_options.h" +#include "flutter/shell/gpu/gpu_surface_vulkan.h" +#include "flutter/shell/gpu/gpu_surface_vulkan_delegate.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/embedder/embedder_external_view_embedder.h" +#include "flutter/shell/platform/embedder/embedder_studio.h" +#include "flutter/vulkan/procs/vulkan_proc_table.h" + +namespace flutter { + +class EmbedderStudioVulkan final : public EmbedderStudio, + public GPUSurfaceVulkanDelegate { + public: + struct VulkanDispatchTable { + PFN_vkGetInstanceProcAddr get_instance_proc_address; // required + std::function + get_next_image; // required + std::function + present_image; // required + }; + + EmbedderStudioVulkan( + uint32_t version, + VkInstance instance, + size_t instance_extension_count, + const char** instance_extensions, + size_t device_extension_count, + const char** device_extensions, + VkPhysicalDevice physical_device, + VkDevice device, + uint32_t queue_family_index, + VkQueue queue, + const VulkanDispatchTable& vulkan_dispatch_table, + std::shared_ptr external_view_embedder); + + ~EmbedderStudioVulkan() override; + + // |GPUSurfaceVulkanDelegate| + const vulkan::VulkanProcTable& vk() override; + + // |GPUSurfaceVulkanDelegate| + FlutterVulkanImage AcquireImage(const SkISize& size) override; + + // |GPUSurfaceVulkanDelegate| + bool PresentImage(VkImage image, VkFormat format) override; + + // |EmbedderStudio| + bool IsValid() const override; + + // |EmbedderStudio| + std::unique_ptr CreateSurface() override; + + // |EmbedderStudio| + sk_sp CreateResourceContext() const override; + + private: + bool valid_ = false; + fml::RefPtr vk_; + vulkan::VulkanDevice device_; + VulkanDispatchTable vulkan_dispatch_table_; + std::shared_ptr external_view_embedder_; + sk_sp main_context_; + sk_sp resource_context_; + + sk_sp CreateGrContext(VkInstance instance, + uint32_t version, + size_t instance_extension_count, + const char** instance_extensions, + size_t device_extension_count, + const char** device_extensions, + ContextType context_type) const; + + void* GetInstanceProcAddress(VkInstance instance, const char* proc_name); + + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderStudioVulkan); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_STUDIO_VULKAN_H_ diff --git a/shell/platform/embedder/embedder_surface_gl.cc b/shell/platform/embedder/embedder_surface_gl.cc index 9742dd0e4779b..929f0e5092d95 100644 --- a/shell/platform/embedder/embedder_surface_gl.cc +++ b/shell/platform/embedder/embedder_surface_gl.cc @@ -7,130 +7,39 @@ #include #include "flutter/shell/common/shell_io_manager.h" +#include "flutter/shell/platform/embedder/embedder_studio_gl.h" namespace flutter { -EmbedderSurfaceGL::EmbedderSurfaceGL( - GLDispatchTable gl_dispatch_table, - bool fbo_reset_after_present, - std::shared_ptr external_view_embedder) - : gl_dispatch_table_(std::move(gl_dispatch_table)), - fbo_reset_after_present_(fbo_reset_after_present), - external_view_embedder_(std::move(external_view_embedder)) { - // Make sure all required members of the dispatch table are checked. - if (!gl_dispatch_table_.gl_make_current_callback || - !gl_dispatch_table_.gl_clear_current_callback || - !gl_dispatch_table_.gl_present_callback || - !gl_dispatch_table_.gl_fbo_callback || - !gl_dispatch_table_.gl_populate_existing_damage) { - return; - } - - valid_ = true; -} +EmbedderSurfaceGL::EmbedderSurfaceGL(sk_sp main_context, + EmbedderStudioGL* studio, + bool render_to_surface) + : main_context_(main_context), + studio_(studio), + render_to_surface_(render_to_surface) {} -EmbedderSurfaceGL::~EmbedderSurfaceGL() { - if (main_context_ != nullptr) { - main_context_->releaseResourcesAndAbandonContext(); - } -} +EmbedderSurfaceGL::~EmbedderSurfaceGL() {} // |EmbedderSurface| bool EmbedderSurfaceGL::IsValid() const { - return valid_; -} - -// |GPUSurfaceGLDelegate| -std::unique_ptr EmbedderSurfaceGL::GLContextMakeCurrent() { - return std::make_unique( - gl_dispatch_table_.gl_make_current_callback()); -} - -// |GPUSurfaceGLDelegate| -bool EmbedderSurfaceGL::GLContextClearCurrent() { - return gl_dispatch_table_.gl_clear_current_callback(); -} - -// |GPUSurfaceGLDelegate| -bool EmbedderSurfaceGL::GLContextPresent(const GLPresentInfo& present_info) { - // Pass the present information to the embedder present callback. - return gl_dispatch_table_.gl_present_callback(present_info); -} - -// |GPUSurfaceGLDelegate| -GLFBOInfo EmbedderSurfaceGL::GLContextFBO(GLFrameInfo frame_info) const { - // Get the FBO ID using the gl_fbo_callback and then get exiting damage by - // passing that ID to the gl_populate_existing_damage. - return gl_dispatch_table_.gl_populate_existing_damage( - gl_dispatch_table_.gl_fbo_callback(frame_info)); -} - -// |GPUSurfaceGLDelegate| -bool EmbedderSurfaceGL::GLContextFBOResetAfterPresent() const { - return fbo_reset_after_present_; -} - -// |GPUSurfaceGLDelegate| -SkMatrix EmbedderSurfaceGL::GLContextSurfaceTransformation() const { - auto callback = gl_dispatch_table_.gl_surface_transformation_callback; - if (!callback) { - SkMatrix matrix; - matrix.setIdentity(); - return matrix; - } - return callback(); -} - -// |GPUSurfaceGLDelegate| -EmbedderSurfaceGL::GLProcResolver EmbedderSurfaceGL::GetGLProcResolver() const { - return gl_dispatch_table_.gl_proc_resolver; -} - -// |GPUSurfaceGLDelegate| -SurfaceFrame::FramebufferInfo EmbedderSurfaceGL::GLContextFramebufferInfo() - const { - // Enable partial repaint by default on the embedders. - auto info = SurfaceFrame::FramebufferInfo{}; - info.supports_readback = true; - info.supports_partial_repaint = - gl_dispatch_table_.gl_populate_existing_damage != nullptr; - return info; + return studio_->IsValid(); } // |EmbedderSurface| std::unique_ptr EmbedderSurfaceGL::CreateGPUSurface() { - if (!main_context_) { - main_context_ = GPUSurfaceGLSkia::MakeGLContext(this); + if (!IsValid()) { + return nullptr; } - const bool render_to_surface = !external_view_embedder_; return std::make_unique( main_context_, - this, // GPU surface GL delegate - render_to_surface // render to surface + studio_, // GPU surface GL delegate + render_to_surface_ // render to surface ); } // |EmbedderSurface| sk_sp EmbedderSurfaceGL::CreateResourceContext() const { - auto callback = gl_dispatch_table_.gl_make_resource_current_callback; - if (callback && callback()) { - if (auto context = ShellIOManager::CreateCompatibleResourceLoadingContext( - GrBackend::kOpenGL_GrBackend, GetGLInterface())) { - return context; - } else { - FML_LOG(ERROR) - << "Internal error: Resource context available but could not create " - "a compatible Skia context."; - return nullptr; - } - } - - // The callback was not available or failed. - FML_LOG(ERROR) - << "Could not create a resource context for async texture uploads. " - "Expect degraded performance. Set a valid make_resource_current " - "callback on FlutterOpenGLRendererConfig."; - return nullptr; + return studio_->CreateResourceContext(); } } // namespace flutter diff --git a/shell/platform/embedder/embedder_surface_gl.h b/shell/platform/embedder/embedder_surface_gl.h index 10d3c8c75b671..05456635661de 100644 --- a/shell/platform/embedder/embedder_surface_gl.h +++ b/shell/platform/embedder/embedder_surface_gl.h @@ -8,73 +8,33 @@ #include "flutter/fml/macros.h" #include "flutter/shell/gpu/gpu_surface_gl_skia.h" #include "flutter/shell/platform/embedder/embedder_external_view_embedder.h" +#include "flutter/shell/platform/embedder/embedder_studio_gl.h" #include "flutter/shell/platform/embedder/embedder_surface.h" namespace flutter { -class EmbedderSurfaceGL final : public EmbedderSurface, - public GPUSurfaceGLDelegate { +class EmbedderSurfaceGL final : public EmbedderSurface { public: - struct GLDispatchTable { - std::function gl_make_current_callback; // required - std::function gl_clear_current_callback; // required - std::function gl_present_callback; // required - std::function gl_fbo_callback; // required - std::function gl_make_resource_current_callback; // optional - std::function - gl_surface_transformation_callback; // optional - std::function gl_proc_resolver; // optional - std::function gl_populate_existing_damage; // required - }; - - EmbedderSurfaceGL( - GLDispatchTable gl_dispatch_table, - bool fbo_reset_after_present, - std::shared_ptr external_view_embedder); + EmbedderSurfaceGL(sk_sp main_context, + EmbedderStudioGL* studio, + bool render_to_surface); ~EmbedderSurfaceGL() override; - private: - bool valid_ = false; - GLDispatchTable gl_dispatch_table_; - bool fbo_reset_after_present_; - sk_sp main_context_; - - std::shared_ptr external_view_embedder_; - // |EmbedderSurface| bool IsValid() const override; + private: + sk_sp main_context_; + EmbedderStudioGL* studio_; + bool render_to_surface_; + // |EmbedderSurface| std::unique_ptr CreateGPUSurface() override; // |EmbedderSurface| sk_sp CreateResourceContext() const override; - // |GPUSurfaceGLDelegate| - std::unique_ptr GLContextMakeCurrent() override; - - // |GPUSurfaceGLDelegate| - bool GLContextClearCurrent() override; - - // |GPUSurfaceGLDelegate| - bool GLContextPresent(const GLPresentInfo& present_info) override; - - // |GPUSurfaceGLDelegate| - GLFBOInfo GLContextFBO(GLFrameInfo frame_info) const override; - - // |GPUSurfaceGLDelegate| - bool GLContextFBOResetAfterPresent() const override; - - // |GPUSurfaceGLDelegate| - SkMatrix GLContextSurfaceTransformation() const override; - - // |GPUSurfaceGLDelegate| - GLProcResolver GetGLProcResolver() const override; - - // |GPUSurfaceGLDelegate| - SurfaceFrame::FramebufferInfo GLContextFramebufferInfo() const override; - FML_DISALLOW_COPY_AND_ASSIGN(EmbedderSurfaceGL); }; diff --git a/shell/platform/embedder/embedder_surface_metal.h b/shell/platform/embedder/embedder_surface_metal.h index d835218103e02..54ad165e5e3c4 100644 --- a/shell/platform/embedder/embedder_surface_metal.h +++ b/shell/platform/embedder/embedder_surface_metal.h @@ -9,59 +9,35 @@ #include "flutter/shell/gpu/gpu_surface_metal_delegate.h" #include "flutter/shell/gpu/gpu_surface_metal_skia.h" #include "flutter/shell/platform/embedder/embedder_external_view_embedder.h" +#include "flutter/shell/platform/embedder/embedder_studio_metal.h" #include "flutter/shell/platform/embedder/embedder_surface.h" #include "third_party/skia/include/core/SkSurface.h" namespace flutter { -class EmbedderSurfaceMetal final : public EmbedderSurface, - public GPUSurfaceMetalDelegate { +class EmbedderSurfaceMetal final : public EmbedderSurface { public: - struct MetalDispatchTable { - std::function present; // required - std::function - get_texture; // required - }; - - EmbedderSurfaceMetal( - GPUMTLDeviceHandle device, - GPUMTLCommandQueueHandle command_queue, - MetalDispatchTable dispatch_table, - std::shared_ptr external_view_embedder); + EmbedderSurfaceMetal(sk_sp main_context, + EmbedderStudioMetal* studio, + bool render_to_surface); ~EmbedderSurfaceMetal() override; - private: - bool valid_ = false; - MetalDispatchTable metal_dispatch_table_; - std::shared_ptr external_view_embedder_; - sk_sp surface_; - sk_sp main_context_; - sk_sp resource_context_; - // |EmbedderSurface| bool IsValid() const override; + private: + sk_sp main_context_; + EmbedderStudioMetal* studio_; + bool render_to_surface_; + // |EmbedderSurface| std::unique_ptr CreateGPUSurface() override; // |EmbedderSurface| sk_sp CreateResourceContext() const override; - // |GPUSurfaceMetalDelegate| - GPUCAMetalLayerHandle GetCAMetalLayer( - const SkISize& frame_size) const override; - - // |GPUSurfaceMetalDelegate| - bool PresentDrawable(GrMTLHandle drawable) const override; - - // |GPUSurfaceMetalDelegate| - GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_size) const override; - - // |GPUSurfaceMetalDelegate| - bool PresentTexture(GPUMTLTextureInfo texture) const override; - FML_DISALLOW_COPY_AND_ASSIGN(EmbedderSurfaceMetal); }; diff --git a/shell/platform/embedder/embedder_surface_metal.mm b/shell/platform/embedder/embedder_surface_metal.mm index b7582d01b5c2c..b3cbd6c96a7ad 100644 --- a/shell/platform/embedder/embedder_surface_metal.mm +++ b/shell/platform/embedder/embedder_surface_metal.mm @@ -9,32 +9,21 @@ #include "flutter/fml/logging.h" #include "flutter/shell/gpu/gpu_surface_metal_delegate.h" #import "flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalSkia.h" +#include "flutter/shell/platform/embedder/embedder_studio_metal.h" #include "third_party/skia/include/gpu/GrDirectContext.h" FLUTTER_ASSERT_NOT_ARC namespace flutter { -EmbedderSurfaceMetal::EmbedderSurfaceMetal( - GPUMTLDeviceHandle device, - GPUMTLCommandQueueHandle command_queue, - MetalDispatchTable metal_dispatch_table, - std::shared_ptr external_view_embedder) - : GPUSurfaceMetalDelegate(MTLRenderTargetType::kMTLTexture), - metal_dispatch_table_(std::move(metal_dispatch_table)), - external_view_embedder_(std::move(external_view_embedder)) { - main_context_ = - [FlutterDarwinContextMetalSkia createGrContext:(id)device - commandQueue:(id)command_queue]; - resource_context_ = - [FlutterDarwinContextMetalSkia createGrContext:(id)device - commandQueue:(id)command_queue]; - valid_ = main_context_ && resource_context_; -} +EmbedderSurfaceMetal::EmbedderSurfaceMetal(sk_sp main_context, + EmbedderStudioMetal* studio, + bool render_to_surface) + : main_context_(main_context), studio_(studio), render_to_surface_(render_to_surface) {} EmbedderSurfaceMetal::~EmbedderSurfaceMetal() = default; bool EmbedderSurfaceMetal::IsValid() const { - return valid_; + return studio_->IsValid(); } std::unique_ptr EmbedderSurfaceMetal::CreateGPUSurface() API_AVAILABLE(ios(13.0)) { @@ -46,9 +35,8 @@ return nullptr; } - const bool render_to_surface = !external_view_embedder_; - auto surface = std::make_unique(this, main_context_, MsaaSampleCount::kNone, - render_to_surface); + auto surface = std::make_unique(studio_, main_context_, + MsaaSampleCount::kNone, render_to_surface_); if (!surface->IsValid()) { return nullptr; @@ -58,25 +46,7 @@ } sk_sp EmbedderSurfaceMetal::CreateResourceContext() const { - return resource_context_; -} - -GPUCAMetalLayerHandle EmbedderSurfaceMetal::GetCAMetalLayer(const SkISize& frame_info) const { - FML_CHECK(false) << "Only rendering to MTLTexture is supported."; - return nullptr; -} - -bool EmbedderSurfaceMetal::PresentDrawable(GrMTLHandle drawable) const { - FML_CHECK(false) << "Only rendering to MTLTexture is supported."; - return false; -} - -GPUMTLTextureInfo EmbedderSurfaceMetal::GetMTLTexture(const SkISize& frame_info) const { - return metal_dispatch_table_.get_texture(frame_info); -} - -bool EmbedderSurfaceMetal::PresentTexture(GPUMTLTextureInfo texture) const { - return metal_dispatch_table_.present(texture); + return studio_->CreateResourceContext(); } } // namespace flutter diff --git a/shell/platform/embedder/embedder_surface_software.cc b/shell/platform/embedder/embedder_surface_software.cc index 1f3735b521951..6db94313126b1 100644 --- a/shell/platform/embedder/embedder_surface_software.cc +++ b/shell/platform/embedder/embedder_surface_software.cc @@ -15,22 +15,15 @@ namespace flutter { -EmbedderSurfaceSoftware::EmbedderSurfaceSoftware( - SoftwareDispatchTable software_dispatch_table, - std::shared_ptr external_view_embedder) - : software_dispatch_table_(std::move(software_dispatch_table)), - external_view_embedder_(std::move(external_view_embedder)) { - if (!software_dispatch_table_.software_present_backing_store) { - return; - } - valid_ = true; -} +EmbedderSurfaceSoftware::EmbedderSurfaceSoftware(EmbedderStudioSoftware* studio, + bool render_to_surface) + : studio_(studio), render_to_surface_(render_to_surface) {} EmbedderSurfaceSoftware::~EmbedderSurfaceSoftware() = default; // |EmbedderSurface| bool EmbedderSurfaceSoftware::IsValid() const { - return valid_; + return true; } // |EmbedderSurface| @@ -38,8 +31,8 @@ std::unique_ptr EmbedderSurfaceSoftware::CreateGPUSurface() { if (!IsValid()) { return nullptr; } - const bool render_to_surface = !external_view_embedder_; - auto surface = std::make_unique(this, render_to_surface); + auto surface = + std::make_unique(studio_, render_to_surface_); if (!surface->IsValid()) { return nullptr; @@ -53,63 +46,4 @@ sk_sp EmbedderSurfaceSoftware::CreateResourceContext() const { return nullptr; } -// |GPUSurfaceSoftwareDelegate| -sk_sp EmbedderSurfaceSoftware::AcquireBackingStore( - const SkISize& size) { - TRACE_EVENT0("flutter", "EmbedderSurfaceSoftware::AcquireBackingStore"); - if (!IsValid()) { - FML_LOG(ERROR) - << "Could not acquire backing store for the software surface."; - return nullptr; - } - - if (sk_surface_ != nullptr && - SkISize::Make(sk_surface_->width(), sk_surface_->height()) == size) { - // The old and new surface sizes are the same. Nothing to do here. - return sk_surface_; - } - - SkImageInfo info = SkImageInfo::MakeN32( - size.fWidth, size.fHeight, kPremul_SkAlphaType, SkColorSpace::MakeSRGB()); - sk_surface_ = SkSurface::MakeRaster(info, nullptr); - - if (sk_surface_ == nullptr) { - FML_LOG(ERROR) << "Could not create backing store for software rendering."; - return nullptr; - } - - return sk_surface_; -} - -// |GPUSurfaceSoftwareDelegate| -bool EmbedderSurfaceSoftware::PresentBackingStore( - sk_sp backing_store) { - if (!IsValid()) { - FML_LOG(ERROR) << "Tried to present an invalid software surface."; - return false; - } - - SkPixmap pixmap; - if (!backing_store->peekPixels(&pixmap)) { - FML_LOG(ERROR) << "Could not peek the pixels of the backing store."; - return false; - } - - // Some basic sanity checking. - uint64_t expected_pixmap_data_size = pixmap.width() * pixmap.height() * 4; - - const size_t pixmap_size = pixmap.computeByteSize(); - - if (expected_pixmap_data_size != pixmap_size) { - FML_LOG(ERROR) << "Software backing store had unexpected size."; - return false; - } - - return software_dispatch_table_.software_present_backing_store( - pixmap.addr(), // - pixmap.rowBytes(), // - pixmap.height() // - ); -} - } // namespace flutter diff --git a/shell/platform/embedder/embedder_surface_software.h b/shell/platform/embedder/embedder_surface_software.h index 5d52e63d76528..6d938b6627f29 100644 --- a/shell/platform/embedder/embedder_surface_software.h +++ b/shell/platform/embedder/embedder_surface_software.h @@ -8,47 +8,33 @@ #include "flutter/fml/macros.h" #include "flutter/shell/gpu/gpu_surface_software.h" #include "flutter/shell/platform/embedder/embedder_external_view_embedder.h" +#include "flutter/shell/platform/embedder/embedder_studio_software.h" #include "flutter/shell/platform/embedder/embedder_surface.h" #include "third_party/skia/include/core/SkSurface.h" namespace flutter { -class EmbedderSurfaceSoftware final : public EmbedderSurface, - public GPUSurfaceSoftwareDelegate { +class EmbedderSurfaceSoftware final : public EmbedderSurface { public: - struct SoftwareDispatchTable { - std::function - software_present_backing_store; // required - }; - - EmbedderSurfaceSoftware( - SoftwareDispatchTable software_dispatch_table, - std::shared_ptr external_view_embedder); + EmbedderSurfaceSoftware(EmbedderStudioSoftware* studio, + bool render_to_surface); ~EmbedderSurfaceSoftware() override; - private: - bool valid_ = false; - SoftwareDispatchTable software_dispatch_table_; - sk_sp sk_surface_; - std::shared_ptr external_view_embedder_; - // |EmbedderSurface| bool IsValid() const override; + private: + EmbedderStudioSoftware* studio_; + bool render_to_surface_; + // |EmbedderSurface| std::unique_ptr CreateGPUSurface() override; // |EmbedderSurface| sk_sp CreateResourceContext() const override; - // |GPUSurfaceSoftwareDelegate| - sk_sp AcquireBackingStore(const SkISize& size) override; - - // |GPUSurfaceSoftwareDelegate| - bool PresentBackingStore(sk_sp backing_store) override; - FML_DISALLOW_COPY_AND_ASSIGN(EmbedderSurfaceSoftware); }; diff --git a/shell/platform/embedder/embedder_surface_vulkan.cc b/shell/platform/embedder/embedder_surface_vulkan.cc index d7191770025de..09e55b309281a 100644 --- a/shell/platform/embedder/embedder_surface_vulkan.cc +++ b/shell/platform/embedder/embedder_surface_vulkan.cc @@ -18,160 +18,29 @@ namespace flutter { EmbedderSurfaceVulkan::EmbedderSurfaceVulkan( - uint32_t version, - VkInstance instance, - size_t instance_extension_count, - const char** instance_extensions, - size_t device_extension_count, - const char** device_extensions, - VkPhysicalDevice physical_device, - VkDevice device, - uint32_t queue_family_index, - VkQueue queue, - const VulkanDispatchTable& vulkan_dispatch_table, - std::shared_ptr external_view_embedder) - : vk_(fml::MakeRefCounted( - vulkan_dispatch_table.get_instance_proc_address)), - device_(*vk_, - vulkan::VulkanHandle{physical_device}, - vulkan::VulkanHandle{device}, - queue_family_index, - vulkan::VulkanHandle{queue}), - vulkan_dispatch_table_(vulkan_dispatch_table), - external_view_embedder_(std::move(external_view_embedder)) { - // Make sure all required members of the dispatch table are checked. - if (!vulkan_dispatch_table_.get_instance_proc_address || - !vulkan_dispatch_table_.get_next_image || - !vulkan_dispatch_table_.present_image) { - return; - } + EmbedderStudioVulkan* studio, + sk_sp main_context, + bool render_to_surface) + : main_context_(main_context), + studio_(studio), + render_to_surface_(render_to_surface) {} - bool success = vk_->SetupInstanceProcAddresses( - vulkan::VulkanHandle{instance}); - if (!success) { - FML_LOG(ERROR) << "Could not setup instance proc addresses."; - return; - } - success = - vk_->SetupDeviceProcAddresses(vulkan::VulkanHandle{device}); - if (!success) { - FML_LOG(ERROR) << "Could not setup device proc addresses."; - return; - } - if (!vk_->IsValid()) { - FML_LOG(ERROR) << "VulkanProcTable invalid."; - return; - } - - main_context_ = CreateGrContext(instance, version, instance_extension_count, - instance_extensions, device_extension_count, - device_extensions, ContextType::kRender); - // TODO(96954): Add a second (optional) queue+family index to the Embedder API - // to allow embedders to specify a dedicated transfer queue for - // use by the resource context. Queue families with graphics - // capability can always be used for memory transferring, but it - // would be advantageous to use a dedicated transter queue here. - resource_context_ = CreateGrContext( - instance, version, instance_extension_count, instance_extensions, - device_extension_count, device_extensions, ContextType::kResource); - - valid_ = main_context_ && resource_context_; -} - -EmbedderSurfaceVulkan::~EmbedderSurfaceVulkan() { - if (main_context_) { - main_context_->releaseResourcesAndAbandonContext(); - } - if (resource_context_) { - resource_context_->releaseResourcesAndAbandonContext(); - } -} - -// |GPUSurfaceVulkanDelegate| -const vulkan::VulkanProcTable& EmbedderSurfaceVulkan::vk() { - return *vk_; -} - -// |GPUSurfaceVulkanDelegate| -FlutterVulkanImage EmbedderSurfaceVulkan::AcquireImage(const SkISize& size) { - return vulkan_dispatch_table_.get_next_image(size); -} - -// |GPUSurfaceVulkanDelegate| -bool EmbedderSurfaceVulkan::PresentImage(VkImage image, VkFormat format) { - return vulkan_dispatch_table_.present_image(image, format); -} +EmbedderSurfaceVulkan::~EmbedderSurfaceVulkan() {} // |EmbedderSurface| bool EmbedderSurfaceVulkan::IsValid() const { - return valid_; + return studio_->IsValid(); } // |EmbedderSurface| std::unique_ptr EmbedderSurfaceVulkan::CreateGPUSurface() { - const bool render_to_surface = !external_view_embedder_; - return std::make_unique(this, main_context_, - render_to_surface); + return std::make_unique(studio_, main_context_, + render_to_surface_); } // |EmbedderSurface| sk_sp EmbedderSurfaceVulkan::CreateResourceContext() const { - return resource_context_; -} - -sk_sp EmbedderSurfaceVulkan::CreateGrContext( - VkInstance instance, - uint32_t version, - size_t instance_extension_count, - const char** instance_extensions, - size_t device_extension_count, - const char** device_extensions, - ContextType context_type) const { - uint32_t skia_features = 0; - if (!device_.GetPhysicalDeviceFeaturesSkia(&skia_features)) { - FML_LOG(ERROR) << "Failed to get physical device features."; - - return nullptr; - } - - auto get_proc = CreateSkiaGetProc(vk_); - if (get_proc == nullptr) { - FML_LOG(ERROR) << "Failed to create Vulkan getProc for Skia."; - return nullptr; - } - - GrVkExtensions extensions; - - GrVkBackendContext backend_context = {}; - backend_context.fInstance = instance; - backend_context.fPhysicalDevice = device_.GetPhysicalDeviceHandle(); - backend_context.fDevice = device_.GetHandle(); - backend_context.fQueue = device_.GetQueueHandle(); - backend_context.fGraphicsQueueIndex = device_.GetGraphicsQueueIndex(); - backend_context.fMinAPIVersion = version; - backend_context.fMaxAPIVersion = version; - backend_context.fFeatures = skia_features; - backend_context.fVkExtensions = &extensions; - backend_context.fGetProc = get_proc; - backend_context.fOwnsInstanceAndDevice = false; - - uint32_t vulkan_api_version = version; - sk_sp allocator = - flutter::FlutterSkiaVulkanMemoryAllocator::Make( - vulkan_api_version, instance, device_.GetPhysicalDeviceHandle(), - device_.GetHandle(), vk_, true); - - backend_context.fMemoryAllocator = allocator; - - extensions.init(backend_context.fGetProc, backend_context.fInstance, - backend_context.fPhysicalDevice, instance_extension_count, - instance_extensions, device_extension_count, - device_extensions); - - GrContextOptions options = - MakeDefaultContextOptions(context_type, GrBackendApi::kVulkan); - options.fReduceOpsTaskSplitting = GrContextOptions::Enable::kNo; - return GrDirectContext::MakeVulkan(backend_context, options); + return studio_->CreateResourceContext(); } } // namespace flutter diff --git a/shell/platform/embedder/embedder_surface_vulkan.h b/shell/platform/embedder/embedder_surface_vulkan.h index 0e972d3723b74..f26a44591d407 100644 --- a/shell/platform/embedder/embedder_surface_vulkan.h +++ b/shell/platform/embedder/embedder_surface_vulkan.h @@ -11,58 +11,27 @@ #include "flutter/shell/gpu/gpu_surface_vulkan_delegate.h" #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/embedder/embedder_external_view_embedder.h" +#include "flutter/shell/platform/embedder/embedder_studio_vulkan.h" #include "flutter/shell/platform/embedder/embedder_surface.h" #include "flutter/vulkan/procs/vulkan_proc_table.h" namespace flutter { -class EmbedderSurfaceVulkan final : public EmbedderSurface, - public GPUSurfaceVulkanDelegate { +class EmbedderSurfaceVulkan final : public EmbedderSurface { public: - struct VulkanDispatchTable { - PFN_vkGetInstanceProcAddr get_instance_proc_address; // required - std::function - get_next_image; // required - std::function - present_image; // required - }; - - EmbedderSurfaceVulkan( - uint32_t version, - VkInstance instance, - size_t instance_extension_count, - const char** instance_extensions, - size_t device_extension_count, - const char** device_extensions, - VkPhysicalDevice physical_device, - VkDevice device, - uint32_t queue_family_index, - VkQueue queue, - const VulkanDispatchTable& vulkan_dispatch_table, - std::shared_ptr external_view_embedder); + EmbedderSurfaceVulkan(EmbedderStudioVulkan* studio, + sk_sp main_context, + bool render_to_surface); ~EmbedderSurfaceVulkan() override; - // |GPUSurfaceVulkanDelegate| - const vulkan::VulkanProcTable& vk() override; - - // |GPUSurfaceVulkanDelegate| - FlutterVulkanImage AcquireImage(const SkISize& size) override; - - // |GPUSurfaceVulkanDelegate| - bool PresentImage(VkImage image, VkFormat format) override; + // |EmbedderSurface| + bool IsValid() const override; private: - bool valid_ = false; - fml::RefPtr vk_; - vulkan::VulkanDevice device_; - VulkanDispatchTable vulkan_dispatch_table_; - std::shared_ptr external_view_embedder_; sk_sp main_context_; - sk_sp resource_context_; - - // |EmbedderSurface| - bool IsValid() const override; + EmbedderStudioVulkan* studio_; + bool render_to_surface_; // |EmbedderSurface| std::unique_ptr CreateGPUSurface() override; diff --git a/shell/platform/embedder/platform_view_embedder.h b/shell/platform/embedder/platform_view_embedder.h index d870268cc4e7f..3e30a33101b5b 100644 --- a/shell/platform/embedder/platform_view_embedder.h +++ b/shell/platform/embedder/platform_view_embedder.h @@ -14,20 +14,20 @@ #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/embedder/embedder_studio.h" #include "flutter/shell/platform/embedder/embedder_surface.h" -#include "flutter/shell/platform/embedder/embedder_surface_software.h" +// #include "flutter/shell/platform/embedder/embedder_surface_software.h" #include "flutter/shell/platform/embedder/vsync_waiter_embedder.h" -#ifdef SHELL_ENABLE_GL -#include "flutter/shell/platform/embedder/embedder_surface_gl.h" -#endif +// #ifdef SHELL_ENABLE_GL +// #include "flutter/shell/platform/embedder/embedder_surface_gl.h" +// #endif -#ifdef SHELL_ENABLE_METAL -#include "flutter/shell/platform/embedder/embedder_surface_metal.h" -#endif +// #ifdef SHELL_ENABLE_METAL +// #include "flutter/shell/platform/embedder/embedder_surface_metal.h" +// #endif -#ifdef SHELL_ENABLE_VULKAN -#include "flutter/shell/platform/embedder/embedder_surface_vulkan.h" -#endif +// #ifdef SHELL_ENABLE_VULKAN +// #include "flutter/shell/platform/embedder/embedder_surface_vulkan.h" +// #endif namespace flutter { diff --git a/shell/platform/embedder/platform_view_embedder_unittests.cc b/shell/platform/embedder/platform_view_embedder_unittests.cc index 3735e97eca2ea..04b4fb804b871 100644 --- a/shell/platform/embedder/platform_view_embedder_unittests.cc +++ b/shell/platform/embedder/platform_view_embedder_unittests.cc @@ -61,8 +61,7 @@ class MockResponse : public PlatformMessageResponse { class MockStudio : public EmbedderStudio { public: - MockStudio() : EmbedderStudio(nullptr) {} - + MOCK_CONST_METHOD0(IsValid, bool()); MOCK_METHOD0(CreateSurface, std::unique_ptr()); MOCK_CONST_METHOD0(CreateResourceContext, sk_sp()); }; @@ -77,7 +76,6 @@ TEST(PlatformViewEmbedderTest, HasPlatformMessageHandler) { fml::AutoResetWaitableEvent latch; task_runners.GetPlatformTaskRunner()->PostTask([&latch, task_runners] { MockDelegate delegate; - EmbedderSurfaceSoftware::SoftwareDispatchTable software_dispatch_table; PlatformViewEmbedder::PlatformDispatchTable platform_dispatch_table; std::shared_ptr external_view_embedder; auto embedder = std::make_unique( @@ -143,28 +141,27 @@ TEST(PlatformViewEmbedderTest, DeletionDisabledDispatch) { bool did_call = false; { fml::AutoResetWaitableEvent latch; - task_runners.GetPlatformTaskRunner()->PostTask([&latch, task_runners, - &did_call] { - MockDelegate delegate; - EmbedderSurfaceSoftware::SoftwareDispatchTable software_dispatch_table; - PlatformViewEmbedder::PlatformDispatchTable platform_dispatch_table; - platform_dispatch_table.platform_message_response_callback = - [&did_call](std::unique_ptr message) { - did_call = true; - }; - std::shared_ptr external_view_embedder; - auto embedder = std::make_unique( - delegate, task_runners, std::make_unique(), - platform_dispatch_table, external_view_embedder); - auto platform_message_handler = embedder->GetPlatformMessageHandler(); - fml::RefPtr response = - fml::MakeRefCounted(); - std::unique_ptr message = - std::make_unique("foo", response); - platform_message_handler->HandlePlatformMessage(std::move(message)); - embedder.reset(); - latch.Signal(); - }); + task_runners.GetPlatformTaskRunner()->PostTask( + [&latch, task_runners, &did_call] { + MockDelegate delegate; + PlatformViewEmbedder::PlatformDispatchTable platform_dispatch_table; + platform_dispatch_table.platform_message_response_callback = + [&did_call](std::unique_ptr message) { + did_call = true; + }; + std::shared_ptr external_view_embedder; + auto embedder = std::make_unique( + delegate, task_runners, std::make_unique(), + platform_dispatch_table, external_view_embedder); + auto platform_message_handler = embedder->GetPlatformMessageHandler(); + fml::RefPtr response = + fml::MakeRefCounted(); + std::unique_ptr message = + std::make_unique("foo", response); + platform_message_handler->HandlePlatformMessage(std::move(message)); + embedder.reset(); + latch.Signal(); + }); latch.Wait(); } { From 424d9a73184526b5429d5060aeb1b0b4444449be Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 17 Mar 2023 14:18:42 -0700 Subject: [PATCH 013/147] Format --- shell/platform/embedder/embedder_studio_gl.cc | 2 +- shell/platform/embedder/embedder_studio_gl.h | 2 +- shell/platform/embedder/embedder_studio_metal.h | 2 +- shell/platform/embedder/embedder_studio_metal.mm | 9 ++++----- shell/platform/embedder/embedder_studio_software.cc | 3 ++- shell/platform/embedder/embedder_studio_software.h | 2 +- shell/platform/embedder/embedder_studio_vulkan.cc | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/shell/platform/embedder/embedder_studio_gl.cc b/shell/platform/embedder/embedder_studio_gl.cc index d89a1a69bd1f0..1f03fc815787f 100644 --- a/shell/platform/embedder/embedder_studio_gl.cc +++ b/shell/platform/embedder/embedder_studio_gl.cc @@ -6,8 +6,8 @@ #include -#include "flutter/shell/platform/embedder/embedder_surface_gl.h" #include "flutter/shell/common/shell_io_manager.h" +#include "flutter/shell/platform/embedder/embedder_surface_gl.h" namespace flutter { diff --git a/shell/platform/embedder/embedder_studio_gl.h b/shell/platform/embedder/embedder_studio_gl.h index 1a0b227ad918d..65ebda6855bcc 100644 --- a/shell/platform/embedder/embedder_studio_gl.h +++ b/shell/platform/embedder/embedder_studio_gl.h @@ -13,7 +13,7 @@ namespace flutter { class EmbedderStudioGL final : public EmbedderStudio, - public GPUSurfaceGLDelegate { + public GPUSurfaceGLDelegate { public: struct GLDispatchTable { std::function gl_make_current_callback; // required diff --git a/shell/platform/embedder/embedder_studio_metal.h b/shell/platform/embedder/embedder_studio_metal.h index 8dd0ab25b288c..db1d0bbf1a4be 100644 --- a/shell/platform/embedder/embedder_studio_metal.h +++ b/shell/platform/embedder/embedder_studio_metal.h @@ -16,7 +16,7 @@ namespace flutter { class EmbedderStudioMetal final : public EmbedderStudio, - public GPUSurfaceMetalDelegate { + public GPUSurfaceMetalDelegate { public: struct MetalDispatchTable { std::function present; // required diff --git a/shell/platform/embedder/embedder_studio_metal.mm b/shell/platform/embedder/embedder_studio_metal.mm index 7400e454af1ff..87ef747734042 100644 --- a/shell/platform/embedder/embedder_studio_metal.mm +++ b/shell/platform/embedder/embedder_studio_metal.mm @@ -8,8 +8,8 @@ #include "flutter/fml/logging.h" #include "flutter/shell/gpu/gpu_surface_metal_delegate.h" -#include "flutter/shell/platform/embedder/embedder_surface_metal.h" #import "flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalSkia.h" +#include "flutter/shell/platform/embedder/embedder_surface_metal.h" #include "third_party/skia/include/gpu/GrDirectContext.h" FLUTTER_ASSERT_NOT_ARC @@ -43,10 +43,9 @@ return nullptr; } const bool render_to_surface = !external_view_embedder_; - return std::make_unique( - main_context_, - this, // GPU surface GL delegate - render_to_surface // render to surface + return std::make_unique(main_context_, + this, // GPU surface GL delegate + render_to_surface // render to surface ); } diff --git a/shell/platform/embedder/embedder_studio_software.cc b/shell/platform/embedder/embedder_studio_software.cc index a3d898b8c2b0d..eceeda79fee89 100644 --- a/shell/platform/embedder/embedder_studio_software.cc +++ b/shell/platform/embedder/embedder_studio_software.cc @@ -41,7 +41,8 @@ std::unique_ptr EmbedderStudioSoftware::CreateSurface() { return nullptr; } const bool render_to_surface = !external_view_embedder_; - auto surface = std::make_unique(this, render_to_surface); + auto surface = + std::make_unique(this, render_to_surface); if (!surface->IsValid()) { return nullptr; diff --git a/shell/platform/embedder/embedder_studio_software.h b/shell/platform/embedder/embedder_studio_software.h index 7be884601f292..ce9ba6f9f8182 100644 --- a/shell/platform/embedder/embedder_studio_software.h +++ b/shell/platform/embedder/embedder_studio_software.h @@ -15,7 +15,7 @@ namespace flutter { class EmbedderStudioSoftware final : public EmbedderStudio, - public GPUSurfaceSoftwareDelegate { + public GPUSurfaceSoftwareDelegate { public: struct SoftwareDispatchTable { std::function diff --git a/shell/platform/embedder/embedder_studio_vulkan.cc b/shell/platform/embedder/embedder_studio_vulkan.cc index dad77ce3b3ec5..ba61e2217a209 100644 --- a/shell/platform/embedder/embedder_studio_vulkan.cc +++ b/shell/platform/embedder/embedder_studio_vulkan.cc @@ -9,8 +9,8 @@ #include "flutter/flutter_vma/flutter_skia_vma.h" #include "flutter/shell/common/shell_io_manager.h" #include "flutter/shell/gpu/gpu_surface_vulkan.h" -#include "flutter/shell/platform/embedder/embedder_surface_vulkan.h" #include "flutter/shell/gpu/gpu_surface_vulkan_delegate.h" +#include "flutter/shell/platform/embedder/embedder_surface_vulkan.h" #include "flutter/vulkan/vulkan_skia_proc_table.h" #include "include/gpu/GrDirectContext.h" #include "include/gpu/vk/GrVkBackendContext.h" @@ -112,7 +112,7 @@ bool EmbedderStudioVulkan::IsValid() const { std::unique_ptr EmbedderStudioVulkan::CreateSurface() { const bool render_to_surface = !external_view_embedder_; return std::make_unique(this, main_context_, - render_to_surface); + render_to_surface); } // |EmbedderStudio| From beb6db9935fedb80af114b9c167539155493f848 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 17 Mar 2023 14:48:38 -0700 Subject: [PATCH 014/147] conditional import --- shell/gpu/gpu_surface_gl_skia.h | 1 - shell/platform/embedder/embedder.cc | 10 ++++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/shell/gpu/gpu_surface_gl_skia.h b/shell/gpu/gpu_surface_gl_skia.h index 2b40e53f27055..1cbb4c50ecd29 100644 --- a/shell/gpu/gpu_surface_gl_skia.h +++ b/shell/gpu/gpu_surface_gl_skia.h @@ -70,7 +70,6 @@ class GPUSurfaceGLSkia : public Surface { // still have an option of overriding this damage with their own in // `GLContextFrameBufferInfo`. std::optional existing_damage_ = std::nullopt; - bool context_owner_ = false; // TODO(38466): Refactor GPU surface APIs take into account the fact that an // external view embedder may want to render to the root surface. This is a // hack to make avoid allocating resources for the root surface when an diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index b650355d4bcc1..573661ff1a674 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -54,10 +54,7 @@ extern const intptr_t kPlatformStrongDillSize; #include "flutter/shell/platform/embedder/embedder_platform_message_response.h" #include "flutter/shell/platform/embedder/embedder_render_target.h" #include "flutter/shell/platform/embedder/embedder_struct_macros.h" -#include "flutter/shell/platform/embedder/embedder_studio_gl.h" -#include "flutter/shell/platform/embedder/embedder_studio_metal.h" #include "flutter/shell/platform/embedder/embedder_studio_software.h" -#include "flutter/shell/platform/embedder/embedder_studio_vulkan.h" #include "flutter/shell/platform/embedder/embedder_task_runner.h" #include "flutter/shell/platform/embedder/embedder_thread_host.h" #include "flutter/shell/platform/embedder/pixel_formats.h" @@ -67,10 +64,15 @@ extern const intptr_t kPlatformStrongDillSize; #ifdef SHELL_ENABLE_GL #include "flutter/shell/platform/embedder/embedder_external_texture_gl.h" +#include "flutter/shell/platform/embedder/embedder_studio_gl.h" #endif #ifdef SHELL_ENABLE_METAL -#include "flutter/shell/platform/embedder/embedder_surface_metal.h" +#include "flutter/shell/platform/embedder/embedder_studio_metal.h" +#endif + +#ifdef SHELL_ENABLE_VULKAN +#include "flutter/shell/platform/embedder/embedder_studio_vulkan.h" #endif const int32_t kFlutterSemanticsNodeIdBatchEnd = -1; From 65f2fe7178ad0b8d576c037d6b4be3ff8fde911d Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 17 Mar 2023 18:44:35 -0700 Subject: [PATCH 015/147] lint --- shell/platform/embedder/embedder_surface_gl.cc | 2 +- shell/platform/embedder/embedder_surface_metal.mm | 4 +++- shell/platform/embedder/embedder_surface_vulkan.cc | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/shell/platform/embedder/embedder_surface_gl.cc b/shell/platform/embedder/embedder_surface_gl.cc index 929f0e5092d95..6159a15e68af5 100644 --- a/shell/platform/embedder/embedder_surface_gl.cc +++ b/shell/platform/embedder/embedder_surface_gl.cc @@ -14,7 +14,7 @@ namespace flutter { EmbedderSurfaceGL::EmbedderSurfaceGL(sk_sp main_context, EmbedderStudioGL* studio, bool render_to_surface) - : main_context_(main_context), + : main_context_(std::move(main_context)), studio_(studio), render_to_surface_(render_to_surface) {} diff --git a/shell/platform/embedder/embedder_surface_metal.mm b/shell/platform/embedder/embedder_surface_metal.mm index b3cbd6c96a7ad..8560c2e5d6174 100644 --- a/shell/platform/embedder/embedder_surface_metal.mm +++ b/shell/platform/embedder/embedder_surface_metal.mm @@ -18,7 +18,9 @@ EmbedderSurfaceMetal::EmbedderSurfaceMetal(sk_sp main_context, EmbedderStudioMetal* studio, bool render_to_surface) - : main_context_(main_context), studio_(studio), render_to_surface_(render_to_surface) {} + : main_context_(std::move(main_context)), + studio_(studio), + render_to_surface_(render_to_surface) {} EmbedderSurfaceMetal::~EmbedderSurfaceMetal() = default; diff --git a/shell/platform/embedder/embedder_surface_vulkan.cc b/shell/platform/embedder/embedder_surface_vulkan.cc index 09e55b309281a..de92ed3babc0c 100644 --- a/shell/platform/embedder/embedder_surface_vulkan.cc +++ b/shell/platform/embedder/embedder_surface_vulkan.cc @@ -21,7 +21,7 @@ EmbedderSurfaceVulkan::EmbedderSurfaceVulkan( EmbedderStudioVulkan* studio, sk_sp main_context, bool render_to_surface) - : main_context_(main_context), + : main_context_(std::move(main_context)), studio_(studio), render_to_surface_(render_to_surface) {} From 4ef438c8bc451f79a89be2c7db442de8f645856c Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Sun, 19 Mar 2023 23:30:30 -0700 Subject: [PATCH 016/147] Add gpu studios --- ci/licenses_golden/licenses_flutter | 28 ++++ flow/studio.cc | 33 +++++ flow/studio.h | 47 +++++++ shell/gpu/BUILD.gn | 14 ++ shell/gpu/gpu_studio_gl_impeller.cc | 83 ++++++++++++ shell/gpu/gpu_studio_gl_impeller.h | 60 +++++++++ shell/gpu/gpu_studio_gl_skia.cc | 171 ++++++++++++++++++++++++ shell/gpu/gpu_studio_gl_skia.h | 74 ++++++++++ shell/gpu/gpu_studio_metal_impeller.h | 58 ++++++++ shell/gpu/gpu_studio_metal_impeller.mm | 109 +++++++++++++++ shell/gpu/gpu_studio_metal_skia.h | 62 +++++++++ shell/gpu/gpu_studio_metal_skia.mm | 106 +++++++++++++++ shell/gpu/gpu_studio_software.cc | 34 +++++ shell/gpu/gpu_studio_software.h | 41 ++++++ shell/gpu/gpu_studio_vulkan.cc | 48 +++++++ shell/gpu/gpu_studio_vulkan.h | 59 ++++++++ shell/gpu/gpu_studio_vulkan_impeller.cc | 68 ++++++++++ shell/gpu/gpu_studio_vulkan_impeller.h | 49 +++++++ 18 files changed, 1144 insertions(+) create mode 100644 flow/studio.cc create mode 100644 flow/studio.h create mode 100644 shell/gpu/gpu_studio_gl_impeller.cc create mode 100644 shell/gpu/gpu_studio_gl_impeller.h create mode 100644 shell/gpu/gpu_studio_gl_skia.cc create mode 100644 shell/gpu/gpu_studio_gl_skia.h create mode 100644 shell/gpu/gpu_studio_metal_impeller.h create mode 100644 shell/gpu/gpu_studio_metal_impeller.mm create mode 100644 shell/gpu/gpu_studio_metal_skia.h create mode 100644 shell/gpu/gpu_studio_metal_skia.mm create mode 100644 shell/gpu/gpu_studio_software.cc create mode 100644 shell/gpu/gpu_studio_software.h create mode 100644 shell/gpu/gpu_studio_vulkan.cc create mode 100644 shell/gpu/gpu_studio_vulkan.h create mode 100644 shell/gpu/gpu_studio_vulkan_impeller.cc create mode 100644 shell/gpu/gpu_studio_vulkan_impeller.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index c78d25f37c0df..45beef38f4281 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -2115,6 +2115,20 @@ ORIGIN: ../../../flutter/shell/common/vsync_waiter_fallback.cc + ../../../flutte ORIGIN: ../../../flutter/shell/common/vsync_waiter_fallback.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/vsync_waiters_test.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/vsync_waiters_test.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/gpu/gpu_studio_gl_impeller.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/gpu/gpu_studio_gl_impeller.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/gpu/gpu_studio_gl_skia.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/gpu/gpu_studio_gl_skia.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/gpu/gpu_studio_metal_impeller.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/gpu/gpu_studio_metal_impeller.mm + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/gpu/gpu_studio_metal_skia.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/gpu/gpu_studio_metal_skia.mm + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/gpu/gpu_studio_software.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/gpu/gpu_studio_software.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/gpu/gpu_studio_vulkan.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/gpu/gpu_studio_vulkan.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/gpu/gpu_studio_vulkan_impeller.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/gpu/gpu_studio_vulkan_impeller.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/gpu/gpu_surface_gl_delegate.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/gpu/gpu_surface_gl_delegate.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/gpu/gpu_surface_gl_impeller.cc + ../../../flutter/LICENSE @@ -4659,6 +4673,20 @@ FILE: ../../../flutter/shell/common/vsync_waiter_fallback.cc FILE: ../../../flutter/shell/common/vsync_waiter_fallback.h FILE: ../../../flutter/shell/common/vsync_waiters_test.cc FILE: ../../../flutter/shell/common/vsync_waiters_test.h +FILE: ../../../flutter/shell/gpu/gpu_studio_gl_impeller.cc +FILE: ../../../flutter/shell/gpu/gpu_studio_gl_impeller.h +FILE: ../../../flutter/shell/gpu/gpu_studio_gl_skia.cc +FILE: ../../../flutter/shell/gpu/gpu_studio_gl_skia.h +FILE: ../../../flutter/shell/gpu/gpu_studio_metal_impeller.h +FILE: ../../../flutter/shell/gpu/gpu_studio_metal_impeller.mm +FILE: ../../../flutter/shell/gpu/gpu_studio_metal_skia.h +FILE: ../../../flutter/shell/gpu/gpu_studio_metal_skia.mm +FILE: ../../../flutter/shell/gpu/gpu_studio_software.cc +FILE: ../../../flutter/shell/gpu/gpu_studio_software.h +FILE: ../../../flutter/shell/gpu/gpu_studio_vulkan.cc +FILE: ../../../flutter/shell/gpu/gpu_studio_vulkan.h +FILE: ../../../flutter/shell/gpu/gpu_studio_vulkan_impeller.cc +FILE: ../../../flutter/shell/gpu/gpu_studio_vulkan_impeller.h FILE: ../../../flutter/shell/gpu/gpu_surface_gl_delegate.cc FILE: ../../../flutter/shell/gpu/gpu_surface_gl_delegate.h FILE: ../../../flutter/shell/gpu/gpu_surface_gl_impeller.cc diff --git a/flow/studio.cc b/flow/studio.cc new file mode 100644 index 0000000000000..879281c59cf00 --- /dev/null +++ b/flow/studio.cc @@ -0,0 +1,33 @@ +// 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. + +#include "flutter/flow/studio.h" + +namespace flutter { + +Studio::Studio() = default; + +Studio::~Studio() = default; + +std::unique_ptr Studio::MakeRenderContextCurrent() { + return std::make_unique(true); +} + +bool Studio::ClearRenderContext() { + return false; +} + +bool Studio::AllowsDrawingWhenGpuDisabled() const { + return true; +} + +bool Studio::EnableRasterCache() const { + return true; +} + +impeller::AiksContext* Studio::GetAiksContext() const { + return nullptr; +} + +} // namespace flutter diff --git a/flow/studio.h b/flow/studio.h new file mode 100644 index 0000000000000..7c746d1e7796a --- /dev/null +++ b/flow/studio.h @@ -0,0 +1,47 @@ +// 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_FLOW_STUDIO_H_ +#define FLUTTER_FLOW_STUDIO_H_ + +#include + +#include "flutter/common/graphics/gl_context_switch.h" +#include "flutter/fml/macros.h" + +class GrDirectContext; + +namespace impeller { +class AiksContext; +} // namespace impeller + +namespace flutter { + +class Studio { + public: + Studio(); + + virtual ~Studio(); + + virtual bool IsValid() = 0; + + virtual GrDirectContext* GetContext() = 0; + + virtual std::unique_ptr MakeRenderContextCurrent(); + + virtual bool ClearRenderContext(); + + virtual bool AllowsDrawingWhenGpuDisabled() const; + + virtual bool EnableRasterCache() const; + + virtual impeller::AiksContext* GetAiksContext() const; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(Studio); +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_STUDIO_H_ diff --git a/shell/gpu/BUILD.gn b/shell/gpu/BUILD.gn index 5e5c1cce53a6c..ec7b2a25b3530 100644 --- a/shell/gpu/BUILD.gn +++ b/shell/gpu/BUILD.gn @@ -17,6 +17,8 @@ gpu_common_deps = [ source_set("gpu_surface_software") { sources = [ + "gpu_studio_software.cc", + "gpu_studio_software.h", "gpu_surface_software.cc", "gpu_surface_software.h", "gpu_surface_software_delegate.cc", @@ -28,6 +30,8 @@ source_set("gpu_surface_software") { source_set("gpu_surface_gl") { sources = [ + "gpu_studio_gl_skia.cc", + "gpu_studio_gl_skia.h", "gpu_surface_gl_delegate.cc", "gpu_surface_gl_delegate.h", "gpu_surface_gl_skia.cc", @@ -38,6 +42,8 @@ source_set("gpu_surface_gl") { if (impeller_enable_opengles) { sources += [ + "gpu_studio_gl_impeller.cc", + "gpu_studio_gl_impeller.h", "gpu_surface_gl_impeller.cc", "gpu_surface_gl_impeller.h", ] @@ -48,6 +54,8 @@ source_set("gpu_surface_gl") { source_set("gpu_surface_vulkan") { sources = [ + "gpu_studio_vulkan.cc", + "gpu_studio_vulkan.h", "gpu_surface_vulkan.cc", "gpu_surface_vulkan.h", "gpu_surface_vulkan_delegate.cc", @@ -61,6 +69,8 @@ source_set("gpu_surface_vulkan") { if (impeller_enable_vulkan) { sources += [ + "gpu_studio_vulkan_impeller.cc", + "gpu_studio_vulkan_impeller.h", "gpu_surface_vulkan_impeller.cc", "gpu_surface_vulkan_impeller.h", ] @@ -71,6 +81,8 @@ source_set("gpu_surface_vulkan") { source_set("gpu_surface_metal") { sources = [ + "gpu_studio_metal_skia.h", + "gpu_studio_metal_skia.mm", "gpu_surface_metal_delegate.cc", "gpu_surface_metal_delegate.h", "gpu_surface_metal_skia.h", @@ -81,6 +93,8 @@ source_set("gpu_surface_metal") { if (impeller_enable_metal) { sources += [ + "gpu_studio_metal_impeller.h", + "gpu_studio_metal_impeller.mm", "gpu_surface_metal_impeller.h", "gpu_surface_metal_impeller.mm", ] diff --git a/shell/gpu/gpu_studio_gl_impeller.cc b/shell/gpu/gpu_studio_gl_impeller.cc new file mode 100644 index 0000000000000..c7b443f4efe0e --- /dev/null +++ b/shell/gpu/gpu_studio_gl_impeller.cc @@ -0,0 +1,83 @@ +// 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. + +#include "flutter/shell/gpu/gpu_studio_gl_impeller.h" + +#include "flutter/fml/make_copyable.h" +#include "flutter/impeller/display_list/display_list_dispatcher.h" +#include "flutter/impeller/renderer/backend/gles/surface_gles.h" +#include "flutter/impeller/renderer/renderer.h" + +namespace flutter { + +GPUStudioGLImpeller::GPUStudioGLImpeller( + GPUStudioGLDelegate* delegate, + std::shared_ptr context) + : weak_factory_(this) { + if (delegate == nullptr) { + return; + } + + if (!context || !context->IsValid()) { + return; + } + + auto renderer = std::make_shared(context); + if (!renderer->IsValid()) { + return; + } + + auto aiks_context = std::make_shared(context); + + if (!aiks_context->IsValid()) { + return; + } + + delegate_ = delegate; + impeller_context_ = std::move(context); + impeller_renderer_ = std::move(renderer); + aiks_context_ = std::move(aiks_context); + is_valid_ = true; +} + +// |Studio| +GPUStudioGLImpeller::~GPUStudioGLImpeller() = default; + +// |Studio| +bool GPUStudioGLImpeller::IsValid() { + return is_valid_; +} + +// |Studio| +GrDirectContext* GPUStudioGLImpeller::GetContext() { + // Impeller != Skia. + return nullptr; +} + +// |Studio| +std::unique_ptr +GPUStudioGLImpeller::MakeRenderContextCurrent() { + return delegate_->GLContextMakeCurrent(); +} + +// |Studio| +bool GPUStudioGLImpeller::ClearRenderContext() { + return delegate_->GLContextClearCurrent(); +} + +bool GPUStudioGLImpeller::AllowsDrawingWhenGpuDisabled() const { + return delegate_->AllowsDrawingWhenGpuDisabled(); +} + +// |Studio| +bool GPUStudioGLImpeller::EnableRasterCache() const { + return false; +} + +// |Studio| +impeller::AiksContext* GPUStudioGLImpeller::GetAiksContext() const { + return aiks_context_.get(); +} + +} // namespace flutter diff --git a/shell/gpu/gpu_studio_gl_impeller.h b/shell/gpu/gpu_studio_gl_impeller.h new file mode 100644 index 0000000000000..2397cc89a1fd3 --- /dev/null +++ b/shell/gpu/gpu_studio_gl_impeller.h @@ -0,0 +1,60 @@ +// 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 SHELL_GPU_GPU_SURFACE_GL_IMPELLER_H_ +#define SHELL_GPU_GPU_SURFACE_GL_IMPELLER_H_ + +#include "flutter/common/graphics/gl_context_switch.h" +#include "flutter/flow/surface.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/impeller/aiks/aiks_context.h" +#include "flutter/impeller/renderer/context.h" +#include "flutter/shell/gpu/gpu_surface_gl_delegate.h" + +namespace flutter { + +class GPUStudioGLImpeller final : public Studio { + public: + explicit GPUStudioGLImpeller(GPUStudioGLDelegate* delegate, + std::shared_ptr context); + + // |Studio| + ~GPUStudioGLImpeller() override; + + // |Studio| + bool IsValid() override; + + private: + GPUStudioGLDelegate* delegate_ = nullptr; + std::shared_ptr impeller_context_; + std::shared_ptr impeller_renderer_; + std::shared_ptr aiks_context_; + bool is_valid_ = false; + fml::WeakPtrFactory weak_factory_; + + // |Studio| + GrDirectContext* GetContext() override; + + // |Studio| + std::unique_ptr MakeRenderContextCurrent() override; + + // |Studio| + bool ClearRenderContext() override; + + // |Studio| + bool AllowsDrawingWhenGpuDisabled() const override; + + // |Studio| + bool EnableRasterCache() const override; + + // |Studio| + impeller::AiksContext* GetAiksContext() const override; + + FML_DISALLOW_COPY_AND_ASSIGN(GPUStudioGLImpeller); +}; + +} // namespace flutter + +#endif // SHELL_GPU_GPU_SURFACE_GL_IMPELLER_H_ diff --git a/shell/gpu/gpu_studio_gl_skia.cc b/shell/gpu/gpu_studio_gl_skia.cc new file mode 100644 index 0000000000000..227c9099175be --- /dev/null +++ b/shell/gpu/gpu_studio_gl_skia.cc @@ -0,0 +1,171 @@ +// 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. + +#include "flutter/shell/gpu/gpu_studio_gl_skia.h" + +#include "flutter/common/graphics/persistent_cache.h" +#include "flutter/fml/base32.h" +#include "flutter/fml/logging.h" +#include "flutter/fml/size.h" +#include "flutter/fml/trace_event.h" +#include "flutter/shell/common/context_options.h" +#include "flutter/shell/gpu/gpu_surface_gl_delegate.h" +#include "third_party/skia/include/core/SkAlphaType.h" +#include "third_party/skia/include/core/SkColorFilter.h" +#include "third_party/skia/include/core/SkColorSpace.h" +#include "third_party/skia/include/core/SkColorType.h" +#include "third_party/skia/include/core/SkStudio.h" +#include "third_party/skia/include/gpu/GrBackendStudio.h" +#include "third_party/skia/include/gpu/GrContextOptions.h" + +// These are common defines present on all OpenGL headers. However, we don't +// want to perform GL header reasolution on each platform we support. So just +// define these upfront. It is unlikely we will need more. But, if we do, we can +// add the same here. +#define GPU_GL_RGBA8 0x8058 +#define GPU_GL_RGBA4 0x8056 +#define GPU_GL_RGB565 0x8D62 + +namespace flutter { + +// Default maximum number of bytes of GPU memory of budgeted resources in the +// cache. +// The shell will dynamically increase or decrease this cache based on the +// viewport size, unless a user has specifically requested a size on the Skia +// system channel. +static const size_t kGrCacheMaxByteSize = 24 * (1 << 20); + +sk_sp GPUStudioGLSkia::MakeGLContext( + GPUStudioGLDelegate* delegate) { + auto context_switch = delegate->GLContextMakeCurrent(); + if (!context_switch->GetResult()) { + FML_LOG(ERROR) + << "Could not make the context current to set up the Gr context."; + return nullptr; + } + + const auto options = + MakeDefaultContextOptions(ContextType::kRender, GrBackendApi::kOpenGL); + + auto context = GrDirectContext::MakeGL(delegate->GetGLInterface(), options); + + if (!context) { + FML_LOG(ERROR) << "Failed to set up Skia Gr context."; + return nullptr; + } + + context->setResourceCacheLimit(kGrCacheMaxByteSize); + + PersistentCache::GetCacheForProcess()->PrecompileKnownSkSLs(context.get()); + + return context; +} + +GPUStudioGLSkia::GPUStudioGLSkia(const sk_sp& gr_context, + GPUStudioGLDelegate* delegate, + bool render_to_surface) + : delegate_(delegate), + context_(gr_context), + + render_to_surface_(render_to_surface), + weak_factory_(this) { + auto context_switch = delegate_->GLContextMakeCurrent(); + if (!context_switch->GetResult()) { + FML_LOG(ERROR) + << "Could not make the context current to set up the Gr context."; + return; + } + + delegate_->GLContextClearCurrent(); + + valid_ = gr_context != nullptr; +} + +GPUStudioGLSkia::~GPUStudioGLSkia() { + if (!valid_) { + return; + } + auto context_switch = delegate_->GLContextMakeCurrent(); + if (!context_switch->GetResult()) { + FML_LOG(ERROR) << "Could not make the context current to destroy the " + "GrDirectContext resources."; + return; + } + + onscreen_surface_ = nullptr; + fbo_id_ = 0; + context_ = nullptr; + + delegate_->GLContextClearCurrent(); +} + +// |Studio| +bool GPUStudioGLSkia::IsValid() { + return valid_; +} + +static SkColorType FirstSupportedColorType(GrDirectContext* context, + GrGLenum* format) { +#define RETURN_IF_RENDERABLE(x, y) \ + if (context->colorTypeSupportedAsStudio((x))) { \ + *format = (y); \ + return (x); \ + } + RETURN_IF_RENDERABLE(kRGBA_8888_SkColorType, GPU_GL_RGBA8); + RETURN_IF_RENDERABLE(kARGB_4444_SkColorType, GPU_GL_RGBA4); + RETURN_IF_RENDERABLE(kRGB_565_SkColorType, GPU_GL_RGB565); + return kUnknown_SkColorType; +} + +static sk_sp WrapOnscreenStudio(GrDirectContext* context, + const SkISize& size, + intptr_t fbo) { + GrGLenum format = kUnknown_SkColorType; + const SkColorType color_type = FirstSupportedColorType(context, &format); + + GrGLFramebufferInfo framebuffer_info = {}; + framebuffer_info.fFBOID = static_cast(fbo); + framebuffer_info.fFormat = format; + + GrBackendRenderTarget render_target(size.width(), // width + size.height(), // height + 0, // sample count + 0, // stencil bits + framebuffer_info // framebuffer info + ); + + sk_sp colorspace = SkColorSpace::MakeSRGB(); + SkStudioProps surface_props(0, kUnknown_SkPixelGeometry); + + return SkStudio::MakeFromBackendRenderTarget( + context, // Gr context + render_target, // render target + GrStudioOrigin::kBottomLeft_GrStudioOrigin, // origin + color_type, // color type + colorspace, // colorspace + &surface_props // surface properties + ); +} + +// |Studio| +GrDirectContext* GPUStudioGLSkia::GetContext() { + return context_.get(); +} + +// |Studio| +std::unique_ptr GPUStudioGLSkia::MakeRenderContextCurrent() { + return delegate_->GLContextMakeCurrent(); +} + +// |Studio| +bool GPUStudioGLSkia::ClearRenderContext() { + return delegate_->GLContextClearCurrent(); +} + +// |Studio| +bool GPUStudioGLSkia::AllowsDrawingWhenGpuDisabled() const { + return delegate_->AllowsDrawingWhenGpuDisabled(); +} + +} // namespace flutter diff --git a/shell/gpu/gpu_studio_gl_skia.h b/shell/gpu/gpu_studio_gl_skia.h new file mode 100644 index 0000000000000..11429f0df3bc9 --- /dev/null +++ b/shell/gpu/gpu_studio_gl_skia.h @@ -0,0 +1,74 @@ +// 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 SHELL_GPU_GPU_SURFACE_GL_SKIA_H_ +#define SHELL_GPU_GPU_SURFACE_GL_SKIA_H_ + +#include +#include + +#include "flutter/common/graphics/gl_context_switch.h" +#include "flutter/flow/embedded_views.h" +#include "flutter/flow/surface.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/shell/gpu/gpu_surface_gl_delegate.h" + +#include "third_party/skia/include/core/SkStudio.h" +#include "third_party/skia/include/gpu/GrDirectContext.h" + +namespace flutter { + +class GPUStudioGLSkia : public Studio { + public: + static sk_sp MakeGLContext(GPUStudioGLDelegate* delegate); + + GPUStudioGLSkia(const sk_sp& gr_context, + GPUStudioGLDelegate* delegate, + bool render_to_surface); + + // |Studio| + ~GPUStudioGLSkia() override; + + // |Studio| + bool IsValid() override; + + // |Studio| + GrDirectContext* GetContext() override; + + // |Studio| + std::unique_ptr MakeRenderContextCurrent() override; + + // |Studio| + bool ClearRenderContext() override; + + // |Studio| + bool AllowsDrawingWhenGpuDisabled() const override; + + private: + + GPUStudioGLDelegate* delegate_; + sk_sp context_; + sk_sp onscreen_surface_; + /// FBO backing the current `onscreen_surface_`. + uint32_t fbo_id_ = 0; + // The current FBO's existing damage, as tracked by the GPU surface, delegates + // still have an option of overriding this damage with their own in + // `GLContextFrameBufferInfo`. + std::optional existing_damage_ = std::nullopt; + // TODO(38466): Refactor GPU surface APIs take into account the fact that an + // external view embedder may want to render to the root surface. This is a + // hack to make avoid allocating resources for the root surface when an + // external view embedder is present. + const bool render_to_surface_ = true; + bool valid_ = false; + + // WeakPtrFactory must be the last member. + fml::TaskRunnerAffineWeakPtrFactory weak_factory_; + FML_DISALLOW_COPY_AND_ASSIGN(GPUStudioGLSkia); +}; + +} // namespace flutter + +#endif // SHELL_GPU_GPU_SURFACE_GL_SKIA_H_ diff --git a/shell/gpu/gpu_studio_metal_impeller.h b/shell/gpu/gpu_studio_metal_impeller.h new file mode 100644 index 0000000000000..7211da592dff7 --- /dev/null +++ b/shell/gpu/gpu_studio_metal_impeller.h @@ -0,0 +1,58 @@ +// 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_GPU_GPU_SURFACE_METAL_IMPELLER_H_ +#define FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_IMPELLER_H_ + +#include + +#include "flutter/flow/surface.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/impeller/aiks/aiks_context.h" +#include "flutter/impeller/renderer/renderer.h" +#include "flutter/shell/gpu/gpu_surface_metal_delegate.h" + +namespace flutter { + +class SK_API_AVAILABLE_CA_METAL_LAYER GPUStudioMetalImpeller : public Studio { + public: + GPUStudioMetalImpeller(GPUStudioMetalDelegate* delegate, + const std::shared_ptr& context); + + // |Studio| + ~GPUStudioMetalImpeller(); + + // |Studio| + bool IsValid() override; + + virtual Studio::StudioData GetStudioData() const override; + + private: + const GPUStudioMetalDelegate* delegate_; + std::shared_ptr impeller_renderer_; + std::shared_ptr aiks_context_; + fml::scoped_nsprotocol> last_drawable_; + + // |Studio| + GrDirectContext* GetContext() override; + + // |Studio| + std::unique_ptr MakeRenderContextCurrent() override; + + // |Studio| + bool AllowsDrawingWhenGpuDisabled() const override; + + // |Studio| + bool EnableRasterCache() const override; + + // |Studio| + impeller::AiksContext* GetAiksContext() const override; + + FML_DISALLOW_COPY_AND_ASSIGN(GPUStudioMetalImpeller); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_IMPELLER_H_ diff --git a/shell/gpu/gpu_studio_metal_impeller.mm b/shell/gpu/gpu_studio_metal_impeller.mm new file mode 100644 index 0000000000000..8d2a3a04e3a22 --- /dev/null +++ b/shell/gpu/gpu_studio_metal_impeller.mm @@ -0,0 +1,109 @@ +// 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. + +#include "flutter/shell/gpu/gpu_studio_metal_impeller.h" + +#import +#import + +#include "flutter/common/settings.h" +#include "flutter/fml/make_copyable.h" +#include "flutter/fml/mapping.h" +#include "flutter/fml/trace_event.h" +#include "flutter/impeller/display_list/display_list_dispatcher.h" +#include "flutter/impeller/renderer/backend/metal/surface_mtl.h" + +static_assert(!__has_feature(objc_arc), "ARC must be disabled."); + +namespace flutter { + +static std::shared_ptr CreateImpellerRenderer( + std::shared_ptr context) { + auto renderer = std::make_shared(std::move(context)); + if (!renderer->IsValid()) { + FML_LOG(ERROR) << "Could not create valid Impeller Renderer."; + return nullptr; + } + return renderer; +} + +GPUStudioMetalImpeller::GPUStudioMetalImpeller(GPUStudioMetalDelegate* delegate, + const std::shared_ptr& context) + : delegate_(delegate), + impeller_renderer_(CreateImpellerRenderer(context)), + aiks_context_( + std::make_shared(impeller_renderer_ ? context : nullptr)) {} + +GPUStudioMetalImpeller::~GPUStudioMetalImpeller() = default; + +// |Studio| +bool GPUStudioMetalImpeller::IsValid() { + return !!aiks_context_; +} + +// |Studio| +GrDirectContext* GPUStudioMetalImpeller::GetContext() { + return nullptr; +} + +// |Studio| +std::unique_ptr GPUStudioMetalImpeller::MakeRenderContextCurrent() { + // This backend has no such concept. + return std::make_unique(true); +} + +bool GPUStudioMetalImpeller::AllowsDrawingWhenGpuDisabled() const { + return delegate_->AllowsDrawingWhenGpuDisabled(); +} + +// |Studio| +bool GPUStudioMetalImpeller::EnableRasterCache() const { + return false; +} + +// |Studio| +impeller::AiksContext* GPUStudioMetalImpeller::GetAiksContext() const { + return aiks_context_.get(); +} + +Studio::StudioData GPUStudioMetalImpeller::GetStudioData() const { + if (!(last_drawable_ && [last_drawable_ conformsToProtocol:@protocol(CAMetalDrawable)])) { + return {}; + } + id metal_drawable = static_cast>(last_drawable_); + id texture = metal_drawable.texture; + int bytesPerPixel = 0; + std::string pixel_format; + switch (texture.pixelFormat) { + case MTLPixelFormatBGR10_XR: + bytesPerPixel = 4; + pixel_format = "MTLPixelFormatBGR10_XR"; + break; + case MTLPixelFormatBGRA10_XR: + bytesPerPixel = 8; + pixel_format = "MTLPixelFormatBGRA10_XR"; + break; + case MTLPixelFormatBGRA8Unorm: + bytesPerPixel = 4; + pixel_format = "MTLPixelFormatBGRA8Unorm"; + break; + default: + return {}; + } + + // Zero initialized so that errors are easier to find at the cost of + // performance. + sk_sp result = + SkData::MakeZeroInitialized(texture.width * texture.height * bytesPerPixel); + [texture getBytes:result->writable_data() + bytesPerRow:texture.width * bytesPerPixel + fromRegion:MTLRegionMake2D(0, 0, texture.width, texture.height) + mipmapLevel:0]; + return { + .pixel_format = pixel_format, + .data = result, + }; +} + +} // namespace flutter diff --git a/shell/gpu/gpu_studio_metal_skia.h b/shell/gpu/gpu_studio_metal_skia.h new file mode 100644 index 0000000000000..ae18c1fc3e742 --- /dev/null +++ b/shell/gpu/gpu_studio_metal_skia.h @@ -0,0 +1,62 @@ +// 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_GPU_GPU_SURFACE_METAL_H_ +#define FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_H_ + +#include "flutter/common/graphics/msaa_sample_count.h" +#include "flutter/flow/surface.h" +#include "flutter/fml/macros.h" +#include "flutter/shell/gpu/gpu_surface_metal_delegate.h" +#include "third_party/skia/include/gpu/GrDirectContext.h" + +namespace flutter { + +class SK_API_AVAILABLE_CA_METAL_LAYER GPUStudioMetalSkia : public Studio { + public: + GPUStudioMetalSkia(GPUStudioMetalDelegate* delegate, + sk_sp context, + MsaaSampleCount msaa_samples, + bool render_to_surface = true); + + // |Studio| + ~GPUStudioMetalSkia(); + + // |Studio| + bool IsValid() override; + + private: + const GPUStudioMetalDelegate* delegate_; + const MTLRenderTargetType render_target_type_; + sk_sp context_; + GrDirectContext* precompiled_sksl_context_ = nullptr; + MsaaSampleCount msaa_samples_ = MsaaSampleCount::kNone; + // TODO(38466): Refactor GPU surface APIs take into account the fact that an + // external view embedder may want to render to the root surface. This is a + // hack to make avoid allocating resources for the root surface when an + // external view embedder is present. + bool render_to_surface_ = true; + bool disable_partial_repaint_ = false; + + // Accumulated damage for each framebuffer; Key is address of underlying + // MTLTexture for each drawable + std::map damage_; + + // |Studio| + GrDirectContext* GetContext() override; + + // |Studio| + std::unique_ptr MakeRenderContextCurrent() override; + + // |Studio| + bool AllowsDrawingWhenGpuDisabled() const override; + + void PrecompileKnownSkSLsIfNecessary(); + + FML_DISALLOW_COPY_AND_ASSIGN(GPUStudioMetalSkia); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_H_ diff --git a/shell/gpu/gpu_studio_metal_skia.mm b/shell/gpu/gpu_studio_metal_skia.mm new file mode 100644 index 0000000000000..11273546c9f1a --- /dev/null +++ b/shell/gpu/gpu_studio_metal_skia.mm @@ -0,0 +1,106 @@ +// 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. + +#include "flutter/shell/gpu/gpu_studio_metal_skia.h" + +#import +#import + +#include + +#include "flutter/common/graphics/persistent_cache.h" +#include "flutter/fml/make_copyable.h" +#include "flutter/fml/platform/darwin/cf_utils.h" +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/fml/trace_event.h" +#include "flutter/shell/gpu/gpu_surface_metal_delegate.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkColorSpace.h" +#include "third_party/skia/include/core/SkColorType.h" +#include "third_party/skia/include/core/SkMatrix.h" +#include "third_party/skia/include/core/SkRect.h" +#include "third_party/skia/include/core/SkRefCnt.h" +#include "third_party/skia/include/core/SkSize.h" +#include "third_party/skia/include/core/SkStudio.h" +#include "third_party/skia/include/core/SkStudioProps.h" +#include "third_party/skia/include/gpu/GrBackendStudio.h" +#include "third_party/skia/include/ports/SkCFObject.h" + +static_assert(!__has_feature(objc_arc), "ARC must be disabled."); + +namespace flutter { + +namespace { +sk_sp CreateStudioFromMetalTexture(GrDirectContext* context, + id texture, + GrStudioOrigin origin, + MsaaSampleCount sample_cnt, + SkColorType color_type, + sk_sp color_space, + const SkStudioProps* props, + SkStudio::TextureReleaseProc release_proc, + SkStudio::ReleaseContext release_context) { + GrMtlTextureInfo info; + info.fTexture.reset([texture retain]); + GrBackendTexture backend_texture(texture.width, texture.height, GrMipmapped::kNo, info); + return SkStudio::MakeFromBackendTexture( + context, backend_texture, origin, static_cast(sample_cnt), color_type, + std::move(color_space), props, release_proc, release_context); +} +} // namespace + +GPUStudioMetalSkia::GPUStudioMetalSkia(GPUStudioMetalDelegate* delegate, + sk_sp context, + MsaaSampleCount msaa_samples, + bool render_to_surface) + : delegate_(delegate), + render_target_type_(delegate->GetRenderTargetType()), + context_(std::move(context)), + msaa_samples_(msaa_samples), + render_to_surface_(render_to_surface) { + // If this preference is explicitly set, we allow for disabling partial repaint. + NSNumber* disablePartialRepaint = + [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTDisablePartialRepaint"]; + if (disablePartialRepaint != nil) { + disable_partial_repaint_ = disablePartialRepaint.boolValue; + } +} + +GPUStudioMetalSkia::~GPUStudioMetalSkia() = default; + +// |Studio| +bool GPUStudioMetalSkia::IsValid() { + return context_ != nullptr; +} + +void GPUStudioMetalSkia::PrecompileKnownSkSLsIfNecessary() { + auto* current_context = GetContext(); + if (current_context == precompiled_sksl_context_) { + // Known SkSLs have already been prepared in this context. + return; + } + precompiled_sksl_context_ = current_context; + flutter::PersistentCache::GetCacheForProcess()->PrecompileKnownSkSLs(precompiled_sksl_context_); +} + +// |Studio| +GrDirectContext* GPUStudioMetalSkia::GetContext() { + return context_.get(); +} + +// |Studio| +std::unique_ptr GPUStudioMetalSkia::MakeRenderContextCurrent() { + // A context may either be necessary to render to the surface or to snapshot an offscreen + // surface. Either way, SkSL precompilation must be attempted. + PrecompileKnownSkSLsIfNecessary(); + + // This backend has no such concept. + return std::make_unique(true); +} + +bool GPUStudioMetalSkia::AllowsDrawingWhenGpuDisabled() const { + return delegate_->AllowsDrawingWhenGpuDisabled(); +} + +} // namespace flutter diff --git a/shell/gpu/gpu_studio_software.cc b/shell/gpu/gpu_studio_software.cc new file mode 100644 index 0000000000000..8bff529fdeec3 --- /dev/null +++ b/shell/gpu/gpu_studio_software.cc @@ -0,0 +1,34 @@ +// 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. + +#include "flutter/shell/gpu/gpu_studio_software.h" + +#include + +#include "flutter/fml/logging.h" + +#include "third_party/skia/include/core/SkStudio.h" + +namespace flutter { + +GPUStudioSoftware::GPUStudioSoftware(GPUStudioSoftwareDelegate* delegate, + bool render_to_surface) + : delegate_(delegate), + render_to_surface_(render_to_surface), + weak_factory_(this) {} + +GPUStudioSoftware::~GPUStudioSoftware() = default; + +// |Studio| +bool GPUStudioSoftware::IsValid() { + return delegate_ != nullptr; +} + +// |Studio| +GrDirectContext* GPUStudioSoftware::GetContext() { + // There is no GrContext associated with a software surface. + return nullptr; +} + +} // namespace flutter diff --git a/shell/gpu/gpu_studio_software.h b/shell/gpu/gpu_studio_software.h new file mode 100644 index 0000000000000..3c22ce6bb2b26 --- /dev/null +++ b/shell/gpu/gpu_studio_software.h @@ -0,0 +1,41 @@ +// 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_GPU_GPU_SURFACE_SOFTWARE_H_ +#define FLUTTER_SHELL_GPU_GPU_SURFACE_SOFTWARE_H_ + +#include "flutter/flow/surface.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/shell/gpu/gpu_surface_software_delegate.h" + +namespace flutter { + +class GPUStudioSoftware : public Studio { + public: + GPUStudioSoftware(GPUStudioSoftwareDelegate* delegate, + bool render_to_surface); + + ~GPUStudioSoftware() override; + + // |Studio| + bool IsValid() override; + + // |Studio| + GrDirectContext* GetContext() override; + + private: + GPUStudioSoftwareDelegate* delegate_; + // TODO(38466): Refactor GPU surface APIs take into account the fact that an + // external view embedder may want to render to the root surface. This is a + // hack to make avoid allocating resources for the root surface when an + // external view embedder is present. + const bool render_to_surface_; + fml::TaskRunnerAffineWeakPtrFactory weak_factory_; + FML_DISALLOW_COPY_AND_ASSIGN(GPUStudioSoftware); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_GPU_GPU_SURFACE_SOFTWARE_H_ diff --git a/shell/gpu/gpu_studio_vulkan.cc b/shell/gpu/gpu_studio_vulkan.cc new file mode 100644 index 0000000000000..a666b61e201a3 --- /dev/null +++ b/shell/gpu/gpu_studio_vulkan.cc @@ -0,0 +1,48 @@ +// 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. + +#include "flutter/shell/gpu/gpu_studio_vulkan.h" + +#include "flutter/fml/logging.h" +#include "flutter/fml/trace_event.h" + +#include "third_party/skia/include/core/SkColorSpace.h" +#include "third_party/skia/include/core/SkSize.h" +#include "third_party/skia/include/core/SkStudio.h" +#include "vulkan/vulkan_core.h" + +namespace flutter { + +GPUStudioVulkan::GPUStudioVulkan(GPUStudioVulkanDelegate* delegate, + const sk_sp& skia_context, + bool render_to_surface) + : delegate_(delegate), + skia_context_(skia_context), + render_to_surface_(render_to_surface), + weak_factory_(this) {} + +GPUStudioVulkan::~GPUStudioVulkan() = default; + +bool GPUStudioVulkan::IsValid() { + return skia_context_ != nullptr; +} + +GrDirectContext* GPUStudioVulkan::GetContext() { + return skia_context_.get(); +} + +SkColorType GPUStudioVulkan::ColorTypeFromFormat(const VkFormat format) { + switch (format) { + case VK_FORMAT_R8G8B8A8_UNORM: + case VK_FORMAT_R8G8B8A8_SRGB: + return SkColorType::kRGBA_8888_SkColorType; + case VK_FORMAT_B8G8R8A8_UNORM: + case VK_FORMAT_B8G8R8A8_SRGB: + return SkColorType::kBGRA_8888_SkColorType; + default: + return SkColorType::kUnknown_SkColorType; + } +} + +} // namespace flutter diff --git a/shell/gpu/gpu_studio_vulkan.h b/shell/gpu/gpu_studio_vulkan.h new file mode 100644 index 0000000000000..6cd96406b7dc3 --- /dev/null +++ b/shell/gpu/gpu_studio_vulkan.h @@ -0,0 +1,59 @@ +// 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 SHELL_GPU_GPU_SURFACE_VULKAN_H_ +#define SHELL_GPU_GPU_SURFACE_VULKAN_H_ + +#include + +#include "flutter/flow/surface.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/shell/gpu/gpu_surface_vulkan_delegate.h" +#include "flutter/vulkan/vulkan_backbuffer.h" +#include "flutter/vulkan/vulkan_native_surface.h" +#include "flutter/vulkan/vulkan_window.h" + +#include "third_party/skia/include/core/SkRefCnt.h" +#include "third_party/skia/include/core/SkStudio.h" + +namespace flutter { + +//------------------------------------------------------------------------------ +/// @brief A GPU surface backed by VkImages provided by a +/// GPUStudioVulkanDelegate. +/// +class GPUStudioVulkan : public Studio { + public: + //------------------------------------------------------------------------------ + /// @brief Create a GPUStudioVulkan while letting it reuse an existing + /// GrDirectContext. + /// + GPUStudioVulkan(GPUStudioVulkanDelegate* delegate, + const sk_sp& context, + bool render_to_surface); + + ~GPUStudioVulkan() override; + + // |Studio| + bool IsValid() override; + + // |Studio| + GrDirectContext* GetContext() override; + + static SkColorType ColorTypeFromFormat(const VkFormat format); + + private: + GPUStudioVulkanDelegate* delegate_; + sk_sp skia_context_; + bool render_to_surface_; + + fml::WeakPtrFactory weak_factory_; + + FML_DISALLOW_COPY_AND_ASSIGN(GPUStudioVulkan); +}; + +} // namespace flutter + +#endif // SHELL_GPU_GPU_SURFACE_VULKAN_H_ diff --git a/shell/gpu/gpu_studio_vulkan_impeller.cc b/shell/gpu/gpu_studio_vulkan_impeller.cc new file mode 100644 index 0000000000000..6a843c0bbb02a --- /dev/null +++ b/shell/gpu/gpu_studio_vulkan_impeller.cc @@ -0,0 +1,68 @@ +// 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. + +#include "flutter/shell/gpu/gpu_studio_vulkan_impeller.h" + +#include "flutter/fml/make_copyable.h" +#include "flutter/impeller/display_list/display_list_dispatcher.h" +#include "flutter/impeller/renderer/renderer.h" +#include "impeller/renderer/backend/vulkan/context_vk.h" + +namespace flutter { + +GPUStudioVulkanImpeller::GPUStudioVulkanImpeller( + std::shared_ptr context) + : weak_factory_(this) { + if (!context || !context->IsValid()) { + return; + } + + auto renderer = std::make_shared(context); + if (!renderer->IsValid()) { + return; + } + + auto aiks_context = std::make_shared(context); + if (!aiks_context->IsValid()) { + return; + } + + impeller_context_ = std::move(context); + impeller_renderer_ = std::move(renderer); + aiks_context_ = std::move(aiks_context); + is_valid_ = true; +} + +// |Studio| +GPUStudioVulkanImpeller::~GPUStudioVulkanImpeller() = default; + +// |Studio| +bool GPUStudioVulkanImpeller::IsValid() { + return is_valid_; +} + +// |Studio| +GrDirectContext* GPUStudioVulkanImpeller::GetContext() { + // Impeller != Skia. + return nullptr; +} + +// |Studio| +std::unique_ptr +GPUStudioVulkanImpeller::MakeRenderContextCurrent() { + // This backend has no such concept. + return std::make_unique(true); +} + +// |Studio| +bool GPUStudioVulkanImpeller::EnableRasterCache() const { + return false; +} + +// |Studio| +impeller::AiksContext* GPUStudioVulkanImpeller::GetAiksContext() const { + return aiks_context_.get(); +} + +} // namespace flutter diff --git a/shell/gpu/gpu_studio_vulkan_impeller.h b/shell/gpu/gpu_studio_vulkan_impeller.h new file mode 100644 index 0000000000000..645f3083dbb8b --- /dev/null +++ b/shell/gpu/gpu_studio_vulkan_impeller.h @@ -0,0 +1,49 @@ +// 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. + +#pragma once + +#include "flutter/common/graphics/gl_context_switch.h" +#include "flutter/flow/surface.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/impeller/aiks/aiks_context.h" +#include "flutter/impeller/renderer/context.h" +#include "flutter/shell/gpu/gpu_surface_vulkan_delegate.h" + +namespace flutter { + +class GPUStudioVulkanImpeller final : public Studio { + public: + explicit GPUStudioVulkanImpeller(std::shared_ptr context); + + // |Studio| + ~GPUStudioVulkanImpeller() override; + + // |Studio| + bool IsValid() override; + + private: + std::shared_ptr impeller_context_; + std::shared_ptr impeller_renderer_; + std::shared_ptr aiks_context_; + bool is_valid_ = false; + fml::WeakPtrFactory weak_factory_; + + // |Studio| + GrDirectContext* GetContext() override; + + // |Studio| + std::unique_ptr MakeRenderContextCurrent() override; + + // |Studio| + bool EnableRasterCache() const override; + + // |Studio| + impeller::AiksContext* GetAiksContext() const override; + + FML_DISALLOW_COPY_AND_ASSIGN(GPUStudioVulkanImpeller); +}; + +} // namespace flutter From 2617c704e3d51d8730214b66dabfd41d174d519b Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 00:56:08 -0700 Subject: [PATCH 017/147] Add GPUStudios, compilable --- ci/licenses_golden/licenses_flutter | 4 + flow/BUILD.gn | 2 + shell/gpu/gpu_studio_gl_impeller.cc | 7 +- shell/gpu/gpu_studio_gl_impeller.h | 14 ++- shell/gpu/gpu_studio_gl_skia.cc | 88 +------------------ shell/gpu/gpu_studio_gl_skia.h | 29 ++---- shell/gpu/gpu_studio_metal_impeller.h | 14 ++- shell/gpu/gpu_studio_metal_impeller.mm | 41 +-------- shell/gpu/gpu_studio_metal_skia.h | 27 ++---- shell/gpu/gpu_studio_metal_skia.mm | 39 +------- shell/gpu/gpu_studio_software.cc | 9 +- shell/gpu/gpu_studio_software.h | 19 ++-- shell/gpu/gpu_studio_vulkan.cc | 7 +- shell/gpu/gpu_studio_vulkan.h | 20 ++--- shell/gpu/gpu_studio_vulkan_impeller.cc | 9 +- shell/gpu/gpu_studio_vulkan_impeller.h | 3 +- shell/platform/embedder/embedder_studio.h | 3 + shell/platform/embedder/embedder_studio_gl.cc | 15 ++++ shell/platform/embedder/embedder_studio_gl.h | 3 + .../platform/embedder/embedder_studio_metal.h | 3 + .../embedder/embedder_studio_metal.mm | 19 ++++ .../embedder/embedder_studio_software.cc | 15 ++++ .../embedder/embedder_studio_software.h | 3 + .../embedder/embedder_studio_vulkan.cc | 15 ++++ .../embedder/embedder_studio_vulkan.h | 3 + .../platform_view_embedder_unittests.cc | 1 + 26 files changed, 140 insertions(+), 272 deletions(-) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 45beef38f4281..8460892f4dcfd 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -836,6 +836,8 @@ ORIGIN: ../../../flutter/flow/raster_cache_util.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/rtree.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/rtree.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/skia_gpu_object.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/flow/studio.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/flow/studio.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/surface.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/surface.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/flow/surface_frame.cc + ../../../flutter/LICENSE @@ -3392,6 +3394,8 @@ FILE: ../../../flutter/flow/raster_cache_util.h FILE: ../../../flutter/flow/rtree.cc FILE: ../../../flutter/flow/rtree.h FILE: ../../../flutter/flow/skia_gpu_object.h +FILE: ../../../flutter/flow/studio.cc +FILE: ../../../flutter/flow/studio.h FILE: ../../../flutter/flow/surface.cc FILE: ../../../flutter/flow/surface.h FILE: ../../../flutter/flow/surface_frame.cc diff --git a/flow/BUILD.gn b/flow/BUILD.gn index 1adceefde4bc2..c72a56aca0150 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -80,6 +80,8 @@ source_set("flow") { "rtree.cc", "rtree.h", "skia_gpu_object.h", + "studio.cc", + "studio.h", "surface.cc", "surface.h", "surface_frame.cc", diff --git a/shell/gpu/gpu_studio_gl_impeller.cc b/shell/gpu/gpu_studio_gl_impeller.cc index c7b443f4efe0e..1ffb0e38ed2fb 100644 --- a/shell/gpu/gpu_studio_gl_impeller.cc +++ b/shell/gpu/gpu_studio_gl_impeller.cc @@ -12,9 +12,8 @@ namespace flutter { GPUStudioGLImpeller::GPUStudioGLImpeller( - GPUStudioGLDelegate* delegate, - std::shared_ptr context) - : weak_factory_(this) { + GPUSurfaceGLDelegate* delegate, + std::shared_ptr context) { if (delegate == nullptr) { return; } @@ -35,8 +34,6 @@ GPUStudioGLImpeller::GPUStudioGLImpeller( } delegate_ = delegate; - impeller_context_ = std::move(context); - impeller_renderer_ = std::move(renderer); aiks_context_ = std::move(aiks_context); is_valid_ = true; } diff --git a/shell/gpu/gpu_studio_gl_impeller.h b/shell/gpu/gpu_studio_gl_impeller.h index 2397cc89a1fd3..9c54d7eb18283 100644 --- a/shell/gpu/gpu_studio_gl_impeller.h +++ b/shell/gpu/gpu_studio_gl_impeller.h @@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SHELL_GPU_GPU_SURFACE_GL_IMPELLER_H_ -#define SHELL_GPU_GPU_SURFACE_GL_IMPELLER_H_ +#ifndef SHELL_GPU_GPU_STUDIO_GL_IMPELLER_H_ +#define SHELL_GPU_GPU_STUDIO_GL_IMPELLER_H_ #include "flutter/common/graphics/gl_context_switch.h" #include "flutter/flow/surface.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/impeller/aiks/aiks_context.h" +#include "flutter/flow/studio.h" #include "flutter/impeller/renderer/context.h" #include "flutter/shell/gpu/gpu_surface_gl_delegate.h" @@ -17,7 +18,7 @@ namespace flutter { class GPUStudioGLImpeller final : public Studio { public: - explicit GPUStudioGLImpeller(GPUStudioGLDelegate* delegate, + explicit GPUStudioGLImpeller(GPUSurfaceGLDelegate* delegate, std::shared_ptr context); // |Studio| @@ -27,12 +28,9 @@ class GPUStudioGLImpeller final : public Studio { bool IsValid() override; private: - GPUStudioGLDelegate* delegate_ = nullptr; - std::shared_ptr impeller_context_; - std::shared_ptr impeller_renderer_; + GPUSurfaceGLDelegate* delegate_ = nullptr; std::shared_ptr aiks_context_; bool is_valid_ = false; - fml::WeakPtrFactory weak_factory_; // |Studio| GrDirectContext* GetContext() override; @@ -57,4 +55,4 @@ class GPUStudioGLImpeller final : public Studio { } // namespace flutter -#endif // SHELL_GPU_GPU_SURFACE_GL_IMPELLER_H_ +#endif // SHELL_GPU_GPU_STUDIO_GL_IMPELLER_H_ diff --git a/shell/gpu/gpu_studio_gl_skia.cc b/shell/gpu/gpu_studio_gl_skia.cc index 227c9099175be..b4dce0fbc51d6 100644 --- a/shell/gpu/gpu_studio_gl_skia.cc +++ b/shell/gpu/gpu_studio_gl_skia.cc @@ -15,8 +15,6 @@ #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkColorSpace.h" #include "third_party/skia/include/core/SkColorType.h" -#include "third_party/skia/include/core/SkStudio.h" -#include "third_party/skia/include/gpu/GrBackendStudio.h" #include "third_party/skia/include/gpu/GrContextOptions.h" // These are common defines present on all OpenGL headers. However, we don't @@ -29,47 +27,10 @@ namespace flutter { -// Default maximum number of bytes of GPU memory of budgeted resources in the -// cache. -// The shell will dynamically increase or decrease this cache based on the -// viewport size, unless a user has specifically requested a size on the Skia -// system channel. -static const size_t kGrCacheMaxByteSize = 24 * (1 << 20); - -sk_sp GPUStudioGLSkia::MakeGLContext( - GPUStudioGLDelegate* delegate) { - auto context_switch = delegate->GLContextMakeCurrent(); - if (!context_switch->GetResult()) { - FML_LOG(ERROR) - << "Could not make the context current to set up the Gr context."; - return nullptr; - } - - const auto options = - MakeDefaultContextOptions(ContextType::kRender, GrBackendApi::kOpenGL); - - auto context = GrDirectContext::MakeGL(delegate->GetGLInterface(), options); - - if (!context) { - FML_LOG(ERROR) << "Failed to set up Skia Gr context."; - return nullptr; - } - - context->setResourceCacheLimit(kGrCacheMaxByteSize); - - PersistentCache::GetCacheForProcess()->PrecompileKnownSkSLs(context.get()); - - return context; -} - GPUStudioGLSkia::GPUStudioGLSkia(const sk_sp& gr_context, - GPUStudioGLDelegate* delegate, - bool render_to_surface) + GPUSurfaceGLDelegate* delegate) : delegate_(delegate), - context_(gr_context), - - render_to_surface_(render_to_surface), - weak_factory_(this) { + context_(gr_context) { auto context_switch = delegate_->GLContextMakeCurrent(); if (!context_switch->GetResult()) { FML_LOG(ERROR) @@ -93,8 +54,6 @@ GPUStudioGLSkia::~GPUStudioGLSkia() { return; } - onscreen_surface_ = nullptr; - fbo_id_ = 0; context_ = nullptr; delegate_->GLContextClearCurrent(); @@ -105,49 +64,6 @@ bool GPUStudioGLSkia::IsValid() { return valid_; } -static SkColorType FirstSupportedColorType(GrDirectContext* context, - GrGLenum* format) { -#define RETURN_IF_RENDERABLE(x, y) \ - if (context->colorTypeSupportedAsStudio((x))) { \ - *format = (y); \ - return (x); \ - } - RETURN_IF_RENDERABLE(kRGBA_8888_SkColorType, GPU_GL_RGBA8); - RETURN_IF_RENDERABLE(kARGB_4444_SkColorType, GPU_GL_RGBA4); - RETURN_IF_RENDERABLE(kRGB_565_SkColorType, GPU_GL_RGB565); - return kUnknown_SkColorType; -} - -static sk_sp WrapOnscreenStudio(GrDirectContext* context, - const SkISize& size, - intptr_t fbo) { - GrGLenum format = kUnknown_SkColorType; - const SkColorType color_type = FirstSupportedColorType(context, &format); - - GrGLFramebufferInfo framebuffer_info = {}; - framebuffer_info.fFBOID = static_cast(fbo); - framebuffer_info.fFormat = format; - - GrBackendRenderTarget render_target(size.width(), // width - size.height(), // height - 0, // sample count - 0, // stencil bits - framebuffer_info // framebuffer info - ); - - sk_sp colorspace = SkColorSpace::MakeSRGB(); - SkStudioProps surface_props(0, kUnknown_SkPixelGeometry); - - return SkStudio::MakeFromBackendRenderTarget( - context, // Gr context - render_target, // render target - GrStudioOrigin::kBottomLeft_GrStudioOrigin, // origin - color_type, // color type - colorspace, // colorspace - &surface_props // surface properties - ); -} - // |Studio| GrDirectContext* GPUStudioGLSkia::GetContext() { return context_.get(); diff --git a/shell/gpu/gpu_studio_gl_skia.h b/shell/gpu/gpu_studio_gl_skia.h index 11429f0df3bc9..9dbedb0b6d58e 100644 --- a/shell/gpu/gpu_studio_gl_skia.h +++ b/shell/gpu/gpu_studio_gl_skia.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SHELL_GPU_GPU_SURFACE_GL_SKIA_H_ -#define SHELL_GPU_GPU_SURFACE_GL_SKIA_H_ +#ifndef SHELL_GPU_GPU_STUDIO_GL_SKIA_H_ +#define SHELL_GPU_GPU_STUDIO_GL_SKIA_H_ #include #include @@ -11,22 +11,19 @@ #include "flutter/common/graphics/gl_context_switch.h" #include "flutter/flow/embedded_views.h" #include "flutter/flow/surface.h" +#include "flutter/flow/studio.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/shell/gpu/gpu_surface_gl_delegate.h" -#include "third_party/skia/include/core/SkStudio.h" #include "third_party/skia/include/gpu/GrDirectContext.h" namespace flutter { class GPUStudioGLSkia : public Studio { public: - static sk_sp MakeGLContext(GPUStudioGLDelegate* delegate); - GPUStudioGLSkia(const sk_sp& gr_context, - GPUStudioGLDelegate* delegate, - bool render_to_surface); + GPUSurfaceGLDelegate* delegate); // |Studio| ~GPUStudioGLSkia() override; @@ -48,27 +45,13 @@ class GPUStudioGLSkia : public Studio { private: - GPUStudioGLDelegate* delegate_; + GPUSurfaceGLDelegate* delegate_; sk_sp context_; - sk_sp onscreen_surface_; - /// FBO backing the current `onscreen_surface_`. - uint32_t fbo_id_ = 0; - // The current FBO's existing damage, as tracked by the GPU surface, delegates - // still have an option of overriding this damage with their own in - // `GLContextFrameBufferInfo`. - std::optional existing_damage_ = std::nullopt; - // TODO(38466): Refactor GPU surface APIs take into account the fact that an - // external view embedder may want to render to the root surface. This is a - // hack to make avoid allocating resources for the root surface when an - // external view embedder is present. - const bool render_to_surface_ = true; bool valid_ = false; - // WeakPtrFactory must be the last member. - fml::TaskRunnerAffineWeakPtrFactory weak_factory_; FML_DISALLOW_COPY_AND_ASSIGN(GPUStudioGLSkia); }; } // namespace flutter -#endif // SHELL_GPU_GPU_SURFACE_GL_SKIA_H_ +#endif // SHELL_GPU_GPU_STUDIO_GL_SKIA_H_ diff --git a/shell/gpu/gpu_studio_metal_impeller.h b/shell/gpu/gpu_studio_metal_impeller.h index 7211da592dff7..d9b534c487540 100644 --- a/shell/gpu/gpu_studio_metal_impeller.h +++ b/shell/gpu/gpu_studio_metal_impeller.h @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_IMPELLER_H_ -#define FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_IMPELLER_H_ +#ifndef FLUTTER_SHELL_GPU_GPU_STUDIO_METAL_IMPELLER_H_ +#define FLUTTER_SHELL_GPU_GPU_STUDIO_METAL_IMPELLER_H_ #include +#include "flutter/flow/studio.h" #include "flutter/flow/surface.h" #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" @@ -18,7 +19,7 @@ namespace flutter { class SK_API_AVAILABLE_CA_METAL_LAYER GPUStudioMetalImpeller : public Studio { public: - GPUStudioMetalImpeller(GPUStudioMetalDelegate* delegate, + GPUStudioMetalImpeller(GPUSurfaceMetalDelegate* delegate, const std::shared_ptr& context); // |Studio| @@ -27,13 +28,10 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUStudioMetalImpeller : public Studio { // |Studio| bool IsValid() override; - virtual Studio::StudioData GetStudioData() const override; - private: - const GPUStudioMetalDelegate* delegate_; + const GPUSurfaceMetalDelegate* delegate_; std::shared_ptr impeller_renderer_; std::shared_ptr aiks_context_; - fml::scoped_nsprotocol> last_drawable_; // |Studio| GrDirectContext* GetContext() override; @@ -55,4 +53,4 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUStudioMetalImpeller : public Studio { } // namespace flutter -#endif // FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_IMPELLER_H_ +#endif // FLUTTER_SHELL_GPU_GPU_STUDIO_METAL_IMPELLER_H_ diff --git a/shell/gpu/gpu_studio_metal_impeller.mm b/shell/gpu/gpu_studio_metal_impeller.mm index 8d2a3a04e3a22..b62425a694d49 100644 --- a/shell/gpu/gpu_studio_metal_impeller.mm +++ b/shell/gpu/gpu_studio_metal_impeller.mm @@ -28,7 +28,7 @@ return renderer; } -GPUStudioMetalImpeller::GPUStudioMetalImpeller(GPUStudioMetalDelegate* delegate, +GPUStudioMetalImpeller::GPUStudioMetalImpeller(GPUSurfaceMetalDelegate* delegate, const std::shared_ptr& context) : delegate_(delegate), impeller_renderer_(CreateImpellerRenderer(context)), @@ -67,43 +67,4 @@ return aiks_context_.get(); } -Studio::StudioData GPUStudioMetalImpeller::GetStudioData() const { - if (!(last_drawable_ && [last_drawable_ conformsToProtocol:@protocol(CAMetalDrawable)])) { - return {}; - } - id metal_drawable = static_cast>(last_drawable_); - id texture = metal_drawable.texture; - int bytesPerPixel = 0; - std::string pixel_format; - switch (texture.pixelFormat) { - case MTLPixelFormatBGR10_XR: - bytesPerPixel = 4; - pixel_format = "MTLPixelFormatBGR10_XR"; - break; - case MTLPixelFormatBGRA10_XR: - bytesPerPixel = 8; - pixel_format = "MTLPixelFormatBGRA10_XR"; - break; - case MTLPixelFormatBGRA8Unorm: - bytesPerPixel = 4; - pixel_format = "MTLPixelFormatBGRA8Unorm"; - break; - default: - return {}; - } - - // Zero initialized so that errors are easier to find at the cost of - // performance. - sk_sp result = - SkData::MakeZeroInitialized(texture.width * texture.height * bytesPerPixel); - [texture getBytes:result->writable_data() - bytesPerRow:texture.width * bytesPerPixel - fromRegion:MTLRegionMake2D(0, 0, texture.width, texture.height) - mipmapLevel:0]; - return { - .pixel_format = pixel_format, - .data = result, - }; -} - } // namespace flutter diff --git a/shell/gpu/gpu_studio_metal_skia.h b/shell/gpu/gpu_studio_metal_skia.h index ae18c1fc3e742..71d0c69351df0 100644 --- a/shell/gpu/gpu_studio_metal_skia.h +++ b/shell/gpu/gpu_studio_metal_skia.h @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_H_ -#define FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_H_ +#ifndef FLUTTER_SHELL_GPU_GPU_STUDIO_METAL_H_ +#define FLUTTER_SHELL_GPU_GPU_STUDIO_METAL_H_ #include "flutter/common/graphics/msaa_sample_count.h" #include "flutter/flow/surface.h" +#include "flutter/flow/studio.h" #include "flutter/fml/macros.h" #include "flutter/shell/gpu/gpu_surface_metal_delegate.h" #include "third_party/skia/include/gpu/GrDirectContext.h" @@ -15,10 +16,8 @@ namespace flutter { class SK_API_AVAILABLE_CA_METAL_LAYER GPUStudioMetalSkia : public Studio { public: - GPUStudioMetalSkia(GPUStudioMetalDelegate* delegate, - sk_sp context, - MsaaSampleCount msaa_samples, - bool render_to_surface = true); + GPUStudioMetalSkia(GPUSurfaceMetalDelegate* delegate, + sk_sp context); // |Studio| ~GPUStudioMetalSkia(); @@ -27,21 +26,9 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUStudioMetalSkia : public Studio { bool IsValid() override; private: - const GPUStudioMetalDelegate* delegate_; - const MTLRenderTargetType render_target_type_; + const GPUSurfaceMetalDelegate* delegate_; sk_sp context_; GrDirectContext* precompiled_sksl_context_ = nullptr; - MsaaSampleCount msaa_samples_ = MsaaSampleCount::kNone; - // TODO(38466): Refactor GPU surface APIs take into account the fact that an - // external view embedder may want to render to the root surface. This is a - // hack to make avoid allocating resources for the root surface when an - // external view embedder is present. - bool render_to_surface_ = true; - bool disable_partial_repaint_ = false; - - // Accumulated damage for each framebuffer; Key is address of underlying - // MTLTexture for each drawable - std::map damage_; // |Studio| GrDirectContext* GetContext() override; @@ -59,4 +46,4 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUStudioMetalSkia : public Studio { } // namespace flutter -#endif // FLUTTER_SHELL_GPU_GPU_SURFACE_METAL_H_ +#endif // FLUTTER_SHELL_GPU_GPU_STUDIO_METAL_H_ diff --git a/shell/gpu/gpu_studio_metal_skia.mm b/shell/gpu/gpu_studio_metal_skia.mm index 11273546c9f1a..6bc24ab95d5ef 100644 --- a/shell/gpu/gpu_studio_metal_skia.mm +++ b/shell/gpu/gpu_studio_metal_skia.mm @@ -22,49 +22,16 @@ #include "third_party/skia/include/core/SkRect.h" #include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/core/SkSize.h" -#include "third_party/skia/include/core/SkStudio.h" -#include "third_party/skia/include/core/SkStudioProps.h" -#include "third_party/skia/include/gpu/GrBackendStudio.h" #include "third_party/skia/include/ports/SkCFObject.h" static_assert(!__has_feature(objc_arc), "ARC must be disabled."); namespace flutter { -namespace { -sk_sp CreateStudioFromMetalTexture(GrDirectContext* context, - id texture, - GrStudioOrigin origin, - MsaaSampleCount sample_cnt, - SkColorType color_type, - sk_sp color_space, - const SkStudioProps* props, - SkStudio::TextureReleaseProc release_proc, - SkStudio::ReleaseContext release_context) { - GrMtlTextureInfo info; - info.fTexture.reset([texture retain]); - GrBackendTexture backend_texture(texture.width, texture.height, GrMipmapped::kNo, info); - return SkStudio::MakeFromBackendTexture( - context, backend_texture, origin, static_cast(sample_cnt), color_type, - std::move(color_space), props, release_proc, release_context); -} -} // namespace - -GPUStudioMetalSkia::GPUStudioMetalSkia(GPUStudioMetalDelegate* delegate, - sk_sp context, - MsaaSampleCount msaa_samples, - bool render_to_surface) +GPUStudioMetalSkia::GPUStudioMetalSkia(GPUSurfaceMetalDelegate* delegate, + sk_sp context) : delegate_(delegate), - render_target_type_(delegate->GetRenderTargetType()), - context_(std::move(context)), - msaa_samples_(msaa_samples), - render_to_surface_(render_to_surface) { - // If this preference is explicitly set, we allow for disabling partial repaint. - NSNumber* disablePartialRepaint = - [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTDisablePartialRepaint"]; - if (disablePartialRepaint != nil) { - disable_partial_repaint_ = disablePartialRepaint.boolValue; - } + context_(std::move(context)) { } GPUStudioMetalSkia::~GPUStudioMetalSkia() = default; diff --git a/shell/gpu/gpu_studio_software.cc b/shell/gpu/gpu_studio_software.cc index 8bff529fdeec3..be46c443b22f8 100644 --- a/shell/gpu/gpu_studio_software.cc +++ b/shell/gpu/gpu_studio_software.cc @@ -8,15 +8,10 @@ #include "flutter/fml/logging.h" -#include "third_party/skia/include/core/SkStudio.h" - namespace flutter { -GPUStudioSoftware::GPUStudioSoftware(GPUStudioSoftwareDelegate* delegate, - bool render_to_surface) - : delegate_(delegate), - render_to_surface_(render_to_surface), - weak_factory_(this) {} +GPUStudioSoftware::GPUStudioSoftware(GPUSurfaceSoftwareDelegate* delegate) + : delegate_(delegate) {} GPUStudioSoftware::~GPUStudioSoftware() = default; diff --git a/shell/gpu/gpu_studio_software.h b/shell/gpu/gpu_studio_software.h index 3c22ce6bb2b26..4eca32b8442f6 100644 --- a/shell/gpu/gpu_studio_software.h +++ b/shell/gpu/gpu_studio_software.h @@ -2,20 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_SHELL_GPU_GPU_SURFACE_SOFTWARE_H_ -#define FLUTTER_SHELL_GPU_GPU_SURFACE_SOFTWARE_H_ +#ifndef FLUTTER_SHELL_GPU_GPU_STUDIO_SOFTWARE_H_ +#define FLUTTER_SHELL_GPU_GPU_STUDIO_SOFTWARE_H_ #include "flutter/flow/surface.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/shell/gpu/gpu_surface_software_delegate.h" +#include "flutter/flow/studio.h" namespace flutter { class GPUStudioSoftware : public Studio { public: - GPUStudioSoftware(GPUStudioSoftwareDelegate* delegate, - bool render_to_surface); + GPUStudioSoftware(GPUSurfaceSoftwareDelegate* delegate); ~GPUStudioSoftware() override; @@ -26,16 +26,11 @@ class GPUStudioSoftware : public Studio { GrDirectContext* GetContext() override; private: - GPUStudioSoftwareDelegate* delegate_; - // TODO(38466): Refactor GPU surface APIs take into account the fact that an - // external view embedder may want to render to the root surface. This is a - // hack to make avoid allocating resources for the root surface when an - // external view embedder is present. - const bool render_to_surface_; - fml::TaskRunnerAffineWeakPtrFactory weak_factory_; + GPUSurfaceSoftwareDelegate* delegate_; + FML_DISALLOW_COPY_AND_ASSIGN(GPUStudioSoftware); }; } // namespace flutter -#endif // FLUTTER_SHELL_GPU_GPU_SURFACE_SOFTWARE_H_ +#endif // FLUTTER_SHELL_GPU_GPU_STUDIO_SOFTWARE_H_ diff --git a/shell/gpu/gpu_studio_vulkan.cc b/shell/gpu/gpu_studio_vulkan.cc index a666b61e201a3..245a6370f039f 100644 --- a/shell/gpu/gpu_studio_vulkan.cc +++ b/shell/gpu/gpu_studio_vulkan.cc @@ -9,17 +9,14 @@ #include "third_party/skia/include/core/SkColorSpace.h" #include "third_party/skia/include/core/SkSize.h" -#include "third_party/skia/include/core/SkStudio.h" #include "vulkan/vulkan_core.h" namespace flutter { -GPUStudioVulkan::GPUStudioVulkan(GPUStudioVulkanDelegate* delegate, - const sk_sp& skia_context, - bool render_to_surface) +GPUStudioVulkan::GPUStudioVulkan(GPUSurfaceVulkanDelegate* delegate, + const sk_sp& skia_context) : delegate_(delegate), skia_context_(skia_context), - render_to_surface_(render_to_surface), weak_factory_(this) {} GPUStudioVulkan::~GPUStudioVulkan() = default; diff --git a/shell/gpu/gpu_studio_vulkan.h b/shell/gpu/gpu_studio_vulkan.h index 6cd96406b7dc3..ced9ae426bec5 100644 --- a/shell/gpu/gpu_studio_vulkan.h +++ b/shell/gpu/gpu_studio_vulkan.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SHELL_GPU_GPU_SURFACE_VULKAN_H_ -#define SHELL_GPU_GPU_SURFACE_VULKAN_H_ +#ifndef SHELL_GPU_GPU_STUDIO_VULKAN_H_ +#define SHELL_GPU_GPU_STUDIO_VULKAN_H_ #include @@ -13,16 +13,16 @@ #include "flutter/shell/gpu/gpu_surface_vulkan_delegate.h" #include "flutter/vulkan/vulkan_backbuffer.h" #include "flutter/vulkan/vulkan_native_surface.h" +#include "flutter/flow/studio.h" #include "flutter/vulkan/vulkan_window.h" #include "third_party/skia/include/core/SkRefCnt.h" -#include "third_party/skia/include/core/SkStudio.h" namespace flutter { //------------------------------------------------------------------------------ /// @brief A GPU surface backed by VkImages provided by a -/// GPUStudioVulkanDelegate. +/// GPUSurfaceVulkanDelegate. /// class GPUStudioVulkan : public Studio { public: @@ -30,9 +30,8 @@ class GPUStudioVulkan : public Studio { /// @brief Create a GPUStudioVulkan while letting it reuse an existing /// GrDirectContext. /// - GPUStudioVulkan(GPUStudioVulkanDelegate* delegate, - const sk_sp& context, - bool render_to_surface); + GPUStudioVulkan(GPUSurfaceVulkanDelegate* delegate, + const sk_sp& context); ~GPUStudioVulkan() override; @@ -45,15 +44,12 @@ class GPUStudioVulkan : public Studio { static SkColorType ColorTypeFromFormat(const VkFormat format); private: - GPUStudioVulkanDelegate* delegate_; + GPUSurfaceVulkanDelegate* delegate_; sk_sp skia_context_; - bool render_to_surface_; - - fml::WeakPtrFactory weak_factory_; FML_DISALLOW_COPY_AND_ASSIGN(GPUStudioVulkan); }; } // namespace flutter -#endif // SHELL_GPU_GPU_SURFACE_VULKAN_H_ +#endif // SHELL_GPU_GPU_STUDIO_VULKAN_H_ diff --git a/shell/gpu/gpu_studio_vulkan_impeller.cc b/shell/gpu/gpu_studio_vulkan_impeller.cc index 6a843c0bbb02a..17520fd6b0581 100644 --- a/shell/gpu/gpu_studio_vulkan_impeller.cc +++ b/shell/gpu/gpu_studio_vulkan_impeller.cc @@ -12,24 +12,17 @@ namespace flutter { GPUStudioVulkanImpeller::GPUStudioVulkanImpeller( - std::shared_ptr context) - : weak_factory_(this) { + std::shared_ptr context) { if (!context || !context->IsValid()) { return; } - auto renderer = std::make_shared(context); - if (!renderer->IsValid()) { - return; - } - auto aiks_context = std::make_shared(context); if (!aiks_context->IsValid()) { return; } impeller_context_ = std::move(context); - impeller_renderer_ = std::move(renderer); aiks_context_ = std::move(aiks_context); is_valid_ = true; } diff --git a/shell/gpu/gpu_studio_vulkan_impeller.h b/shell/gpu/gpu_studio_vulkan_impeller.h index 645f3083dbb8b..e25346a9e44de 100644 --- a/shell/gpu/gpu_studio_vulkan_impeller.h +++ b/shell/gpu/gpu_studio_vulkan_impeller.h @@ -6,6 +6,7 @@ #include "flutter/common/graphics/gl_context_switch.h" #include "flutter/flow/surface.h" +#include "flutter/flow/studio.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/impeller/aiks/aiks_context.h" @@ -26,10 +27,8 @@ class GPUStudioVulkanImpeller final : public Studio { private: std::shared_ptr impeller_context_; - std::shared_ptr impeller_renderer_; std::shared_ptr aiks_context_; bool is_valid_ = false; - fml::WeakPtrFactory weak_factory_; // |Studio| GrDirectContext* GetContext() override; diff --git a/shell/platform/embedder/embedder_studio.h b/shell/platform/embedder/embedder_studio.h index 8f50d5d42c677..2ebfa51eb36ae 100644 --- a/shell/platform/embedder/embedder_studio.h +++ b/shell/platform/embedder/embedder_studio.h @@ -7,6 +7,7 @@ #include +#include "flutter/flow/studio.h" #include "flutter/shell/platform/embedder/embedder_external_view_embedder.h" #include "flutter/shell/platform/embedder/embedder_surface.h" @@ -20,6 +21,8 @@ class EmbedderStudio { virtual bool IsValid() const = 0; + virtual std::unique_ptr CreateGPUStudio() = 0; + virtual std::unique_ptr CreateSurface() = 0; virtual sk_sp CreateResourceContext() const = 0; diff --git a/shell/platform/embedder/embedder_studio_gl.cc b/shell/platform/embedder/embedder_studio_gl.cc index 1f03fc815787f..ba0dc47c2ea60 100644 --- a/shell/platform/embedder/embedder_studio_gl.cc +++ b/shell/platform/embedder/embedder_studio_gl.cc @@ -8,6 +8,7 @@ #include "flutter/shell/common/shell_io_manager.h" #include "flutter/shell/platform/embedder/embedder_surface_gl.h" +#include "flutter/shell/gpu/gpu_studio_gl_skia.h" namespace flutter { @@ -98,6 +99,20 @@ SurfaceFrame::FramebufferInfo EmbedderStudioGL::GLContextFramebufferInfo() return info; } +// |EmbedderStudio| +std::unique_ptr EmbedderStudioGL::CreateGPUStudio() { + if (!IsValid()) { + return nullptr; + } + auto studio = + std::make_unique(main_context_, this); + if (!studio->IsValid()) { + return nullptr; + } + + return studio; +} + // |EmbedderStudio| std::unique_ptr EmbedderStudioGL::CreateSurface() { if (!main_context_) { diff --git a/shell/platform/embedder/embedder_studio_gl.h b/shell/platform/embedder/embedder_studio_gl.h index 65ebda6855bcc..4813cb48b0e3f 100644 --- a/shell/platform/embedder/embedder_studio_gl.h +++ b/shell/platform/embedder/embedder_studio_gl.h @@ -37,6 +37,9 @@ class EmbedderStudioGL final : public EmbedderStudio, // |EmbedderStudio| bool IsValid() const override; + // |EmbedderStudio| + std::unique_ptr CreateGPUStudio() override; + // |EmbedderStudio| std::unique_ptr CreateSurface() override; diff --git a/shell/platform/embedder/embedder_studio_metal.h b/shell/platform/embedder/embedder_studio_metal.h index db1d0bbf1a4be..eefc8e2745a12 100644 --- a/shell/platform/embedder/embedder_studio_metal.h +++ b/shell/platform/embedder/embedder_studio_metal.h @@ -35,6 +35,9 @@ class EmbedderStudioMetal final : public EmbedderStudio, // |EmbedderStudio| bool IsValid() const override; + // |EmbedderStudio| + std::unique_ptr CreateGPUStudio() override; + // |EmbedderStudio| std::unique_ptr CreateSurface() override; diff --git a/shell/platform/embedder/embedder_studio_metal.mm b/shell/platform/embedder/embedder_studio_metal.mm index 87ef747734042..a6ab39298a3d0 100644 --- a/shell/platform/embedder/embedder_studio_metal.mm +++ b/shell/platform/embedder/embedder_studio_metal.mm @@ -9,6 +9,7 @@ #include "flutter/fml/logging.h" #include "flutter/shell/gpu/gpu_surface_metal_delegate.h" #import "flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalSkia.h" +#include "flutter/shell/gpu/gpu_studio_metal_skia.h" #include "flutter/shell/platform/embedder/embedder_surface_metal.h" #include "third_party/skia/include/gpu/GrDirectContext.h" @@ -38,6 +39,24 @@ return valid_; } +std::unique_ptr EmbedderStudioMetal::CreateGPUStudio() API_AVAILABLE(ios(13.0)) { + if (@available(iOS 13.0, *)) { + } else { + return nullptr; + } + if (!IsValid()) { + return nullptr; + } + + auto studio = std::make_unique(this, main_context_); + + if (!studio->IsValid()) { + return nullptr; + } + + return studio; +} + std::unique_ptr EmbedderStudioMetal::CreateSurface() { if (!IsValid()) { return nullptr; diff --git a/shell/platform/embedder/embedder_studio_software.cc b/shell/platform/embedder/embedder_studio_software.cc index eceeda79fee89..c9baf961acf85 100644 --- a/shell/platform/embedder/embedder_studio_software.cc +++ b/shell/platform/embedder/embedder_studio_software.cc @@ -10,6 +10,7 @@ #include "flutter/shell/platform/embedder/embedder_studio_software.h" #include "flutter/shell/platform/embedder/embedder_surface_software.h" +#include "flutter/shell/gpu/gpu_studio_software.h" #include "third_party/skia/include/core/SkColorSpace.h" #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkSurface.h" @@ -35,6 +36,20 @@ bool EmbedderStudioSoftware::IsValid() const { return valid_; } +// |EmbedderStudio| +std::unique_ptr EmbedderStudioSoftware::CreateGPUStudio() { + if (!IsValid()) { + return nullptr; + } + auto studio = std::make_unique(this); + + if (!studio->IsValid()) { + return nullptr; + } + + return studio; +} + // |EmbedderStudio| std::unique_ptr EmbedderStudioSoftware::CreateSurface() { if (!IsValid()) { diff --git a/shell/platform/embedder/embedder_studio_software.h b/shell/platform/embedder/embedder_studio_software.h index ce9ba6f9f8182..0ab0e2977fd5e 100644 --- a/shell/platform/embedder/embedder_studio_software.h +++ b/shell/platform/embedder/embedder_studio_software.h @@ -31,6 +31,9 @@ class EmbedderStudioSoftware final : public EmbedderStudio, // |EmbedderStudio| bool IsValid() const override; + // |EmbedderStudio| + std::unique_ptr CreateGPUStudio() override; + // |EmbedderStudio| std::unique_ptr CreateSurface() override; diff --git a/shell/platform/embedder/embedder_studio_vulkan.cc b/shell/platform/embedder/embedder_studio_vulkan.cc index ba61e2217a209..4c7e98dcb9573 100644 --- a/shell/platform/embedder/embedder_studio_vulkan.cc +++ b/shell/platform/embedder/embedder_studio_vulkan.cc @@ -14,6 +14,7 @@ #include "flutter/vulkan/vulkan_skia_proc_table.h" #include "include/gpu/GrDirectContext.h" #include "include/gpu/vk/GrVkBackendContext.h" +#include "flutter/shell/gpu/gpu_studio_vulkan.h" #include "include/gpu/vk/GrVkExtensions.h" namespace flutter { @@ -108,6 +109,20 @@ bool EmbedderStudioVulkan::IsValid() const { return valid_; } +// |EmbedderStudio| +std::unique_ptr EmbedderStudioVulkan::CreateGPUStudio() { + if (!IsValid()) { + return nullptr; + } + auto studio = std::make_unique(this, main_context_); + + if (!studio->IsValid()) { + return nullptr; + } + + return studio; +} + // |EmbedderStudio| std::unique_ptr EmbedderStudioVulkan::CreateSurface() { const bool render_to_surface = !external_view_embedder_; diff --git a/shell/platform/embedder/embedder_studio_vulkan.h b/shell/platform/embedder/embedder_studio_vulkan.h index e161819591105..bfd5af3ed5122 100644 --- a/shell/platform/embedder/embedder_studio_vulkan.h +++ b/shell/platform/embedder/embedder_studio_vulkan.h @@ -55,6 +55,9 @@ class EmbedderStudioVulkan final : public EmbedderStudio, // |EmbedderStudio| bool IsValid() const override; + // |EmbedderStudio| + std::unique_ptr CreateGPUStudio() override; + // |EmbedderStudio| std::unique_ptr CreateSurface() override; diff --git a/shell/platform/embedder/platform_view_embedder_unittests.cc b/shell/platform/embedder/platform_view_embedder_unittests.cc index 04b4fb804b871..f0a07cc8aa004 100644 --- a/shell/platform/embedder/platform_view_embedder_unittests.cc +++ b/shell/platform/embedder/platform_view_embedder_unittests.cc @@ -62,6 +62,7 @@ class MockResponse : public PlatformMessageResponse { class MockStudio : public EmbedderStudio { public: MOCK_CONST_METHOD0(IsValid, bool()); + MOCK_METHOD0(CreateGPUStudio, std::unique_ptr()); MOCK_METHOD0(CreateSurface, std::unique_ptr()); MOCK_CONST_METHOD0(CreateResourceContext, sk_sp()); }; From b9f524cee802a9308145a46b86e7198403d42160 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 12:51:45 -0700 Subject: [PATCH 018/147] Format --- shell/gpu/gpu_studio_gl_impeller.h | 4 ++-- shell/gpu/gpu_studio_gl_skia.cc | 5 ++--- shell/gpu/gpu_studio_gl_skia.h | 5 ++--- shell/gpu/gpu_studio_metal_impeller.h | 2 +- shell/gpu/gpu_studio_metal_impeller.mm | 2 +- shell/gpu/gpu_studio_metal_skia.h | 4 ++-- shell/gpu/gpu_studio_metal_skia.mm | 6 ++---- shell/gpu/gpu_studio_software.h | 2 +- shell/gpu/gpu_studio_vulkan.cc | 6 ++---- shell/gpu/gpu_studio_vulkan.h | 4 ++-- shell/gpu/gpu_studio_vulkan_impeller.h | 2 +- shell/platform/embedder/embedder_studio_gl.cc | 5 ++--- shell/platform/embedder/embedder_studio_metal.mm | 2 +- shell/platform/embedder/embedder_studio_software.cc | 2 +- shell/platform/embedder/embedder_studio_vulkan.cc | 2 +- 15 files changed, 23 insertions(+), 30 deletions(-) diff --git a/shell/gpu/gpu_studio_gl_impeller.h b/shell/gpu/gpu_studio_gl_impeller.h index 9c54d7eb18283..cad5ff185c1a0 100644 --- a/shell/gpu/gpu_studio_gl_impeller.h +++ b/shell/gpu/gpu_studio_gl_impeller.h @@ -6,11 +6,11 @@ #define SHELL_GPU_GPU_STUDIO_GL_IMPELLER_H_ #include "flutter/common/graphics/gl_context_switch.h" +#include "flutter/flow/studio.h" #include "flutter/flow/surface.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/impeller/aiks/aiks_context.h" -#include "flutter/flow/studio.h" #include "flutter/impeller/renderer/context.h" #include "flutter/shell/gpu/gpu_surface_gl_delegate.h" @@ -19,7 +19,7 @@ namespace flutter { class GPUStudioGLImpeller final : public Studio { public: explicit GPUStudioGLImpeller(GPUSurfaceGLDelegate* delegate, - std::shared_ptr context); + std::shared_ptr context); // |Studio| ~GPUStudioGLImpeller() override; diff --git a/shell/gpu/gpu_studio_gl_skia.cc b/shell/gpu/gpu_studio_gl_skia.cc index b4dce0fbc51d6..00c36c7b6e29c 100644 --- a/shell/gpu/gpu_studio_gl_skia.cc +++ b/shell/gpu/gpu_studio_gl_skia.cc @@ -28,9 +28,8 @@ namespace flutter { GPUStudioGLSkia::GPUStudioGLSkia(const sk_sp& gr_context, - GPUSurfaceGLDelegate* delegate) - : delegate_(delegate), - context_(gr_context) { + GPUSurfaceGLDelegate* delegate) + : delegate_(delegate), context_(gr_context) { auto context_switch = delegate_->GLContextMakeCurrent(); if (!context_switch->GetResult()) { FML_LOG(ERROR) diff --git a/shell/gpu/gpu_studio_gl_skia.h b/shell/gpu/gpu_studio_gl_skia.h index 9dbedb0b6d58e..25ee264d1cfbf 100644 --- a/shell/gpu/gpu_studio_gl_skia.h +++ b/shell/gpu/gpu_studio_gl_skia.h @@ -10,8 +10,8 @@ #include "flutter/common/graphics/gl_context_switch.h" #include "flutter/flow/embedded_views.h" -#include "flutter/flow/surface.h" #include "flutter/flow/studio.h" +#include "flutter/flow/surface.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/shell/gpu/gpu_surface_gl_delegate.h" @@ -23,7 +23,7 @@ namespace flutter { class GPUStudioGLSkia : public Studio { public: GPUStudioGLSkia(const sk_sp& gr_context, - GPUSurfaceGLDelegate* delegate); + GPUSurfaceGLDelegate* delegate); // |Studio| ~GPUStudioGLSkia() override; @@ -44,7 +44,6 @@ class GPUStudioGLSkia : public Studio { bool AllowsDrawingWhenGpuDisabled() const override; private: - GPUSurfaceGLDelegate* delegate_; sk_sp context_; bool valid_ = false; diff --git a/shell/gpu/gpu_studio_metal_impeller.h b/shell/gpu/gpu_studio_metal_impeller.h index d9b534c487540..8ae9386530e90 100644 --- a/shell/gpu/gpu_studio_metal_impeller.h +++ b/shell/gpu/gpu_studio_metal_impeller.h @@ -20,7 +20,7 @@ namespace flutter { class SK_API_AVAILABLE_CA_METAL_LAYER GPUStudioMetalImpeller : public Studio { public: GPUStudioMetalImpeller(GPUSurfaceMetalDelegate* delegate, - const std::shared_ptr& context); + const std::shared_ptr& context); // |Studio| ~GPUStudioMetalImpeller(); diff --git a/shell/gpu/gpu_studio_metal_impeller.mm b/shell/gpu/gpu_studio_metal_impeller.mm index b62425a694d49..dbbe46c73738d 100644 --- a/shell/gpu/gpu_studio_metal_impeller.mm +++ b/shell/gpu/gpu_studio_metal_impeller.mm @@ -29,7 +29,7 @@ } GPUStudioMetalImpeller::GPUStudioMetalImpeller(GPUSurfaceMetalDelegate* delegate, - const std::shared_ptr& context) + const std::shared_ptr& context) : delegate_(delegate), impeller_renderer_(CreateImpellerRenderer(context)), aiks_context_( diff --git a/shell/gpu/gpu_studio_metal_skia.h b/shell/gpu/gpu_studio_metal_skia.h index 71d0c69351df0..842a926e1d5e8 100644 --- a/shell/gpu/gpu_studio_metal_skia.h +++ b/shell/gpu/gpu_studio_metal_skia.h @@ -6,8 +6,8 @@ #define FLUTTER_SHELL_GPU_GPU_STUDIO_METAL_H_ #include "flutter/common/graphics/msaa_sample_count.h" -#include "flutter/flow/surface.h" #include "flutter/flow/studio.h" +#include "flutter/flow/surface.h" #include "flutter/fml/macros.h" #include "flutter/shell/gpu/gpu_surface_metal_delegate.h" #include "third_party/skia/include/gpu/GrDirectContext.h" @@ -17,7 +17,7 @@ namespace flutter { class SK_API_AVAILABLE_CA_METAL_LAYER GPUStudioMetalSkia : public Studio { public: GPUStudioMetalSkia(GPUSurfaceMetalDelegate* delegate, - sk_sp context); + sk_sp context); // |Studio| ~GPUStudioMetalSkia(); diff --git a/shell/gpu/gpu_studio_metal_skia.mm b/shell/gpu/gpu_studio_metal_skia.mm index 6bc24ab95d5ef..91dc90e496fb8 100644 --- a/shell/gpu/gpu_studio_metal_skia.mm +++ b/shell/gpu/gpu_studio_metal_skia.mm @@ -29,10 +29,8 @@ namespace flutter { GPUStudioMetalSkia::GPUStudioMetalSkia(GPUSurfaceMetalDelegate* delegate, - sk_sp context) - : delegate_(delegate), - context_(std::move(context)) { -} + sk_sp context) + : delegate_(delegate), context_(std::move(context)) {} GPUStudioMetalSkia::~GPUStudioMetalSkia() = default; diff --git a/shell/gpu/gpu_studio_software.h b/shell/gpu/gpu_studio_software.h index 4eca32b8442f6..f953b66c2fa9b 100644 --- a/shell/gpu/gpu_studio_software.h +++ b/shell/gpu/gpu_studio_software.h @@ -5,11 +5,11 @@ #ifndef FLUTTER_SHELL_GPU_GPU_STUDIO_SOFTWARE_H_ #define FLUTTER_SHELL_GPU_GPU_STUDIO_SOFTWARE_H_ +#include "flutter/flow/studio.h" #include "flutter/flow/surface.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/shell/gpu/gpu_surface_software_delegate.h" -#include "flutter/flow/studio.h" namespace flutter { diff --git a/shell/gpu/gpu_studio_vulkan.cc b/shell/gpu/gpu_studio_vulkan.cc index 245a6370f039f..3ba20181ebcce 100644 --- a/shell/gpu/gpu_studio_vulkan.cc +++ b/shell/gpu/gpu_studio_vulkan.cc @@ -14,10 +14,8 @@ namespace flutter { GPUStudioVulkan::GPUStudioVulkan(GPUSurfaceVulkanDelegate* delegate, - const sk_sp& skia_context) - : delegate_(delegate), - skia_context_(skia_context), - weak_factory_(this) {} + const sk_sp& skia_context) + : delegate_(delegate), skia_context_(skia_context), weak_factory_(this) {} GPUStudioVulkan::~GPUStudioVulkan() = default; diff --git a/shell/gpu/gpu_studio_vulkan.h b/shell/gpu/gpu_studio_vulkan.h index ced9ae426bec5..4aef17f558836 100644 --- a/shell/gpu/gpu_studio_vulkan.h +++ b/shell/gpu/gpu_studio_vulkan.h @@ -7,13 +7,13 @@ #include +#include "flutter/flow/studio.h" #include "flutter/flow/surface.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/shell/gpu/gpu_surface_vulkan_delegate.h" #include "flutter/vulkan/vulkan_backbuffer.h" #include "flutter/vulkan/vulkan_native_surface.h" -#include "flutter/flow/studio.h" #include "flutter/vulkan/vulkan_window.h" #include "third_party/skia/include/core/SkRefCnt.h" @@ -31,7 +31,7 @@ class GPUStudioVulkan : public Studio { /// GrDirectContext. /// GPUStudioVulkan(GPUSurfaceVulkanDelegate* delegate, - const sk_sp& context); + const sk_sp& context); ~GPUStudioVulkan() override; diff --git a/shell/gpu/gpu_studio_vulkan_impeller.h b/shell/gpu/gpu_studio_vulkan_impeller.h index e25346a9e44de..4c516ad22b799 100644 --- a/shell/gpu/gpu_studio_vulkan_impeller.h +++ b/shell/gpu/gpu_studio_vulkan_impeller.h @@ -5,8 +5,8 @@ #pragma once #include "flutter/common/graphics/gl_context_switch.h" -#include "flutter/flow/surface.h" #include "flutter/flow/studio.h" +#include "flutter/flow/surface.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/impeller/aiks/aiks_context.h" diff --git a/shell/platform/embedder/embedder_studio_gl.cc b/shell/platform/embedder/embedder_studio_gl.cc index ba0dc47c2ea60..29693949ad619 100644 --- a/shell/platform/embedder/embedder_studio_gl.cc +++ b/shell/platform/embedder/embedder_studio_gl.cc @@ -7,8 +7,8 @@ #include #include "flutter/shell/common/shell_io_manager.h" -#include "flutter/shell/platform/embedder/embedder_surface_gl.h" #include "flutter/shell/gpu/gpu_studio_gl_skia.h" +#include "flutter/shell/platform/embedder/embedder_surface_gl.h" namespace flutter { @@ -104,8 +104,7 @@ std::unique_ptr EmbedderStudioGL::CreateGPUStudio() { if (!IsValid()) { return nullptr; } - auto studio = - std::make_unique(main_context_, this); + auto studio = std::make_unique(main_context_, this); if (!studio->IsValid()) { return nullptr; } diff --git a/shell/platform/embedder/embedder_studio_metal.mm b/shell/platform/embedder/embedder_studio_metal.mm index a6ab39298a3d0..0425abdcbe0f0 100644 --- a/shell/platform/embedder/embedder_studio_metal.mm +++ b/shell/platform/embedder/embedder_studio_metal.mm @@ -7,9 +7,9 @@ #include #include "flutter/fml/logging.h" +#include "flutter/shell/gpu/gpu_studio_metal_skia.h" #include "flutter/shell/gpu/gpu_surface_metal_delegate.h" #import "flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalSkia.h" -#include "flutter/shell/gpu/gpu_studio_metal_skia.h" #include "flutter/shell/platform/embedder/embedder_surface_metal.h" #include "third_party/skia/include/gpu/GrDirectContext.h" diff --git a/shell/platform/embedder/embedder_studio_software.cc b/shell/platform/embedder/embedder_studio_software.cc index c9baf961acf85..84a565a43c34d 100644 --- a/shell/platform/embedder/embedder_studio_software.cc +++ b/shell/platform/embedder/embedder_studio_software.cc @@ -8,9 +8,9 @@ #include "flutter/fml/trace_event.h" +#include "flutter/shell/gpu/gpu_studio_software.h" #include "flutter/shell/platform/embedder/embedder_studio_software.h" #include "flutter/shell/platform/embedder/embedder_surface_software.h" -#include "flutter/shell/gpu/gpu_studio_software.h" #include "third_party/skia/include/core/SkColorSpace.h" #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkSurface.h" diff --git a/shell/platform/embedder/embedder_studio_vulkan.cc b/shell/platform/embedder/embedder_studio_vulkan.cc index 4c7e98dcb9573..a741df7cdab1f 100644 --- a/shell/platform/embedder/embedder_studio_vulkan.cc +++ b/shell/platform/embedder/embedder_studio_vulkan.cc @@ -8,13 +8,13 @@ #include "flutter/flutter_vma/flutter_skia_vma.h" #include "flutter/shell/common/shell_io_manager.h" +#include "flutter/shell/gpu/gpu_studio_vulkan.h" #include "flutter/shell/gpu/gpu_surface_vulkan.h" #include "flutter/shell/gpu/gpu_surface_vulkan_delegate.h" #include "flutter/shell/platform/embedder/embedder_surface_vulkan.h" #include "flutter/vulkan/vulkan_skia_proc_table.h" #include "include/gpu/GrDirectContext.h" #include "include/gpu/vk/GrVkBackendContext.h" -#include "flutter/shell/gpu/gpu_studio_vulkan.h" #include "include/gpu/vk/GrVkExtensions.h" namespace flutter { From b1ced68d85157b5bb1f94880594c3acd310d7122 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 13:20:37 -0700 Subject: [PATCH 019/147] Add CreateGPUStudio to AndroidSurfaces --- .../android/android_surface_gl_impeller.cc | 14 ++++++++++ .../android/android_surface_gl_impeller.h | 3 +++ .../android/android_surface_gl_skia.cc | 26 ++++++++++++++++--- .../android/android_surface_gl_skia.h | 6 +++++ .../android/android_surface_software.cc | 17 ++++++++++++ .../android/android_surface_software.h | 3 +++ .../android_surface_vulkan_impeller.cc | 17 ++++++++++++ .../android/android_surface_vulkan_impeller.h | 3 +++ .../android/surface/android_surface.h | 4 +++ .../android/surface/android_surface_mock.h | 5 ++++ 10 files changed, 94 insertions(+), 4 deletions(-) diff --git a/shell/platform/android/android_surface_gl_impeller.cc b/shell/platform/android/android_surface_gl_impeller.cc index 3200822776a90..7e862f4390b32 100644 --- a/shell/platform/android/android_surface_gl_impeller.cc +++ b/shell/platform/android/android_surface_gl_impeller.cc @@ -9,6 +9,7 @@ #include "flutter/impeller/renderer/backend/gles/proc_table_gles.h" #include "flutter/impeller/toolkit/egl/context.h" #include "flutter/impeller/toolkit/egl/surface.h" +#include "flutter/shell/gpu/gpu_studio_gl_impeller.h" #include "flutter/shell/gpu/gpu_surface_gl_impeller.h" #include "impeller/entity/gles/entity_shaders_gles.h" #include "impeller/scene/shaders/gles/scene_shaders_gles.h" @@ -186,6 +187,19 @@ bool AndroidSurfaceGLImpeller::IsValid() const { return is_valid_; } +// |AndroidSurface| +std::unique_ptr AndroidSurfaceGLImpeller::CreateGPUStudio( + GrDirectContext* gr_context) override { + auto studio = + std::make_unique(this, // delegate + impeller_context_ // context + ); + if (!studio->IsValid()) { + return nullptr; + } + return studio; +} + // |AndroidSurface| std::unique_ptr AndroidSurfaceGLImpeller::CreateGPUSurface( GrDirectContext* gr_context) { diff --git a/shell/platform/android/android_surface_gl_impeller.h b/shell/platform/android/android_surface_gl_impeller.h index b8ca423072f60..d455242ff46c7 100644 --- a/shell/platform/android/android_surface_gl_impeller.h +++ b/shell/platform/android/android_surface_gl_impeller.h @@ -27,6 +27,9 @@ class AndroidSurfaceGLImpeller final : public GPUSurfaceGLDelegate, // |AndroidSurface| bool IsValid() const override; + // |AndroidSurface| + std::unique_ptr CreateGPUStudio(GrDirectContext* gr_context) override; + // |AndroidSurface| std::unique_ptr CreateGPUSurface( GrDirectContext* gr_context) override; diff --git a/shell/platform/android/android_surface_gl_skia.cc b/shell/platform/android/android_surface_gl_skia.cc index 343d691e2ae75..2f285e9747ab3 100644 --- a/shell/platform/android/android_surface_gl_skia.cc +++ b/shell/platform/android/android_surface_gl_skia.cc @@ -9,6 +9,8 @@ #include "flutter/fml/logging.h" #include "flutter/fml/memory/ref_ptr.h" +#include "flutter/shell/gpu/gpu_studio_gl_skia.h" +#include "flutter/shell/gpu/gpu_surface_gl_skia.h" #include "flutter/shell/platform/android/android_egl_surface.h" #include "flutter/shell/platform/android/android_shell_holder.h" @@ -48,11 +50,10 @@ bool AndroidSurfaceGLSkia::IsValid() const { return offscreen_surface_ && GLContextPtr()->IsValid(); } -std::unique_ptr AndroidSurfaceGLSkia::CreateGPUSurface( +static GrDirectContext* AndroidSurfaceGLSkia::UseExistingMainContextOrCreate( GrDirectContext* gr_context) { if (gr_context) { - return std::make_unique(sk_ref_sp(gr_context), this, - true); + return gr_context; } else { sk_sp main_skia_context = GLContextPtr()->GetMainSkiaContext(); @@ -60,8 +61,25 @@ std::unique_ptr AndroidSurfaceGLSkia::CreateGPUSurface( main_skia_context = GPUSurfaceGLSkia::MakeGLContext(this); GLContextPtr()->SetMainSkiaContext(main_skia_context); } - return std::make_unique(main_skia_context, this, true); + FML_DCHECK(GLContextPtr()->GetMainSkiaContext() == main_skia_context); + return main_skia_context; + } +} + +std::unique_ptr AndroidSurfaceGLSkia::CreateGPUStudio( + GrDirectContext* gr_context) override { + auto studio = std::make_unique( + sk_ref_sp(UseExistingMainContextOrCreate(gr_context)), + this) if (!studio->IsValid()) { + return nullptr; } + return studio; +} + +std::unique_ptr AndroidSurfaceGLSkia::CreateGPUSurface( + GrDirectContext* gr_context) { + return std::make_unique( + UseExistingMainContextOrCreate(gr_context), this, true); } bool AndroidSurfaceGLSkia::OnScreenSurfaceResize(const SkISize& size) { diff --git a/shell/platform/android/android_surface_gl_skia.h b/shell/platform/android/android_surface_gl_skia.h index 274c28f2c6760..1b30c82ea406d 100644 --- a/shell/platform/android/android_surface_gl_skia.h +++ b/shell/platform/android/android_surface_gl_skia.h @@ -30,6 +30,9 @@ class AndroidSurfaceGLSkia final : public GPUSurfaceGLDelegate, // |AndroidSurface| bool IsValid() const override; + // |AndroidSurface| + std::unique_ptr CreateGPUStudio() override; + // |AndroidSurface| std::unique_ptr CreateGPUSurface( GrDirectContext* gr_context) override; @@ -86,6 +89,9 @@ class AndroidSurfaceGLSkia final : public GPUSurfaceGLDelegate, std::unique_ptr onscreen_surface_; std::unique_ptr offscreen_surface_; + static sk_sp UseExistingMainContextOrCreate( + GrDirectContext* gr_context); + //---------------------------------------------------------------------------- /// @brief Takes the super class AndroidSurface's AndroidContext and /// return a raw pointer to an AndroidContextGL. diff --git a/shell/platform/android/android_surface_software.cc b/shell/platform/android/android_surface_software.cc index fdfc8d9d509ac..ae9f3afe9e55e 100644 --- a/shell/platform/android/android_surface_software.cc +++ b/shell/platform/android/android_surface_software.cc @@ -11,6 +11,8 @@ #include "flutter/fml/platform/android/jni_weak_ref.h" #include "flutter/fml/platform/android/scoped_java_ref.h" #include "flutter/fml/trace_event.h" +#include "flutter/shell/gpu/gpu_studio_gl_software.h" +#include "flutter/shell/gpu/gpu_surface_gl_software.h" #include "flutter/shell/platform/android/android_shell_holder.h" #include "flutter/shell/platform/android/jni/platform_view_android_jni.h" @@ -63,6 +65,21 @@ bool AndroidSurfaceSoftware::ResourceContextClearCurrent() { return false; } +std::unique_ptr AndroidSurfaceSoftware::CreateGPUStudio( + GrDirectContext* gr_context) override { + if (!IsValid()) { + return nullptr; + } + + auto studio = std::make_unique(this); + + if (!studio->IsValid()) { + return nullptr; + } + + return studio; +} + std::unique_ptr AndroidSurfaceSoftware::CreateGPUSurface( // The software AndroidSurface neither uses any passed in Skia context // nor does it interact with the AndroidContext's raster Skia context. diff --git a/shell/platform/android/android_surface_software.h b/shell/platform/android/android_surface_software.h index 38e4c498f3843..6817285a03b71 100644 --- a/shell/platform/android/android_surface_software.h +++ b/shell/platform/android/android_surface_software.h @@ -34,6 +34,9 @@ class AndroidSurfaceSoftware final : public AndroidSurface, // |AndroidSurface| bool ResourceContextClearCurrent() override; + // |AndroidSurface| + std::unique_ptr CreateGPUStudio(GrDirectContext* gr_context) override; + // |AndroidSurface| std::unique_ptr CreateGPUSurface( GrDirectContext* gr_context) override; diff --git a/shell/platform/android/android_surface_vulkan_impeller.cc b/shell/platform/android/android_surface_vulkan_impeller.cc index ca2b4f448ddc5..1c40705f01e03 100644 --- a/shell/platform/android/android_surface_vulkan_impeller.cc +++ b/shell/platform/android/android_surface_vulkan_impeller.cc @@ -11,6 +11,7 @@ #include "flutter/fml/logging.h" #include "flutter/fml/memory/ref_ptr.h" #include "flutter/impeller/renderer/backend/vulkan/context_vk.h" +#include "flutter/shell/gpu/gpu_studio_vulkan_impeller.h" #include "flutter/shell/gpu/gpu_surface_vulkan_impeller.h" #include "flutter/vulkan/vulkan_native_surface_android.h" #include "impeller/entity/vk/entity_shaders_vk.h" @@ -66,6 +67,22 @@ void AndroidSurfaceVulkanImpeller::TeardownOnScreenContext() { // Nothing to do. } +std::unique_ptr AndroidSurfaceVulkanImpeller::CreateGPUStudio( + GrDirectContext* gr_context) override { + if (!IsValid()) { + return nullptr; + } + + std::unique_ptr studio = + std::make_unique(impeller_context_); + + if (!studio->IsValid()) { + return nullptr; + } + + return studio; +} + std::unique_ptr AndroidSurfaceVulkanImpeller::CreateGPUSurface( GrDirectContext* gr_context) { if (!IsValid()) { diff --git a/shell/platform/android/android_surface_vulkan_impeller.h b/shell/platform/android/android_surface_vulkan_impeller.h index c96cbd21a8fe1..315da31f20c36 100644 --- a/shell/platform/android/android_surface_vulkan_impeller.h +++ b/shell/platform/android/android_surface_vulkan_impeller.h @@ -24,6 +24,9 @@ class AndroidSurfaceVulkanImpeller : public AndroidSurface { // |AndroidSurface| bool IsValid() const override; + // |AndroidSurface| + std::unique_ptr CreateGPUStudio(GrDirectContext* gr_context) override; + // |AndroidSurface| std::unique_ptr CreateGPUSurface( GrDirectContext* gr_context) override; diff --git a/shell/platform/android/surface/android_surface.h b/shell/platform/android/surface/android_surface.h index 485f619e1a81b..bc5414a1bd78e 100644 --- a/shell/platform/android/surface/android_surface.h +++ b/shell/platform/android/surface/android_surface.h @@ -7,6 +7,7 @@ #include #include "flutter/flow/embedded_views.h" +#include "flutter/flow/studio.h" #include "flutter/flow/surface.h" #include "flutter/fml/macros.h" #include "flutter/shell/platform/android/context/android_context.h" @@ -30,6 +31,9 @@ class AndroidSurface { virtual void TeardownOnScreenContext() = 0; + virtual std::unique_ptr CreateGPUStudio( + GrDirectContext* gr_context = nullptr) = 0; + virtual std::unique_ptr CreateGPUSurface( GrDirectContext* gr_context = nullptr) = 0; diff --git a/shell/platform/android/surface/android_surface_mock.h b/shell/platform/android/surface/android_surface_mock.h index 3f326a21920b7..fb2aae33f30d6 100644 --- a/shell/platform/android/surface/android_surface_mock.h +++ b/shell/platform/android/surface/android_surface_mock.h @@ -25,6 +25,11 @@ class AndroidSurfaceMock final : public GPUSurfaceGLDelegate, MOCK_METHOD(void, TeardownOnScreenContext, (), (override)); + MOCK_METHOD(std::unique_ptr, + CreateGPUStudio, + (GrDirectContext * gr_context), + (override)); + MOCK_METHOD(std::unique_ptr, CreateGPUSurface, (GrDirectContext * gr_context), From 6c7dfc90c6e13a51f3832333b4cc3e0d1e6c91cf Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 13:36:42 -0700 Subject: [PATCH 020/147] Add IOSSurface::CreateGPUStudio --- shell/platform/darwin/ios/ios_surface.h | 4 ++++ .../darwin/ios/ios_surface_metal_impeller.h | 3 +++ .../darwin/ios/ios_surface_metal_impeller.mm | 10 ++++++++++ shell/platform/darwin/ios/ios_surface_metal_skia.h | 3 +++ .../platform/darwin/ios/ios_surface_metal_skia.mm | 8 ++++++++ shell/platform/darwin/ios/ios_surface_software.h | 3 +++ shell/platform/darwin/ios/ios_surface_software.mm | 14 ++++++++++++++ 7 files changed, 45 insertions(+) diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index 78394f25781c6..90ec430c6569e 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -11,6 +11,7 @@ #include "flutter/flow/embedded_views.h" #include "flutter/flow/surface.h" +#include "flutter/flow/studio.h" #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" @@ -35,6 +36,9 @@ class IOSSurface { virtual void UpdateStorageSizeIfNecessary() = 0; + virtual std::unique_ptr CreateGPUStudio( + GrDirectContext* gr_context = nullptr) = 0; + // Creates a GPU surface. If no GrDirectContext is supplied and the rendering mode // supports one, a new one will be created; otherwise, the software backend // will be used. diff --git a/shell/platform/darwin/ios/ios_surface_metal_impeller.h b/shell/platform/darwin/ios/ios_surface_metal_impeller.h index 6c3615318a9ca..9f29a9cc68dc3 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_impeller.h +++ b/shell/platform/darwin/ios/ios_surface_metal_impeller.h @@ -38,6 +38,9 @@ class SK_API_AVAILABLE_CA_METAL_LAYER IOSSurfaceMetalImpeller final // |IOSSurface| void UpdateStorageSizeIfNecessary() override; + // |IOSSurface| + std::unique_ptr CreateGPUStudio(GrDirectContext* gr_context) override; + // |IOSSurface| std::unique_ptr CreateGPUSurface(GrDirectContext* gr_context) override; diff --git a/shell/platform/darwin/ios/ios_surface_metal_impeller.mm b/shell/platform/darwin/ios/ios_surface_metal_impeller.mm index c1696ff309d70..8871c5551de08 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_impeller.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_impeller.mm @@ -103,6 +103,16 @@ PixelFormat GetColorAttachmentPixelFormat() const override { // Nothing to do. } +// |IOSSurface| +std::unique_ptr IOSSurfaceMetalImpeller::CreateGPUStudio(GrDirectContext*) override { + auto context = std::make_shared( + impeller_context_, + InferOffscreenLayerPixelFormat(FromMTLPixelFormat(layer_.get().pixelFormat))); + return std::make_unique(this, // + context // + ); +} + // |IOSSurface| std::unique_ptr IOSSurfaceMetalImpeller::CreateGPUSurface(GrDirectContext*) { auto context = std::make_shared( diff --git a/shell/platform/darwin/ios/ios_surface_metal_skia.h b/shell/platform/darwin/ios/ios_surface_metal_skia.h index efd174b07e09d..80c1f0f5cfbd3 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_skia.h +++ b/shell/platform/darwin/ios/ios_surface_metal_skia.h @@ -35,6 +35,9 @@ class SK_API_AVAILABLE_CA_METAL_LAYER IOSSurfaceMetalSkia final : public IOSSurf // |IOSSurface| void UpdateStorageSizeIfNecessary() override; + // |IOSSurface| + std::unique_ptr CreateGPUStudio(GrDirectContext* gr_context = nullptr) override; + // |IOSSurface| std::unique_ptr CreateGPUSurface(GrDirectContext* gr_context) override; diff --git a/shell/platform/darwin/ios/ios_surface_metal_skia.mm b/shell/platform/darwin/ios/ios_surface_metal_skia.mm index 13ac639a2fe0c..ab320edfa6de0 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_skia.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_skia.mm @@ -39,6 +39,14 @@ // Nothing to do. } +// |IOSSurface| +std::unique_ptr IOSSurfaceMetalSkia::CreateGPUStudio(GrDirectContext* gr_context) override { + FML_DCHECK(context); + return std::make_unique(this, // delegate + sk_ref_sp(context), // context + ); +} + // |IOSSurface| std::unique_ptr IOSSurfaceMetalSkia::CreateGPUSurface(GrDirectContext* context) { FML_DCHECK(context); diff --git a/shell/platform/darwin/ios/ios_surface_software.h b/shell/platform/darwin/ios/ios_surface_software.h index e1ed6b69518c0..e0529df886bf1 100644 --- a/shell/platform/darwin/ios/ios_surface_software.h +++ b/shell/platform/darwin/ios/ios_surface_software.h @@ -31,6 +31,9 @@ class IOSSurfaceSoftware final : public IOSSurface, public GPUSurfaceSoftwareDel // |IOSSurface| void UpdateStorageSizeIfNecessary() override; + // |IOSSurface| + std::unique_ptr CreateGPUStudio(GrDirectContext* gr_context = nullptr) override; + // |IOSSurface| std::unique_ptr CreateGPUSurface(GrDirectContext* gr_context = nullptr) override; diff --git a/shell/platform/darwin/ios/ios_surface_software.mm b/shell/platform/darwin/ios/ios_surface_software.mm index 2e87510cc0885..abc197e1ef98f 100644 --- a/shell/platform/darwin/ios/ios_surface_software.mm +++ b/shell/platform/darwin/ios/ios_surface_software.mm @@ -34,6 +34,20 @@ // Android oddities. } +std::unique_ptr IOSSurfaceSoftware::CreateGPUStudio(GrDirectContext* gr_context) override { + if (!IsValid()) { + return nullptr; + } + + auto studio = std::make_unique(this); + + if (!studio->IsValid()) { + return nullptr; + } + + return studio; +} + std::unique_ptr IOSSurfaceSoftware::CreateGPUSurface(GrDirectContext* gr_context) { if (!IsValid()) { return nullptr; From fa138f14580f423518ab12ed71134c894273c37a Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 13:37:19 -0700 Subject: [PATCH 021/147] Format --- shell/platform/darwin/ios/ios_surface.h | 5 ++--- shell/platform/darwin/ios/ios_surface_metal_skia.mm | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index 90ec430c6569e..c93eb886f5a25 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -10,8 +10,8 @@ #include #include "flutter/flow/embedded_views.h" -#include "flutter/flow/surface.h" #include "flutter/flow/studio.h" +#include "flutter/flow/surface.h" #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" @@ -36,8 +36,7 @@ class IOSSurface { virtual void UpdateStorageSizeIfNecessary() = 0; - virtual std::unique_ptr CreateGPUStudio( - GrDirectContext* gr_context = nullptr) = 0; + virtual std::unique_ptr CreateGPUStudio(GrDirectContext* gr_context = nullptr) = 0; // Creates a GPU surface. If no GrDirectContext is supplied and the rendering mode // supports one, a new one will be created; otherwise, the software backend diff --git a/shell/platform/darwin/ios/ios_surface_metal_skia.mm b/shell/platform/darwin/ios/ios_surface_metal_skia.mm index ab320edfa6de0..47daaf1ee29a6 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_skia.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_skia.mm @@ -42,8 +42,8 @@ // |IOSSurface| std::unique_ptr IOSSurfaceMetalSkia::CreateGPUStudio(GrDirectContext* gr_context) override { FML_DCHECK(context); - return std::make_unique(this, // delegate - sk_ref_sp(context), // context + return std::make_unique(this, // delegate + sk_ref_sp(context), // context ); } From 116d9b32b10f63bcdb8203d03871f83eb6856ea9 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 13:42:03 -0700 Subject: [PATCH 022/147] Compile --- shell/gpu/gpu_studio_vulkan.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/gpu/gpu_studio_vulkan.cc b/shell/gpu/gpu_studio_vulkan.cc index 3ba20181ebcce..7f150e6cb9287 100644 --- a/shell/gpu/gpu_studio_vulkan.cc +++ b/shell/gpu/gpu_studio_vulkan.cc @@ -15,7 +15,7 @@ namespace flutter { GPUStudioVulkan::GPUStudioVulkan(GPUSurfaceVulkanDelegate* delegate, const sk_sp& skia_context) - : delegate_(delegate), skia_context_(skia_context), weak_factory_(this) {} + : delegate_(delegate), skia_context_(skia_context) {} GPUStudioVulkan::~GPUStudioVulkan() = default; From 1454b830ee5d2a22d3391cd6fcbc408b5e228119 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 14:35:48 -0700 Subject: [PATCH 023/147] Try to fix compile --- shell/platform/android/android_surface_gl_impeller.cc | 2 +- shell/platform/android/android_surface_gl_skia.cc | 7 ++++--- shell/platform/android/android_surface_gl_skia.h | 4 ++-- shell/platform/android/android_surface_software.cc | 6 +++--- shell/platform/android/android_surface_vulkan_impeller.cc | 2 +- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/shell/platform/android/android_surface_gl_impeller.cc b/shell/platform/android/android_surface_gl_impeller.cc index 7e862f4390b32..8cf716c594af9 100644 --- a/shell/platform/android/android_surface_gl_impeller.cc +++ b/shell/platform/android/android_surface_gl_impeller.cc @@ -189,7 +189,7 @@ bool AndroidSurfaceGLImpeller::IsValid() const { // |AndroidSurface| std::unique_ptr AndroidSurfaceGLImpeller::CreateGPUStudio( - GrDirectContext* gr_context) override { + GrDirectContext* gr_context) { auto studio = std::make_unique(this, // delegate impeller_context_ // context diff --git a/shell/platform/android/android_surface_gl_skia.cc b/shell/platform/android/android_surface_gl_skia.cc index 2f285e9747ab3..39c3f5c343818 100644 --- a/shell/platform/android/android_surface_gl_skia.cc +++ b/shell/platform/android/android_surface_gl_skia.cc @@ -50,7 +50,7 @@ bool AndroidSurfaceGLSkia::IsValid() const { return offscreen_surface_ && GLContextPtr()->IsValid(); } -static GrDirectContext* AndroidSurfaceGLSkia::UseExistingMainContextOrCreate( +GrDirectContext* AndroidSurfaceGLSkia::UseExistingMainContextOrCreate( GrDirectContext* gr_context) { if (gr_context) { return gr_context; @@ -67,10 +67,11 @@ static GrDirectContext* AndroidSurfaceGLSkia::UseExistingMainContextOrCreate( } std::unique_ptr AndroidSurfaceGLSkia::CreateGPUStudio( - GrDirectContext* gr_context) override { + GrDirectContext* gr_context) { auto studio = std::make_unique( sk_ref_sp(UseExistingMainContextOrCreate(gr_context)), - this) if (!studio->IsValid()) { + this); + if (!studio->IsValid()) { return nullptr; } return studio; diff --git a/shell/platform/android/android_surface_gl_skia.h b/shell/platform/android/android_surface_gl_skia.h index 1b30c82ea406d..78b82577b2582 100644 --- a/shell/platform/android/android_surface_gl_skia.h +++ b/shell/platform/android/android_surface_gl_skia.h @@ -31,7 +31,7 @@ class AndroidSurfaceGLSkia final : public GPUSurfaceGLDelegate, bool IsValid() const override; // |AndroidSurface| - std::unique_ptr CreateGPUStudio() override; + std::unique_ptr CreateGPUStudio(GrDirectContext* gr_context) override; // |AndroidSurface| std::unique_ptr CreateGPUSurface( @@ -89,7 +89,7 @@ class AndroidSurfaceGLSkia final : public GPUSurfaceGLDelegate, std::unique_ptr onscreen_surface_; std::unique_ptr offscreen_surface_; - static sk_sp UseExistingMainContextOrCreate( + GrDirectContext* UseExistingMainContextOrCreate( GrDirectContext* gr_context); //---------------------------------------------------------------------------- diff --git a/shell/platform/android/android_surface_software.cc b/shell/platform/android/android_surface_software.cc index ae9f3afe9e55e..7c48418178bb0 100644 --- a/shell/platform/android/android_surface_software.cc +++ b/shell/platform/android/android_surface_software.cc @@ -11,8 +11,8 @@ #include "flutter/fml/platform/android/jni_weak_ref.h" #include "flutter/fml/platform/android/scoped_java_ref.h" #include "flutter/fml/trace_event.h" -#include "flutter/shell/gpu/gpu_studio_gl_software.h" -#include "flutter/shell/gpu/gpu_surface_gl_software.h" +#include "flutter/shell/gpu/gpu_studio_software.h" +#include "flutter/shell/gpu/gpu_surface_software.h" #include "flutter/shell/platform/android/android_shell_holder.h" #include "flutter/shell/platform/android/jni/platform_view_android_jni.h" @@ -66,7 +66,7 @@ bool AndroidSurfaceSoftware::ResourceContextClearCurrent() { } std::unique_ptr AndroidSurfaceSoftware::CreateGPUStudio( - GrDirectContext* gr_context) override { + GrDirectContext* gr_context) { if (!IsValid()) { return nullptr; } diff --git a/shell/platform/android/android_surface_vulkan_impeller.cc b/shell/platform/android/android_surface_vulkan_impeller.cc index 1c40705f01e03..a1802664d61d2 100644 --- a/shell/platform/android/android_surface_vulkan_impeller.cc +++ b/shell/platform/android/android_surface_vulkan_impeller.cc @@ -68,7 +68,7 @@ void AndroidSurfaceVulkanImpeller::TeardownOnScreenContext() { } std::unique_ptr AndroidSurfaceVulkanImpeller::CreateGPUStudio( - GrDirectContext* gr_context) override { + GrDirectContext* gr_context) { if (!IsValid()) { return nullptr; } From 6c8b153990049f59606a97f9b9eecc0a15804e74 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 14:36:40 -0700 Subject: [PATCH 024/147] fuchsia studio --- shell/platform/fuchsia/flutter/BUILD.gn | 2 ++ shell/platform/fuchsia/flutter/engine.cc | 4 +++ shell/platform/fuchsia/flutter/engine.h | 5 +++- shell/platform/fuchsia/flutter/studio.cc | 31 +++++++++++++++++++++ shell/platform/fuchsia/flutter/studio.h | 34 ++++++++++++++++++++++++ 5 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 shell/platform/fuchsia/flutter/studio.cc create mode 100644 shell/platform/fuchsia/flutter/studio.h diff --git a/shell/platform/fuchsia/flutter/BUILD.gn b/shell/platform/fuchsia/flutter/BUILD.gn index cc1407fa98a0f..86def4be58711 100644 --- a/shell/platform/fuchsia/flutter/BUILD.gn +++ b/shell/platform/fuchsia/flutter/BUILD.gn @@ -99,6 +99,8 @@ template("runner_sources") { "software_surface.h", "software_surface_producer.cc", "software_surface_producer.h", + "studio.cc", + "studio.h", "surface.cc", "surface.h", "surface_producer.h", diff --git a/shell/platform/fuchsia/flutter/engine.cc b/shell/platform/fuchsia/flutter/engine.cc index d623804c1712b..8146468c7b737 100644 --- a/shell/platform/fuchsia/flutter/engine.cc +++ b/shell/platform/fuchsia/flutter/engine.cc @@ -833,6 +833,10 @@ void Engine::DestroyFlatlandView(int64_t view_id, }); } +std::unique_ptr Engine::CreateStudio() { + return std::make_unique(surface_producer_->gr_context()); +} + std::unique_ptr Engine::CreateSurface() { return std::make_unique(thread_label_, GetExternalViewEmbedder(), surface_producer_->gr_context()); diff --git a/shell/platform/fuchsia/flutter/engine.h b/shell/platform/fuchsia/flutter/engine.h index e11c41e878c14..eac798a1a1ee9 100644 --- a/shell/platform/fuchsia/flutter/engine.h +++ b/shell/platform/fuchsia/flutter/engine.h @@ -19,7 +19,6 @@ #include #include "flutter/flow/embedded_views.h" -#include "flutter/flow/surface.h" #include "flutter/fml/macros.h" #include "flutter/shell/common/shell.h" #include "flutter/shell/common/thread_host.h" @@ -31,7 +30,9 @@ #include "gfx_external_view_embedder.h" #include "gfx_session_connection.h" #include "isolate_configurator.h" +#include "studio.h" #include "surface_producer.h" +#include "surface.h" namespace flutter_runner { @@ -144,6 +145,8 @@ class Engine final : public fuchsia::memorypressure::Watcher { std::shared_ptr GetExternalViewEmbedder(); + std::unique_ptr CreateStudio(); + std::unique_ptr CreateSurface(); Delegate& delegate_; diff --git a/shell/platform/fuchsia/flutter/studio.cc b/shell/platform/fuchsia/flutter/studio.cc new file mode 100644 index 0000000000000..c1a6f5240daae --- /dev/null +++ b/shell/platform/fuchsia/flutter/studio.cc @@ -0,0 +1,31 @@ +// 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. + +#include "studio.h" + +#include +#include +#include +#include + +#include "flutter/fml/unique_fd.h" + +namespace flutter_runner { + +Studio::Studio(GrDirectContext* gr_context) + : gr_context_(gr_context) {} + +Studio::~Studio() = default; + +// |flutter::Studio| +bool Studio::IsValid() { + return true; +} + +// |flutter::Studio| +GrDirectContext* Studio::GetContext() { + return gr_context_; +} + +} // namespace flutter_runner diff --git a/shell/platform/fuchsia/flutter/studio.h b/shell/platform/fuchsia/flutter/studio.h new file mode 100644 index 0000000000000..1560238e74ee3 --- /dev/null +++ b/shell/platform/fuchsia/flutter/studio.h @@ -0,0 +1,34 @@ +// 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. + +#pragma once + +#include "flutter/flow/studio.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/memory/weak_ptr.h" + +namespace flutter_runner { + +// The interface between the Flutter rasterizer and the underlying platform. May +// be constructed on any thread but will be used by the engine only on the +// raster thread. +class Studio final : public flutter::Studio { + public: + Studio(GrDirectContext* gr_context); + + ~Studio() override; + + private: + GrDirectContext* gr_context_; + + // |flutter::Studio| + bool IsValid() override; + + // |flutter::Studio| + GrDirectContext* GetContext() override; + + FML_DISALLOW_COPY_AND_ASSIGN(Studio); +}; + +} // namespace flutter_runner From 52ee277ad3ceac59c9e1e991ccb1b4b27741b0eb Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 14:37:40 -0700 Subject: [PATCH 025/147] Format --- shell/platform/android/android_surface_gl_skia.cc | 5 ++--- shell/platform/android/android_surface_gl_skia.h | 3 +-- shell/platform/fuchsia/flutter/engine.h | 2 +- shell/platform/fuchsia/flutter/studio.cc | 3 +-- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/shell/platform/android/android_surface_gl_skia.cc b/shell/platform/android/android_surface_gl_skia.cc index 39c3f5c343818..644c17a8b0f0d 100644 --- a/shell/platform/android/android_surface_gl_skia.cc +++ b/shell/platform/android/android_surface_gl_skia.cc @@ -69,9 +69,8 @@ GrDirectContext* AndroidSurfaceGLSkia::UseExistingMainContextOrCreate( std::unique_ptr AndroidSurfaceGLSkia::CreateGPUStudio( GrDirectContext* gr_context) { auto studio = std::make_unique( - sk_ref_sp(UseExistingMainContextOrCreate(gr_context)), - this); - if (!studio->IsValid()) { + sk_ref_sp(UseExistingMainContextOrCreate(gr_context)), this); + if (!studio->IsValid()) { return nullptr; } return studio; diff --git a/shell/platform/android/android_surface_gl_skia.h b/shell/platform/android/android_surface_gl_skia.h index 78b82577b2582..b7517a2c0745f 100644 --- a/shell/platform/android/android_surface_gl_skia.h +++ b/shell/platform/android/android_surface_gl_skia.h @@ -89,8 +89,7 @@ class AndroidSurfaceGLSkia final : public GPUSurfaceGLDelegate, std::unique_ptr onscreen_surface_; std::unique_ptr offscreen_surface_; - GrDirectContext* UseExistingMainContextOrCreate( - GrDirectContext* gr_context); + GrDirectContext* UseExistingMainContextOrCreate(GrDirectContext* gr_context); //---------------------------------------------------------------------------- /// @brief Takes the super class AndroidSurface's AndroidContext and diff --git a/shell/platform/fuchsia/flutter/engine.h b/shell/platform/fuchsia/flutter/engine.h index eac798a1a1ee9..06097a9692f85 100644 --- a/shell/platform/fuchsia/flutter/engine.h +++ b/shell/platform/fuchsia/flutter/engine.h @@ -31,8 +31,8 @@ #include "gfx_session_connection.h" #include "isolate_configurator.h" #include "studio.h" -#include "surface_producer.h" #include "surface.h" +#include "surface_producer.h" namespace flutter_runner { diff --git a/shell/platform/fuchsia/flutter/studio.cc b/shell/platform/fuchsia/flutter/studio.cc index c1a6f5240daae..2b40a531586fa 100644 --- a/shell/platform/fuchsia/flutter/studio.cc +++ b/shell/platform/fuchsia/flutter/studio.cc @@ -13,8 +13,7 @@ namespace flutter_runner { -Studio::Studio(GrDirectContext* gr_context) - : gr_context_(gr_context) {} +Studio::Studio(GrDirectContext* gr_context) : gr_context_(gr_context) {} Studio::~Studio() = default; From 6e298c4843e954675f81feda975ebecc5aa3c6a6 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 14:49:37 -0700 Subject: [PATCH 026/147] Compile --- shell/platform/android/android_surface_gl_skia.cc | 6 +++--- shell/platform/android/android_surface_gl_skia.h | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/shell/platform/android/android_surface_gl_skia.cc b/shell/platform/android/android_surface_gl_skia.cc index 644c17a8b0f0d..5b094bef6f342 100644 --- a/shell/platform/android/android_surface_gl_skia.cc +++ b/shell/platform/android/android_surface_gl_skia.cc @@ -50,10 +50,10 @@ bool AndroidSurfaceGLSkia::IsValid() const { return offscreen_surface_ && GLContextPtr()->IsValid(); } -GrDirectContext* AndroidSurfaceGLSkia::UseExistingMainContextOrCreate( +sk_sp AndroidSurfaceGLSkia::UseExistingMainContextOrCreate( GrDirectContext* gr_context) { if (gr_context) { - return gr_context; + return sk_ref_sp(gr_context); } else { sk_sp main_skia_context = GLContextPtr()->GetMainSkiaContext(); @@ -69,7 +69,7 @@ GrDirectContext* AndroidSurfaceGLSkia::UseExistingMainContextOrCreate( std::unique_ptr AndroidSurfaceGLSkia::CreateGPUStudio( GrDirectContext* gr_context) { auto studio = std::make_unique( - sk_ref_sp(UseExistingMainContextOrCreate(gr_context)), this); + UseExistingMainContextOrCreate(gr_context), this); if (!studio->IsValid()) { return nullptr; } diff --git a/shell/platform/android/android_surface_gl_skia.h b/shell/platform/android/android_surface_gl_skia.h index b7517a2c0745f..43b731a9829d6 100644 --- a/shell/platform/android/android_surface_gl_skia.h +++ b/shell/platform/android/android_surface_gl_skia.h @@ -89,7 +89,8 @@ class AndroidSurfaceGLSkia final : public GPUSurfaceGLDelegate, std::unique_ptr onscreen_surface_; std::unique_ptr offscreen_surface_; - GrDirectContext* UseExistingMainContextOrCreate(GrDirectContext* gr_context); + sk_sp UseExistingMainContextOrCreate( + GrDirectContext* gr_context); //---------------------------------------------------------------------------- /// @brief Takes the super class AndroidSurface's AndroidContext and From aeac80f7bc85748051fa076a168c3de16023f04d Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 14:55:28 -0700 Subject: [PATCH 027/147] Compile --- ci/licenses_golden/licenses_flutter | 4 ++++ shell/gpu/gpu_studio_vulkan.cc | 5 ++--- shell/gpu/gpu_studio_vulkan.h | 4 +--- shell/platform/darwin/ios/ios_surface_metal_impeller.mm | 3 ++- shell/platform/darwin/ios/ios_surface_metal_skia.mm | 3 ++- shell/platform/darwin/ios/ios_surface_software.mm | 4 +++- shell/platform/embedder/embedder_studio_vulkan.cc | 2 +- 7 files changed, 15 insertions(+), 10 deletions(-) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 8460892f4dcfd..67ae72eb5cb98 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -2824,6 +2824,8 @@ ORIGIN: ../../../flutter/shell/platform/fuchsia/flutter/software_surface.cc + .. ORIGIN: ../../../flutter/shell/platform/fuchsia/flutter/software_surface.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/fuchsia/flutter/software_surface_producer.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/fuchsia/flutter/software_surface_producer.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/fuchsia/flutter/studio.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/fuchsia/flutter/studio.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/fuchsia/flutter/surface.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/fuchsia/flutter/surface.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/fuchsia/flutter/surface_producer.h + ../../../flutter/LICENSE @@ -5403,6 +5405,8 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/software_surface.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/software_surface.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/software_surface_producer.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/software_surface_producer.h +FILE: ../../../flutter/shell/platform/fuchsia/flutter/studio.cc +FILE: ../../../flutter/shell/platform/fuchsia/flutter/studio.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/surface.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/surface.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/surface_producer.h diff --git a/shell/gpu/gpu_studio_vulkan.cc b/shell/gpu/gpu_studio_vulkan.cc index 7f150e6cb9287..90453b9f4bd22 100644 --- a/shell/gpu/gpu_studio_vulkan.cc +++ b/shell/gpu/gpu_studio_vulkan.cc @@ -13,9 +13,8 @@ namespace flutter { -GPUStudioVulkan::GPUStudioVulkan(GPUSurfaceVulkanDelegate* delegate, - const sk_sp& skia_context) - : delegate_(delegate), skia_context_(skia_context) {} +GPUStudioVulkan::GPUStudioVulkan(const sk_sp& skia_context) + : skia_context_(skia_context) {} GPUStudioVulkan::~GPUStudioVulkan() = default; diff --git a/shell/gpu/gpu_studio_vulkan.h b/shell/gpu/gpu_studio_vulkan.h index 4aef17f558836..294a2572ac247 100644 --- a/shell/gpu/gpu_studio_vulkan.h +++ b/shell/gpu/gpu_studio_vulkan.h @@ -30,8 +30,7 @@ class GPUStudioVulkan : public Studio { /// @brief Create a GPUStudioVulkan while letting it reuse an existing /// GrDirectContext. /// - GPUStudioVulkan(GPUSurfaceVulkanDelegate* delegate, - const sk_sp& context); + GPUStudioVulkan(const sk_sp& context); ~GPUStudioVulkan() override; @@ -44,7 +43,6 @@ class GPUStudioVulkan : public Studio { static SkColorType ColorTypeFromFormat(const VkFormat format); private: - GPUSurfaceVulkanDelegate* delegate_; sk_sp skia_context_; FML_DISALLOW_COPY_AND_ASSIGN(GPUStudioVulkan); diff --git a/shell/platform/darwin/ios/ios_surface_metal_impeller.mm b/shell/platform/darwin/ios/ios_surface_metal_impeller.mm index 8871c5551de08..d2c48a87f2aba 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_impeller.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_impeller.mm @@ -6,6 +6,7 @@ #include "flutter/impeller/renderer/backend/metal/formats_mtl.h" #include "flutter/impeller/renderer/context.h" +#include "flutter/shell/gpu/gpu_studio_metal_impeller.h" #include "flutter/shell/gpu/gpu_surface_metal_impeller.h" namespace { @@ -104,7 +105,7 @@ PixelFormat GetColorAttachmentPixelFormat() const override { } // |IOSSurface| -std::unique_ptr IOSSurfaceMetalImpeller::CreateGPUStudio(GrDirectContext*) override { +std::unique_ptr IOSSurfaceMetalImpeller::CreateGPUStudio(GrDirectContext*) { auto context = std::make_shared( impeller_context_, InferOffscreenLayerPixelFormat(FromMTLPixelFormat(layer_.get().pixelFormat))); diff --git a/shell/platform/darwin/ios/ios_surface_metal_skia.mm b/shell/platform/darwin/ios/ios_surface_metal_skia.mm index 47daaf1ee29a6..c561d67a6fc38 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_skia.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_skia.mm @@ -6,6 +6,7 @@ #include "flutter/shell/gpu/gpu_surface_metal_delegate.h" #include "flutter/shell/gpu/gpu_surface_metal_skia.h" +#include "flutter/shell/gpu/gpu_studio_metal_skia.h" #include "flutter/shell/platform/darwin/ios/ios_context_metal_skia.h" namespace flutter { @@ -40,7 +41,7 @@ } // |IOSSurface| -std::unique_ptr IOSSurfaceMetalSkia::CreateGPUStudio(GrDirectContext* gr_context) override { +std::unique_ptr IOSSurfaceMetalSkia::CreateGPUStudio(GrDirectContext* gr_context) { FML_DCHECK(context); return std::make_unique(this, // delegate sk_ref_sp(context), // context diff --git a/shell/platform/darwin/ios/ios_surface_software.mm b/shell/platform/darwin/ios/ios_surface_software.mm index abc197e1ef98f..481f3f331d4e6 100644 --- a/shell/platform/darwin/ios/ios_surface_software.mm +++ b/shell/platform/darwin/ios/ios_surface_software.mm @@ -11,6 +11,8 @@ #include "flutter/fml/logging.h" #include "flutter/fml/platform/darwin/cf_utils.h" #include "flutter/fml/trace_event.h" +#include "flutter/shell/gpu/gpu_surface_software.h" +#include "flutter/shell/gpu/gpu_studio_software.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/utils/mac/SkCGUtils.h" @@ -34,7 +36,7 @@ // Android oddities. } -std::unique_ptr IOSSurfaceSoftware::CreateGPUStudio(GrDirectContext* gr_context) override { +std::unique_ptr IOSSurfaceSoftware::CreateGPUStudio(GrDirectContext* gr_context) { if (!IsValid()) { return nullptr; } diff --git a/shell/platform/embedder/embedder_studio_vulkan.cc b/shell/platform/embedder/embedder_studio_vulkan.cc index a741df7cdab1f..1a03b103a7220 100644 --- a/shell/platform/embedder/embedder_studio_vulkan.cc +++ b/shell/platform/embedder/embedder_studio_vulkan.cc @@ -114,7 +114,7 @@ std::unique_ptr EmbedderStudioVulkan::CreateGPUStudio() { if (!IsValid()) { return nullptr; } - auto studio = std::make_unique(this, main_context_); + auto studio = std::make_unique(main_context_); if (!studio->IsValid()) { return nullptr; From c985269f67236df83f02f8c2eed362092551384d Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 14:55:38 -0700 Subject: [PATCH 028/147] Format --- shell/platform/darwin/ios/ios_surface_metal_skia.mm | 2 +- shell/platform/darwin/ios/ios_surface_software.mm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/ios_surface_metal_skia.mm b/shell/platform/darwin/ios/ios_surface_metal_skia.mm index c561d67a6fc38..16762b4db6b29 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_skia.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_skia.mm @@ -4,9 +4,9 @@ #import "flutter/shell/platform/darwin/ios/ios_surface_metal_skia.h" +#include "flutter/shell/gpu/gpu_studio_metal_skia.h" #include "flutter/shell/gpu/gpu_surface_metal_delegate.h" #include "flutter/shell/gpu/gpu_surface_metal_skia.h" -#include "flutter/shell/gpu/gpu_studio_metal_skia.h" #include "flutter/shell/platform/darwin/ios/ios_context_metal_skia.h" namespace flutter { diff --git a/shell/platform/darwin/ios/ios_surface_software.mm b/shell/platform/darwin/ios/ios_surface_software.mm index 481f3f331d4e6..a61221248ee76 100644 --- a/shell/platform/darwin/ios/ios_surface_software.mm +++ b/shell/platform/darwin/ios/ios_surface_software.mm @@ -11,8 +11,8 @@ #include "flutter/fml/logging.h" #include "flutter/fml/platform/darwin/cf_utils.h" #include "flutter/fml/trace_event.h" -#include "flutter/shell/gpu/gpu_surface_software.h" #include "flutter/shell/gpu/gpu_studio_software.h" +#include "flutter/shell/gpu/gpu_surface_software.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/utils/mac/SkCGUtils.h" From c2e9f57e6da974880e77ad721ea67491e43be4c6 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 15:16:25 -0700 Subject: [PATCH 029/147] Compile --- shell/gpu/gpu_studio_gl_impeller.cc | 2 +- shell/gpu/gpu_studio_gl_impeller.h | 2 +- shell/platform/darwin/ios/ios_surface_metal_skia.mm | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/shell/gpu/gpu_studio_gl_impeller.cc b/shell/gpu/gpu_studio_gl_impeller.cc index 1ffb0e38ed2fb..70dfad1e438f0 100644 --- a/shell/gpu/gpu_studio_gl_impeller.cc +++ b/shell/gpu/gpu_studio_gl_impeller.cc @@ -13,7 +13,7 @@ namespace flutter { GPUStudioGLImpeller::GPUStudioGLImpeller( GPUSurfaceGLDelegate* delegate, - std::shared_ptr context) { + const std::shared_ptr& context) { if (delegate == nullptr) { return; } diff --git a/shell/gpu/gpu_studio_gl_impeller.h b/shell/gpu/gpu_studio_gl_impeller.h index cad5ff185c1a0..9f26e8d022e83 100644 --- a/shell/gpu/gpu_studio_gl_impeller.h +++ b/shell/gpu/gpu_studio_gl_impeller.h @@ -19,7 +19,7 @@ namespace flutter { class GPUStudioGLImpeller final : public Studio { public: explicit GPUStudioGLImpeller(GPUSurfaceGLDelegate* delegate, - std::shared_ptr context); + const std::shared_ptr& context); // |Studio| ~GPUStudioGLImpeller() override; diff --git a/shell/platform/darwin/ios/ios_surface_metal_skia.mm b/shell/platform/darwin/ios/ios_surface_metal_skia.mm index 16762b4db6b29..ff2b858e5b91f 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_skia.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_skia.mm @@ -41,7 +41,7 @@ } // |IOSSurface| -std::unique_ptr IOSSurfaceMetalSkia::CreateGPUStudio(GrDirectContext* gr_context) { +std::unique_ptr IOSSurfaceMetalSkia::CreateGPUStudio(GrDirectContext* context) { FML_DCHECK(context); return std::make_unique(this, // delegate sk_ref_sp(context), // context From dceb2d201732fb0d8f3ba59913dd2f1c359fab8c Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 15:16:46 -0700 Subject: [PATCH 030/147] Format --- shell/gpu/gpu_studio_gl_impeller.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/shell/gpu/gpu_studio_gl_impeller.h b/shell/gpu/gpu_studio_gl_impeller.h index 9f26e8d022e83..9cddf0a29de3c 100644 --- a/shell/gpu/gpu_studio_gl_impeller.h +++ b/shell/gpu/gpu_studio_gl_impeller.h @@ -18,8 +18,9 @@ namespace flutter { class GPUStudioGLImpeller final : public Studio { public: - explicit GPUStudioGLImpeller(GPUSurfaceGLDelegate* delegate, - const std::shared_ptr& context); + explicit GPUStudioGLImpeller( + GPUSurfaceGLDelegate* delegate, + const std::shared_ptr& context); // |Studio| ~GPUStudioGLImpeller() override; From 9f256fac5a061df4aedb51dd338ce734e8b501b5 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 15:30:17 -0700 Subject: [PATCH 031/147] Compile --- shell/platform/darwin/ios/ios_surface_metal_skia.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/ios_surface_metal_skia.mm b/shell/platform/darwin/ios/ios_surface_metal_skia.mm index ff2b858e5b91f..7a64898ff865a 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_skia.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_skia.mm @@ -43,8 +43,8 @@ // |IOSSurface| std::unique_ptr IOSSurfaceMetalSkia::CreateGPUStudio(GrDirectContext* context) { FML_DCHECK(context); - return std::make_unique(this, // delegate - sk_ref_sp(context), // context + return std::make_unique(this, // delegate + sk_ref_sp(context) // context ); } From 05063a5b505432c147dc77914cf66ea66409d35e Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 16:24:00 -0700 Subject: [PATCH 032/147] Wire up platformview and rasterizer --- shell/common/platform_view.cc | 33 +++++++--- shell/common/platform_view.h | 8 ++- shell/common/rasterizer.cc | 66 ++++++++++--------- shell/common/rasterizer.h | 4 +- shell/common/shell.cc | 16 +++-- .../platform/android/platform_view_android.cc | 9 +++ .../platform/android/platform_view_android.h | 3 + shell/platform/darwin/ios/platform_view_ios.h | 3 + .../platform/darwin/ios/platform_view_ios.mm | 12 ++++ .../embedder/platform_view_embedder.cc | 5 ++ .../embedder/platform_view_embedder.h | 3 + .../platform/fuchsia/flutter/platform_view.cc | 7 ++ .../platform/fuchsia/flutter/platform_view.h | 8 +++ 13 files changed, 127 insertions(+), 50 deletions(-) diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index 4e6df69e823d2..b9c73f5323d2b 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -65,27 +65,36 @@ void PlatformView::NotifyDestroyed() { constexpr int64_t kFlutterDefaultViewId = 0; -std::unique_ptr PlatformView::CreateSurface() { +std::pair, std::unique_ptr> +PlatformView::CreateStudioAndSurface() { + std::unique_ptr studio; std::unique_ptr surface; // Threading: We want to use the platform view on the non-platform thread. // Using the weak pointer is illegal. But, we are going to introduce a latch - // so that the platform view is not collected till the surface is obtained. + // so that the platform view is not collected till the studio and the surface + // are obtained. auto* platform_view = this; fml::ManualResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask( - task_runners_.GetRasterTaskRunner(), [platform_view, &surface, &latch]() { + task_runners_.GetRasterTaskRunner(), + [platform_view, &studio, &surface, &latch]() { + studio = platform_view->CreateRenderingStudio(); surface = platform_view->CreateRenderingSurface(kFlutterDefaultViewId); - if (surface && !surface->IsValid()) { + if (!studio || !studio->IsValid() || !surface || !surface->IsValid()) { surface.reset(); + studio.reset(); } latch.Signal(); }); latch.Wait(); - if (!surface) { + std::pair, std::unique_ptr> result; + if (!studio || !surface) { FML_LOG(ERROR) << "Failed to create platform view rendering surface"; - return nullptr; + return result; } - return surface; + result.first = std::move(studio); + result.second = std::move(surface); + return result; } void PlatformView::ScheduleFrame() { @@ -140,11 +149,19 @@ void PlatformView::MarkTextureFrameAvailable(int64_t texture_id) { delegate_.OnPlatformViewMarkTextureFrameAvailable(texture_id); } +std::unique_ptr PlatformView::CreateRenderingStudio() { + // We have a default implementation because tests create a platform view but + // never a rendering studio. + FML_DCHECK(false) << "This platform does not provide a rendering studio but " + "it was notified of rendering studio creation."; + return nullptr; +} + std::unique_ptr PlatformView::CreateRenderingSurface(int64_t view_id) { // We have a default implementation because tests create a platform view but // never a rendering surface. FML_DCHECK(false) << "This platform does not provide a rendering surface but " - "it was notified of surface rendering surface creation."; + "it was notified of rendering surface creation."; return nullptr; } diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index cd8c15a838162..77088d57eceb6 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -11,6 +11,7 @@ #include "flutter/common/graphics/texture.h" #include "flutter/common/task_runners.h" #include "flutter/flow/embedded_views.h" +#include "flutter/flow/studio.h" #include "flutter/flow/surface.h" #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" @@ -493,7 +494,8 @@ class PlatformView { /// virtual void NotifyDestroyed(); - std::unique_ptr CreateSurface(); + std::pair, std::unique_ptr> + CreateStudioAndSurface(); //---------------------------------------------------------------------------- /// @brief Used by embedders to schedule a frame. In response to this @@ -829,7 +831,9 @@ class PlatformView { const Settings& GetSettings() const; protected: - // This is the only method called on the raster task runner. + virtual std::unique_ptr CreateRenderingStudio(); + + // This is called on the raster task runner. virtual std::unique_ptr CreateRenderingSurface(int64_t view_id); PlatformView::Delegate& delegate_; diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index d4a43e66b32da..f29f02f25eed5 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -51,7 +51,9 @@ fml::TaskRunnerAffineWeakPtr Rasterizer::GetSnapshotDelegate() return weak_factory_.GetWeakPtr(); } -void Rasterizer::Setup(std::unique_ptr surface) { +void Rasterizer::Setup(std::unique_ptr studio, + std::unique_ptr surface) { + studio_ = std::move(studio); surface_ = std::move(surface); if (max_cache_bytes_.has_value()) { @@ -59,7 +61,7 @@ void Rasterizer::Setup(std::unique_ptr surface) { user_override_resource_cache_bytes_); } - auto context_switch = surface_->MakeRenderContextCurrent(); + auto context_switch = studio_->MakeRenderContextCurrent(); if (context_switch->GetResult()) { compositor_context_->OnGrContextCreated(); } @@ -77,8 +79,8 @@ void Rasterizer::Setup(std::unique_ptr surface) { if (raster_thread_merger_) { raster_thread_merger_->SetMergeUnmergeCallback([=]() { // Clear the GL context after the thread configuration has changed. - if (surface_) { - surface_->ClearRenderContext(); + if (studio_) { + studio_->ClearRenderContext(); } }); } @@ -91,16 +93,17 @@ void Rasterizer::TeardownExternalViewEmbedder() { } void Rasterizer::Teardown() { - if (surface_) { - auto context_switch = surface_->MakeRenderContextCurrent(); + if (studio_) { + auto context_switch = studio_->MakeRenderContextCurrent(); if (context_switch->GetResult()) { compositor_context_->OnGrContextDestroyed(); - if (auto* context = surface_->GetContext()) { + if (auto* context = studio_->GetContext()) { context->purgeUnlockedResources(/*scratchResourcesOnly=*/false); } } - surface_.reset(); + studio_.reset(); } + surface_.reset(); last_layer_tree_.reset(); @@ -125,18 +128,18 @@ void Rasterizer::DisableThreadMergerIfNeeded() { } void Rasterizer::NotifyLowMemoryWarning() const { - if (!surface_) { + if (!studio_) { FML_DLOG(INFO) << "Rasterizer::NotifyLowMemoryWarning called with no surface."; return; } - auto context = surface_->GetContext(); + auto context = studio_->GetContext(); if (!context) { FML_DLOG(INFO) << "Rasterizer::NotifyLowMemoryWarning called with no GrContext."; return; } - auto context_switch = surface_->MakeRenderContextCurrent(); + auto context_switch = studio_->MakeRenderContextCurrent(); if (!context_switch->GetResult()) { return; } @@ -148,7 +151,7 @@ std::shared_ptr Rasterizer::GetTextureRegistry() { } GrDirectContext* Rasterizer::GetGrContext() { - return surface_ ? surface_->GetContext() : nullptr; + return studio_ ? studio_->GetContext() : nullptr; } flutter::LayerTree* Rasterizer::GetLastLayerTree() { @@ -157,7 +160,7 @@ flutter::LayerTree* Rasterizer::GetLastLayerTree() { void Rasterizer::DrawLastLayerTree( std::unique_ptr frame_timings_recorder) { - if (!last_layer_tree_ || !surface_) { + if (!last_layer_tree_ || !studio_) { return; } RasterStatus raster_status = @@ -298,9 +301,9 @@ std::unique_ptr Rasterizer::MakeSkiaGpuImage( result = MakeBitmapImage(display_list, image_info); }) .SetIfFalse([&result, &image_info, &display_list, - surface = surface_.get(), + studio = studio_.get(), gpu_image_behavior = gpu_image_behavior_] { - if (!surface || + if (!studio || gpu_image_behavior == MakeGpuImageBehavior::kBitmap) { // TODO(dnfield): This isn't safe if display_list contains any GPU // resources like an SkImage_gpu. @@ -308,13 +311,13 @@ std::unique_ptr Rasterizer::MakeSkiaGpuImage( return; } - auto context_switch = surface->MakeRenderContextCurrent(); + auto context_switch = studio->MakeRenderContextCurrent(); if (!context_switch->GetResult()) { result = MakeBitmapImage(display_list, image_info); return; } - auto* context = surface->GetContext(); + auto* context = studio->GetContext(); if (!context) { result = MakeBitmapImage(display_list, image_info); return; @@ -505,7 +508,7 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( FML_DCHECK(!external_view_embedder_->GetUsedThisFrame()); external_view_embedder_->SetUsedThisFrame(true); external_view_embedder_->BeginFrame( - layer_tree.frame_size(), surface_->GetContext(), + layer_tree.frame_size(), studio_->GetContext(), layer_tree.device_pixel_ratio(), raster_thread_merger_); embedder_root_canvas = external_view_embedder_->GetRootCanvas(); } @@ -533,7 +536,7 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( embedder_root_canvas ? embedder_root_canvas : frame->Canvas(); auto compositor_frame = compositor_context_->AcquireFrame( - surface_->GetContext(), // skia GrContext + studio_->GetContext(), // skia GrContext root_surface_canvas, // root surface canvas external_view_embedder_.get(), // external view embedder root_surface_transformation, // root surface transformation @@ -542,7 +545,7 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( .supports_readback, // surface supports pixel reads raster_thread_merger_, // thread merger frame->GetDisplayListBuilder().get(), // display list builder - surface_->GetAiksContext() // aiks context + studio_->GetAiksContext() // aiks context ); if (compositor_frame) { compositor_context_->raster_cache().BeginFrame(); @@ -571,7 +574,7 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( } bool ignore_raster_cache = true; - if (surface_->EnableRasterCache() && + if (studio_->EnableRasterCache() && !layer_tree.is_leaf_layer_tracing_enabled()) { ignore_raster_cache = false; } @@ -605,7 +608,7 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( if (external_view_embedder_ && (!raster_thread_merger_ || raster_thread_merger_->IsMerged())) { FML_DCHECK(!frame->IsSubmitted()); - external_view_embedder_->SubmitFrame(surface_->GetContext(), + external_view_embedder_->SubmitFrame(studio_->GetContext(), std::move(frame)); } else { frame->Submit(); @@ -621,8 +624,8 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( &compositor_context_->raster_cache()); FireNextFrameCallbackIfPresent(); - if (surface_->GetContext()) { - surface_->GetContext()->performDeferredCleanup(kSkiaCleanupExpiration); + if (studio_->GetContext()) { + studio_->GetContext()->performDeferredCleanup(kSkiaCleanupExpiration); } return raster_status; @@ -689,7 +692,7 @@ sk_sp Rasterizer::ScreenshotLayerTreeAsImage( // render context is GL. frame->Raster() pops the gl context in platforms // that gl context switching are used. (For example, older iOS that uses GL) // We reset the GL context using the context switch. - auto context_switch = surface_->MakeRenderContextCurrent(); + auto context_switch = studio_->MakeRenderContextCurrent(); if (!context_switch->GetResult()) { FML_LOG(ERROR) << "Screenshot: unable to make image screenshot"; return nullptr; @@ -725,8 +728,7 @@ Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree( sk_sp data = nullptr; std::string format; - GrDirectContext* surface_context = - surface_ ? surface_->GetContext() : nullptr; + GrDirectContext* surface_context = studio_ ? studio_->GetContext() : nullptr; switch (type) { case ScreenshotType::SkiaPicture: @@ -804,13 +806,13 @@ void Rasterizer::SetResourceCacheMaxBytes(size_t max_bytes, bool from_user) { } max_cache_bytes_ = max_bytes; - if (!surface_) { + if (!studio_) { return; } - GrDirectContext* context = surface_->GetContext(); + GrDirectContext* context = studio_->GetContext(); if (context) { - auto context_switch = surface_->MakeRenderContextCurrent(); + auto context_switch = studio_->MakeRenderContextCurrent(); if (!context_switch->GetResult()) { return; } @@ -820,10 +822,10 @@ void Rasterizer::SetResourceCacheMaxBytes(size_t max_bytes, bool from_user) { } std::optional Rasterizer::GetResourceCacheMaxBytes() const { - if (!surface_) { + if (!studio_) { return std::nullopt; } - GrDirectContext* context = surface_->GetContext(); + GrDirectContext* context = studio_->GetContext(); if (context) { return context->getResourceCacheLimit(); } diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index c529ea987dcd5..7092c8523dd9b 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -15,6 +15,7 @@ #include "flutter/flow/embedded_views.h" #include "flutter/flow/frame_timings.h" #include "flutter/flow/layers/layer_tree.h" +#include "flutter/flow/studio.h" #include "flutter/flow/surface.h" #include "flutter/fml/closure.h" #include "flutter/fml/memory/weak_ptr.h" @@ -152,7 +153,7 @@ class Rasterizer final : public SnapshotDelegate, /// /// @param[in] surface The on-screen render surface. /// - void Setup(std::unique_ptr surface); + void Setup(std::unique_ptr studio, std::unique_ptr surface); //---------------------------------------------------------------------------- /// @brief Releases the previously set up on-screen render surface and @@ -541,6 +542,7 @@ class Rasterizer final : public SnapshotDelegate, Delegate& delegate_; MakeGpuImageBehavior gpu_image_behavior_; + std::unique_ptr studio_; std::unique_ptr surface_; std::unique_ptr snapshot_surface_producer_; std::unique_ptr compositor_context_; diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 01c8d123d6c9f..f92c8c2091f34 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -750,8 +750,10 @@ void Shell::OnPlatformViewCreated() { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); - std::unique_ptr surface = platform_view_->CreateSurface(); - if (surface == nullptr) { + auto studio_and_surface = platform_view_->CreateStudioAndSurface(); + std::unique_ptr studio = std::move(studio_and_surface.first); + std::unique_ptr surface = std::move(studio_and_surface.second); + if (studio == nullptr || surface == nullptr) { // TODO(dkwingsmt): This case is observed in windows unit tests. Anyway, // we're probably not creating the surface in this callback eventually. return; @@ -782,15 +784,15 @@ void Shell::OnPlatformViewCreated() { !task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread(); fml::AutoResetWaitableEvent latch; - auto raster_task = - fml::MakeCopyable([&waiting_for_first_frame = waiting_for_first_frame_, - rasterizer = rasterizer_->GetWeakPtr(), // - surface = std::move(surface)]() mutable { + auto raster_task = fml::MakeCopyable( + [&waiting_for_first_frame = waiting_for_first_frame_, + rasterizer = rasterizer_->GetWeakPtr(), // + studio = std::move(studio), surface = std::move(surface)]() mutable { if (rasterizer) { // Enables the thread merger which may be used by the external view // embedder. rasterizer->EnableThreadMergerIfNeeded(); - rasterizer->Setup(std::move(surface)); + rasterizer->Setup(std::move(studio), std::move(surface)); } waiting_for_first_frame.store(true); diff --git a/shell/platform/android/platform_view_android.cc b/shell/platform/android/platform_view_android.cc index 4ba82e801df6d..f0f7a745bb2bf 100644 --- a/shell/platform/android/platform_view_android.cc +++ b/shell/platform/android/platform_view_android.cc @@ -280,6 +280,15 @@ std::unique_ptr PlatformViewAndroid::CreateVSyncWaiter() { return std::make_unique(task_runners_); } +// |PlatformView| +std::unique_ptr PlatformViewAndroid::CreateRenderingStudio() { + if (!android_surface_) { + return nullptr; + } + return android_surface_->CreateGPUStudio( + android_context_->GetMainSkiaContext().get()); +} + // |PlatformView| std::unique_ptr PlatformViewAndroid::CreateRenderingSurface( int64_t view_id) { diff --git a/shell/platform/android/platform_view_android.h b/shell/platform/android/platform_view_android.h index 1bcd8a6482631..20962ab5fa897 100644 --- a/shell/platform/android/platform_view_android.h +++ b/shell/platform/android/platform_view_android.h @@ -142,6 +142,9 @@ class PlatformViewAndroid final : public PlatformView { // |PlatformView| std::unique_ptr CreateVSyncWaiter() override; + // |PlatformView| + std::unique_ptr CreateRenderingStudio() override; + // |PlatformView| std::unique_ptr CreateRenderingSurface(int64_t view_id) override; diff --git a/shell/platform/darwin/ios/platform_view_ios.h b/shell/platform/darwin/ios/platform_view_ios.h index ba036bd6aa341..c9c0d4f3cfa2b 100644 --- a/shell/platform/darwin/ios/platform_view_ios.h +++ b/shell/platform/darwin/ios/platform_view_ios.h @@ -148,6 +148,9 @@ class PlatformViewIOS final : public PlatformView { // |PlatformView| void HandlePlatformMessage(std::unique_ptr message) override; + // |PlatformView| + std::unique_ptr CreateRenderingStudio() override; + // |PlatformView| std::unique_ptr CreateRenderingSurface(int64_t view_id) override; diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index 851e5ecdbad00..7b13a27c5b04d 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -137,6 +137,18 @@ new PlatformMessageHandlerIos(task_runners.GetPlatformTaskRunner())) {} texture_id, fml::scoped_nsobject>{[texture retain]})); } +// |PlatformView| +std::unique_ptr PlatformViewIOS::CreateRenderingStudio() { + FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); + std::lock_guard guard(ios_surface_mutex_); + if (!ios_surface_) { + FML_DLOG(INFO) << "Could not CreateRenderingSurface, this PlatformViewIOS " + "has no ViewController."; + return nullptr; + } + return ios_surface_->CreateGPUStudio(ios_context_->GetMainContext().get()); +} + // |PlatformView| std::unique_ptr PlatformViewIOS::CreateRenderingSurface(int64_t view_id) { FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); diff --git a/shell/platform/embedder/platform_view_embedder.cc b/shell/platform/embedder/platform_view_embedder.cc index 09ae49db8210b..5714af00d8430 100644 --- a/shell/platform/embedder/platform_view_embedder.cc +++ b/shell/platform/embedder/platform_view_embedder.cc @@ -87,6 +87,11 @@ void PlatformViewEmbedder::HandlePlatformMessage( std::move(message)); } +// |PlatformView| +std::unique_ptr PlatformViewEmbedder::CreateRenderingStudio() { + return embedder_studio_->CreateGPUStudio(); +} + // |PlatformView| std::unique_ptr PlatformViewEmbedder::CreateRenderingSurface( int64_t view_id) { diff --git a/shell/platform/embedder/platform_view_embedder.h b/shell/platform/embedder/platform_view_embedder.h index 3e30a33101b5b..b0e723dece794 100644 --- a/shell/platform/embedder/platform_view_embedder.h +++ b/shell/platform/embedder/platform_view_embedder.h @@ -83,6 +83,9 @@ class PlatformViewEmbedder final : public PlatformView { std::shared_ptr platform_message_handler_; PlatformDispatchTable platform_dispatch_table_; + // |PlatformView| + std::unique_ptr CreateRenderingStudio() override; + // |PlatformView| std::unique_ptr CreateRenderingSurface(int64_t view_id) override; diff --git a/shell/platform/fuchsia/flutter/platform_view.cc b/shell/platform/fuchsia/flutter/platform_view.cc index e0238148c3029..51447ff5daaca 100644 --- a/shell/platform/fuchsia/flutter/platform_view.cc +++ b/shell/platform/fuchsia/flutter/platform_view.cc @@ -50,6 +50,7 @@ PlatformView::PlatformView( fuchsia::ui::pointerinjector::RegistryHandle pointerinjector_registry, OnEnableWireframe wireframe_enabled_callback, OnUpdateView on_update_view_callback, + OnCreateStudio on_create_studio_callback, OnCreateSurface on_create_surface_callback, OnSemanticsNodeUpdate on_semantics_node_update_callback, OnRequestAnnounce on_request_announce_callback, @@ -67,6 +68,7 @@ PlatformView::PlatformView( std::move(mouse_source))), wireframe_enabled_callback_(std::move(wireframe_enabled_callback)), on_update_view_callback_(std::move(on_update_view_callback)), + on_create_studio_callback_(std::move(on_create_studio_callback)), on_create_surface_callback_(std::move(on_create_surface_callback)), on_semantics_node_update_callback_( std::move(on_semantics_node_update_callback)), @@ -314,6 +316,11 @@ std::unique_ptr PlatformView::CreateVSyncWaiter() { task_runners_); } +// |flutter::PlatformView| +std::unique_ptr PlatformView::CreateRenderingStudio() { + return on_create_studio_callback_ ? on_create_studio_callback_() : nullptr; +} + // |flutter::PlatformView| std::unique_ptr PlatformView::CreateRenderingSurface( int64_t view_id) { diff --git a/shell/platform/fuchsia/flutter/platform_view.h b/shell/platform/fuchsia/flutter/platform_view.h index 5ae674f8d8fee..f9357dfd971bb 100644 --- a/shell/platform/fuchsia/flutter/platform_view.h +++ b/shell/platform/fuchsia/flutter/platform_view.h @@ -34,6 +34,8 @@ #include "focus_delegate.h" #include "pointer_delegate.h" #include "pointer_injector_delegate.h" +#include "studio.h" +#include "surface.h" #include "text_delegate.h" namespace flutter_runner { @@ -41,6 +43,7 @@ namespace flutter_runner { using OnEnableWireframe = fit::function; using ViewCallback = std::function; using OnUpdateView = fit::function; +using OnCreateStudio = fit::function()>; using OnCreateSurface = fit::function()>; using OnSemanticsNodeUpdate = fit::function; @@ -78,6 +81,7 @@ class PlatformView : public flutter::PlatformView { fuchsia::ui::pointerinjector::RegistryHandle pointerinjector_registry, OnEnableWireframe wireframe_enabled_callback, OnUpdateView on_update_view_callback, + OnCreateStudio on_create_studio_callback, OnCreateSurface on_create_surface_callback, OnSemanticsNodeUpdate on_semantics_node_update_callback, OnRequestAnnounce on_request_announce_callback, @@ -105,6 +109,9 @@ class PlatformView : public flutter::PlatformView { // |flutter::PlatformView| std::unique_ptr CreateVSyncWaiter() override; + // |flutter::PlatformView| + std::unique_ptr CreateRenderingStudio() override; + // |flutter::PlatformView| std::unique_ptr CreateRenderingSurface( int64_t view_id) override; @@ -179,6 +186,7 @@ class PlatformView : public flutter::PlatformView { OnEnableWireframe wireframe_enabled_callback_; OnUpdateView on_update_view_callback_; + OnCreateStudio on_create_studio_callback_; OnCreateSurface on_create_surface_callback_; OnSemanticsNodeUpdate on_semantics_node_update_callback_; OnRequestAnnounce on_request_announce_callback_; From 37e119c4ab904f978e218cb914916e3ffb4ad8b5 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 16:39:36 -0700 Subject: [PATCH 033/147] Fix compile --- shell/common/rasterizer_unittests.cc | 57 +++++++++++++------ .../fuchsia/flutter/flatland_platform_view.cc | 2 + .../fuchsia/flutter/gfx_platform_view.cc | 2 + .../fuchsia/flutter/gfx_platform_view.h | 1 + 4 files changed, 46 insertions(+), 16 deletions(-) diff --git a/shell/common/rasterizer_unittests.cc b/shell/common/rasterizer_unittests.cc index a9ebaff8f2f0c..a80c83264714b 100644 --- a/shell/common/rasterizer_unittests.cc +++ b/shell/common/rasterizer_unittests.cc @@ -40,6 +40,15 @@ class MockDelegate : public Rasterizer::Delegate { MOCK_CONST_METHOD0(GetSettings, const Settings&()); }; +class MockStudio : public Studio { + public: + MOCK_METHOD0(IsValid, bool()); + MOCK_METHOD0(GetContext, GrDirectContext*()); + // MOCK_METHOD0(MakeRenderContextCurrent, std::unique_ptr()); + // MOCK_METHOD0(ClearRenderContext, bool()); + // MOCK_CONST_METHOD0(AllowsDrawingWhenGpuDisabled, bool()); +}; + class MockSurface : public Surface { public: MOCK_METHOD0(IsValid, bool()); @@ -116,10 +125,11 @@ TEST(RasterizerTest, drawEmptyPipeline) { ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings)); ON_CALL(delegate, GetTaskRunners()).WillByDefault(ReturnRef(task_runners)); auto rasterizer = std::make_unique(delegate); + auto studio = std::make_unique>(); auto surface = std::make_unique>(); EXPECT_CALL(*surface, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - rasterizer->Setup(std::move(surface)); + rasterizer->Setup(std::move(studio), std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -147,6 +157,7 @@ TEST(RasterizerTest, .WillRepeatedly(ReturnRef(task_runners)); EXPECT_CALL(delegate, OnFrameRasterized(_)); auto rasterizer = std::make_unique(delegate); + auto studio = std::make_unique>(); auto surface = std::make_unique>(); std::shared_ptr> external_view_embedder = @@ -180,7 +191,7 @@ TEST(RasterizerTest, nullptr))) .Times(1); - rasterizer->Setup(std::move(surface)); + rasterizer->Setup(std::move(studio), std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -217,6 +228,7 @@ TEST( .WillRepeatedly(ReturnRef(task_runners)); EXPECT_CALL(delegate, OnFrameRasterized(_)); auto rasterizer = std::make_unique(delegate); + auto studio = std::make_unique>(); auto surface = std::make_unique>(); std::shared_ptr> external_view_embedder = std::make_shared>(); @@ -245,7 +257,7 @@ TEST( /*raster_thread_merger=*/_)) .Times(1); - rasterizer->Setup(std::move(surface)); + rasterizer->Setup(std::move(studio), std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -286,6 +298,7 @@ TEST( EXPECT_CALL(delegate, OnFrameRasterized(_)); auto rasterizer = std::make_unique(delegate); + auto studio = std::make_unique>(); auto surface = std::make_unique>(); std::shared_ptr> external_view_embedder = @@ -317,7 +330,7 @@ TEST( /*raster_thread_merger=*/_)) .Times(1); - rasterizer->Setup(std::move(surface)); + rasterizer->Setup(std::move(studio), std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), @@ -353,6 +366,7 @@ TEST(RasterizerTest, EXPECT_CALL(delegate, OnFrameRasterized(_)); auto rasterizer = std::make_unique(delegate); + auto studio = std::make_unique>(); auto surface = std::make_unique>(); std::shared_ptr> external_view_embedder = @@ -391,7 +405,7 @@ TEST(RasterizerTest, /*raster_thread_merger=*/_)) .Times(2); - rasterizer->Setup(std::move(surface)); + rasterizer->Setup(std::move(studio), std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), @@ -474,6 +488,7 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNotUsedThisFrame) { .WillRepeatedly(ReturnRef(task_runners)); auto rasterizer = std::make_unique(delegate); + auto studio = std::make_unique>(); auto surface = std::make_unique>(); EXPECT_CALL(*surface, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); @@ -481,7 +496,7 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNotUsedThisFrame) { std::shared_ptr> external_view_embedder = std::make_shared>(); rasterizer->SetExternalViewEmbedder(external_view_embedder); - rasterizer->Setup(std::move(surface)); + rasterizer->Setup(std::move(studio), std::move(surface)); EXPECT_CALL(*external_view_embedder, BeginFrame(/*frame_size=*/SkISize(), /*context=*/nullptr, @@ -531,6 +546,7 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenPipelineIsEmpty) { .WillRepeatedly(ReturnRef(task_runners)); auto rasterizer = std::make_unique(delegate); + auto studio = std::make_unique>(); auto surface = std::make_unique>(); EXPECT_CALL(*surface, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); @@ -538,7 +554,7 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenPipelineIsEmpty) { std::shared_ptr> external_view_embedder = std::make_shared>(); rasterizer->SetExternalViewEmbedder(external_view_embedder); - rasterizer->Setup(std::move(surface)); + rasterizer->Setup(std::move(studio), std::move(surface)); EXPECT_CALL( *external_view_embedder, @@ -577,6 +593,7 @@ TEST(RasterizerTest, EXPECT_CALL(delegate, OnFrameRasterized(_)); auto rasterizer = std::make_unique(delegate); + auto studio = std::make_unique>(); auto surface = std::make_unique>(); auto is_gpu_disabled_sync_switch = std::make_shared(false); @@ -596,7 +613,7 @@ TEST(RasterizerTest, EXPECT_CALL(*surface, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - rasterizer->Setup(std::move(surface)); + rasterizer->Setup(std::move(studio), std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -633,6 +650,7 @@ TEST( .WillRepeatedly(ReturnRef(task_runners)); EXPECT_CALL(delegate, OnFrameRasterized(_)); auto rasterizer = std::make_unique(delegate); + auto studio = std::make_unique>(); auto surface = std::make_unique>(); auto is_gpu_disabled_sync_switch = std::make_shared(true); @@ -653,7 +671,7 @@ TEST( EXPECT_CALL(*surface, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - rasterizer->Setup(std::move(surface)); + rasterizer->Setup(std::move(studio), std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -691,6 +709,7 @@ TEST( .WillRepeatedly(ReturnRef(task_runners)); EXPECT_CALL(delegate, OnFrameRasterized(_)); auto rasterizer = std::make_unique(delegate); + auto studio = std::make_unique>(); auto surface = std::make_unique>(); auto is_gpu_disabled_sync_switch = std::make_shared(false); @@ -710,7 +729,7 @@ TEST( EXPECT_CALL(*surface, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - rasterizer->Setup(std::move(surface)); + rasterizer->Setup(std::move(studio), std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -748,6 +767,7 @@ TEST( .WillRepeatedly(ReturnRef(task_runners)); EXPECT_CALL(delegate, OnFrameRasterized(_)).Times(0); auto rasterizer = std::make_unique(delegate); + auto studio = std::make_unique>(); auto surface = std::make_unique>(); auto is_gpu_disabled_sync_switch = std::make_shared(true); @@ -766,7 +786,7 @@ TEST( EXPECT_CALL(*surface, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - rasterizer->Setup(std::move(surface)); + rasterizer->Setup(std::move(studio), std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -811,6 +831,7 @@ TEST( }); auto rasterizer = std::make_unique(delegate); + auto studio = std::make_unique>(); auto surface = std::make_unique>(); auto is_gpu_disabled_sync_switch = std::make_shared(false); @@ -821,7 +842,7 @@ TEST( EXPECT_CALL(*surface, AcquireFrame(SkISize())); EXPECT_CALL(*surface, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - rasterizer->Setup(std::move(surface)); + rasterizer->Setup(std::move(studio), std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -864,6 +885,7 @@ TEST(RasterizerTest, }); latch.Wait(); + auto studio = std::make_unique>(); auto surface = std::make_unique>(); EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()) .WillRepeatedly(Return(true)); @@ -900,7 +922,7 @@ TEST(RasterizerTest, }); thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - rasterizer->Setup(std::move(surface)); + rasterizer->Setup(std::move(studio), std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); for (int i = 0; i < 2; i++) { auto layer_tree = @@ -944,6 +966,7 @@ TEST(RasterizerTest, TeardownFreesResourceCache) { .WillRepeatedly(ReturnRef(task_runners)); auto rasterizer = std::make_unique(delegate); + auto studio = std::make_unique>(); auto surface = std::make_unique>(); auto context = GrDirectContext::MakeMock(nullptr); context->setResourceCacheLimit(0); @@ -954,7 +977,7 @@ TEST(RasterizerTest, TeardownFreesResourceCache) { }); EXPECT_CALL(*surface, GetContext()).WillRepeatedly(Return(context.get())); - rasterizer->Setup(std::move(surface)); + rasterizer->Setup(std::move(studio), std::move(surface)); EXPECT_EQ(context->getResourceCacheLimit(), 0ul); rasterizer->SetResourceCacheMaxBytes(10000000, false); @@ -1045,6 +1068,7 @@ TEST(RasterizerTest, presentationTimeSetWhenVsyncTargetInFuture) { int frames_submitted = 0; fml::CountDownLatch submit_latch(2); + auto studio = std::make_unique>(); auto surface = std::make_unique(); ON_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillByDefault(Return(true)); ON_CALL(*surface, AcquireFrame(SkISize())) @@ -1071,7 +1095,7 @@ TEST(RasterizerTest, presentationTimeSetWhenVsyncTargetInFuture) { [] { return std::make_unique(true); })); thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - rasterizer->Setup(std::move(surface)); + rasterizer->Setup(std::move(studio), std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); for (int i = 0; i < 2; i++) { auto layer_tree = @@ -1128,6 +1152,7 @@ TEST(RasterizerTest, presentationTimeNotSetWhenVsyncTargetInPast) { const auto first_timestamp = fml::TimePoint::Now() - millis_16; fml::CountDownLatch submit_latch(1); + auto studio = std::make_unique>(); auto surface = std::make_unique(); ON_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillByDefault(Return(true)); ON_CALL(*surface, AcquireFrame(SkISize())) @@ -1152,7 +1177,7 @@ TEST(RasterizerTest, presentationTimeNotSetWhenVsyncTargetInPast) { [] { return std::make_unique(true); })); thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - rasterizer->Setup(std::move(surface)); + rasterizer->Setup(std::move(studio), std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), /*device_pixel_ratio=*/2.0f); diff --git a/shell/platform/fuchsia/flutter/flatland_platform_view.cc b/shell/platform/fuchsia/flutter/flatland_platform_view.cc index 0922661723f71..0555c597438e2 100644 --- a/shell/platform/fuchsia/flutter/flatland_platform_view.cc +++ b/shell/platform/fuchsia/flutter/flatland_platform_view.cc @@ -26,6 +26,7 @@ FlatlandPlatformView::FlatlandPlatformView( OnCreateFlatlandView on_create_view_callback, OnUpdateView on_update_view_callback, OnDestroyFlatlandView on_destroy_view_callback, + OnCreateStudio on_create_studio_callback, OnCreateSurface on_create_surface_callback, OnSemanticsNodeUpdate on_semantics_node_update_callback, OnRequestAnnounce on_request_announce_callback, @@ -47,6 +48,7 @@ FlatlandPlatformView::FlatlandPlatformView( std::move(pointerinjector_registry), std::move(wireframe_enabled_callback), std::move(on_update_view_callback), + std::move(on_create_studio_callback), std::move(on_create_surface_callback), std::move(on_semantics_node_update_callback), std::move(on_request_announce_callback), diff --git a/shell/platform/fuchsia/flutter/gfx_platform_view.cc b/shell/platform/fuchsia/flutter/gfx_platform_view.cc index ed9eeece151a0..1dcb2c2e8c534 100644 --- a/shell/platform/fuchsia/flutter/gfx_platform_view.cc +++ b/shell/platform/fuchsia/flutter/gfx_platform_view.cc @@ -27,6 +27,7 @@ GfxPlatformView::GfxPlatformView( OnCreateGfxView on_create_view_callback, OnUpdateView on_update_view_callback, OnDestroyGfxView on_destroy_view_callback, + OnCreateStudio on_create_studio_callback, OnCreateSurface on_create_surface_callback, OnSemanticsNodeUpdate on_semantics_node_update_callback, OnRequestAnnounce on_request_announce_callback, @@ -48,6 +49,7 @@ GfxPlatformView::GfxPlatformView( std::move(pointerinjector_registry), std::move(wireframe_enabled_callback), std::move(on_update_view_callback), + std::move(on_create_studio_callback), std::move(on_create_surface_callback), std::move(on_semantics_node_update_callback), std::move(on_request_announce_callback), diff --git a/shell/platform/fuchsia/flutter/gfx_platform_view.h b/shell/platform/fuchsia/flutter/gfx_platform_view.h index 1004a85808c56..577620021311f 100644 --- a/shell/platform/fuchsia/flutter/gfx_platform_view.h +++ b/shell/platform/fuchsia/flutter/gfx_platform_view.h @@ -44,6 +44,7 @@ class GfxPlatformView final : public flutter_runner::PlatformView, OnCreateGfxView on_create_view_callback, OnUpdateView on_update_view_callback, OnDestroyGfxView on_destroy_view_callback, + OnCreateStudio on_create_studio_callback, OnCreateSurface on_create_surface_callback, OnSemanticsNodeUpdate on_semantics_node_update_callback, OnRequestAnnounce on_request_announce_callback, From a099aab69ee9e38eeb00e7c77d8a57ebba4539a4 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 22:56:49 -0700 Subject: [PATCH 034/147] Mock more --- shell/common/rasterizer_unittests.cc | 52 +++---- shell/common/shell_test_platform_view_gl.cc | 14 +- shell/common/shell_test_platform_view_gl.h | 5 + shell/common/shell_test_platform_view_metal.h | 3 + .../common/shell_test_platform_view_metal.mm | 6 + .../common/shell_test_platform_view_vulkan.cc | 134 +++++++++--------- .../common/shell_test_platform_view_vulkan.h | 14 +- shell/common/shell_unittests.cc | 59 ++++++-- .../embedder/platform_view_embedder.h | 13 -- 9 files changed, 180 insertions(+), 120 deletions(-) diff --git a/shell/common/rasterizer_unittests.cc b/shell/common/rasterizer_unittests.cc index a80c83264714b..ae82d13127ce9 100644 --- a/shell/common/rasterizer_unittests.cc +++ b/shell/common/rasterizer_unittests.cc @@ -44,9 +44,9 @@ class MockStudio : public Studio { public: MOCK_METHOD0(IsValid, bool()); MOCK_METHOD0(GetContext, GrDirectContext*()); - // MOCK_METHOD0(MakeRenderContextCurrent, std::unique_ptr()); - // MOCK_METHOD0(ClearRenderContext, bool()); - // MOCK_CONST_METHOD0(AllowsDrawingWhenGpuDisabled, bool()); + MOCK_METHOD0(MakeRenderContextCurrent, std::unique_ptr()); + MOCK_METHOD0(ClearRenderContext, bool()); + MOCK_CONST_METHOD0(AllowsDrawingWhenGpuDisabled, bool()); }; class MockSurface : public Surface { @@ -127,7 +127,7 @@ TEST(RasterizerTest, drawEmptyPipeline) { auto rasterizer = std::make_unique(delegate); auto studio = std::make_unique>(); auto surface = std::make_unique>(); - EXPECT_CALL(*surface, MakeRenderContextCurrent()) + EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio), std::move(surface)); fml::AutoResetWaitableEvent latch; @@ -171,10 +171,10 @@ TEST(RasterizerTest, /*surface=*/nullptr, framebuffer_info, /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); + EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); EXPECT_CALL(*surface, AcquireFrame(SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame)))); - EXPECT_CALL(*surface, MakeRenderContextCurrent()) + EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); EXPECT_CALL(*external_view_embedder, @@ -241,10 +241,10 @@ TEST( /*surface=*/nullptr, framebuffer_info, /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); + EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); EXPECT_CALL(*surface, AcquireFrame(SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame)))); - EXPECT_CALL(*surface, MakeRenderContextCurrent()) + EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); EXPECT_CALL(*external_view_embedder, @@ -312,10 +312,10 @@ TEST( /*surface=*/nullptr, framebuffer_info, /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); + EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); EXPECT_CALL(*surface, AcquireFrame(SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame)))); - EXPECT_CALL(*surface, MakeRenderContextCurrent()) + EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); EXPECT_CALL(*external_view_embedder, SupportsDynamicThreadMerging) .WillRepeatedly(Return(true)); @@ -384,13 +384,13 @@ TEST(RasterizerTest, /*surface=*/nullptr, framebuffer_info, /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()) + EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()) .WillRepeatedly(Return(true)); // Prepare two frames for Draw() and DrawLastLayerTree(). EXPECT_CALL(*surface, AcquireFrame(SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame1)))) .WillOnce(Return(ByMove(std::move(surface_frame2)))); - EXPECT_CALL(*surface, MakeRenderContextCurrent()) + EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); EXPECT_CALL(*external_view_embedder, SupportsDynamicThreadMerging) .WillRepeatedly(Return(true)); @@ -490,7 +490,7 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNotUsedThisFrame) { auto rasterizer = std::make_unique(delegate); auto studio = std::make_unique>(); auto surface = std::make_unique>(); - EXPECT_CALL(*surface, MakeRenderContextCurrent()) + EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); std::shared_ptr> external_view_embedder = @@ -548,7 +548,7 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenPipelineIsEmpty) { auto rasterizer = std::make_unique(delegate); auto studio = std::make_unique>(); auto surface = std::make_unique>(); - EXPECT_CALL(*surface, MakeRenderContextCurrent()) + EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); std::shared_ptr> external_view_embedder = @@ -604,13 +604,13 @@ TEST(RasterizerTest, /*surface=*/nullptr, /*framebuffer_info=*/framebuffer_info, /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); + EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); ON_CALL(delegate, GetIsGpuDisabledSyncSwitch()) .WillByDefault(Return(is_gpu_disabled_sync_switch)); EXPECT_CALL(delegate, GetIsGpuDisabledSyncSwitch()).Times(0); EXPECT_CALL(*surface, AcquireFrame(SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame)))); - EXPECT_CALL(*surface, MakeRenderContextCurrent()) + EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio), std::move(surface)); @@ -662,13 +662,13 @@ TEST( /*surface=*/nullptr, /*framebuffer_info=*/framebuffer_info, /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); + EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); ON_CALL(delegate, GetIsGpuDisabledSyncSwitch()) .WillByDefault(Return(is_gpu_disabled_sync_switch)); EXPECT_CALL(delegate, GetIsGpuDisabledSyncSwitch()).Times(0); EXPECT_CALL(*surface, AcquireFrame(SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame)))); - EXPECT_CALL(*surface, MakeRenderContextCurrent()) + EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio), std::move(surface)); @@ -721,12 +721,12 @@ TEST( /*surface=*/nullptr, /*framebuffer_info=*/framebuffer_info, /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(false)); + EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(false)); EXPECT_CALL(delegate, GetIsGpuDisabledSyncSwitch()) .WillOnce(Return(is_gpu_disabled_sync_switch)); EXPECT_CALL(*surface, AcquireFrame(SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame)))); - EXPECT_CALL(*surface, MakeRenderContextCurrent()) + EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio), std::move(surface)); @@ -779,11 +779,11 @@ TEST( /*surface=*/nullptr, /*framebuffer_info=*/framebuffer_info, /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(false)); + EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(false)); EXPECT_CALL(delegate, GetIsGpuDisabledSyncSwitch()) .WillOnce(Return(is_gpu_disabled_sync_switch)); EXPECT_CALL(*surface, AcquireFrame(SkISize())).Times(0); - EXPECT_CALL(*surface, MakeRenderContextCurrent()) + EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio), std::move(surface)); @@ -840,7 +840,7 @@ TEST( ON_CALL(*surface, AcquireFrame(SkISize())) .WillByDefault(::testing::Invoke([] { return nullptr; })); EXPECT_CALL(*surface, AcquireFrame(SkISize())); - EXPECT_CALL(*surface, MakeRenderContextCurrent()) + EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio), std::move(surface)); fml::AutoResetWaitableEvent latch; @@ -887,7 +887,7 @@ TEST(RasterizerTest, auto studio = std::make_unique>(); auto surface = std::make_unique>(); - EXPECT_CALL(*surface, AllowsDrawingWhenGpuDisabled()) + EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()) .WillRepeatedly(Return(true)); ON_CALL(*surface, AcquireFrame(SkISize())) .WillByDefault(::testing::Invoke([] { @@ -971,11 +971,11 @@ TEST(RasterizerTest, TeardownFreesResourceCache) { auto context = GrDirectContext::MakeMock(nullptr); context->setResourceCacheLimit(0); - EXPECT_CALL(*surface, MakeRenderContextCurrent()) + EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillRepeatedly([]() -> std::unique_ptr { return std::make_unique(true); }); - EXPECT_CALL(*surface, GetContext()).WillRepeatedly(Return(context.get())); + EXPECT_CALL(*studio, GetContext()).WillRepeatedly(Return(context.get())); rasterizer->Setup(std::move(studio), std::move(surface)); EXPECT_EQ(context->getResourceCacheLimit(), 0ul); diff --git a/shell/common/shell_test_platform_view_gl.cc b/shell/common/shell_test_platform_view_gl.cc index 170b285604c5b..1ed6e9e47ffba 100644 --- a/shell/common/shell_test_platform_view_gl.cc +++ b/shell/common/shell_test_platform_view_gl.cc @@ -36,11 +36,23 @@ void ShellTestPlatformViewGL::SimulateVSync() { vsync_clock_->SimulateVSync(); } +// |PlatformView| +std::unique_ptr ShellTestPlatformViewGL::CreateRenderingStudio() { + if (main_context_ == nullptr) { + main_context_ = GPUSurfaceGLSkia::MakeGLContext(this); + } + return std::make_unique( + main_context_, this); +} + // |PlatformView| std::unique_ptr ShellTestPlatformViewGL::CreateRenderingSurface( int64_t view_id) { + if (main_context_ == nullptr) { + main_context_ = GPUSurfaceGLSkia::MakeGLContext(this); + } return std::make_unique( - GPUSurfaceGLSkia::MakeGLContext(this), this, true); + main_context_, this, true); } // |PlatformView| diff --git a/shell/common/shell_test_platform_view_gl.h b/shell/common/shell_test_platform_view_gl.h index e403e380a5d24..eb2c1b4ef55ec 100644 --- a/shell/common/shell_test_platform_view_gl.h +++ b/shell/common/shell_test_platform_view_gl.h @@ -39,9 +39,14 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView, std::shared_ptr shell_test_external_view_embedder_; + sk_sp main_context_; + // |PlatformView| std::unique_ptr CreateRenderingSurface(int64_t view_id) override; + // |PlatformView| + std::unique_ptr CreateRenderingStudio() override; + // |PlatformView| std::shared_ptr CreateExternalViewEmbedder() override; diff --git a/shell/common/shell_test_platform_view_metal.h b/shell/common/shell_test_platform_view_metal.h index 84f8e07f92240..2372663f2d009 100644 --- a/shell/common/shell_test_platform_view_metal.h +++ b/shell/common/shell_test_platform_view_metal.h @@ -46,6 +46,9 @@ class ShellTestPlatformViewMetal final : public ShellTestPlatformView, // |PlatformView| PointerDataDispatcherMaker GetDispatcherMaker() override; + // |PlatformView| + std::unique_ptr CreateRenderingStudio() override; + // |PlatformView| std::unique_ptr CreateRenderingSurface(int64_t view_id) override; diff --git a/shell/common/shell_test_platform_view_metal.mm b/shell/common/shell_test_platform_view_metal.mm index 77c54ce4e4c2f..51c74c65a950f 100644 --- a/shell/common/shell_test_platform_view_metal.mm +++ b/shell/common/shell_test_platform_view_metal.mm @@ -10,6 +10,7 @@ #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/gpu/gpu_surface_metal_skia.h" +#include "flutter/shell/gpu/gpu_studio_metal_skia.h" #include "flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalSkia.h" namespace flutter { @@ -91,6 +92,11 @@ GPUMTLTextureInfo offscreen_texture_info() const { }; } +// |PlatformView| +std::unique_ptr ShellTestPlatformViewMetal::CreateRenderingStudio() { + return std::make_unique(this, [metal_context_->context() mainContext]); +} + // |PlatformView| std::unique_ptr ShellTestPlatformViewMetal::CreateRenderingSurface(int64_t view_id) { return std::make_unique(this, [metal_context_->context() mainContext], diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index ce40fcb496c1f..94b05920547bb 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -39,7 +39,12 @@ ShellTestPlatformViewVulkan::ShellTestPlatformViewVulkan( vsync_clock_(std::move(vsync_clock)), proc_table_(fml::MakeRefCounted(VULKAN_SO_PATH)), shell_test_external_view_embedder_( - std::move(shell_test_external_view_embedder)) {} + std::move(shell_test_external_view_embedder)) { + // Create the Skia GrContext. + if (!CreateSkiaGrContext()) { + FML_DLOG(ERROR) << "Could not create Skia context."; + } +} ShellTestPlatformViewVulkan::~ShellTestPlatformViewVulkan() = default; @@ -51,11 +56,17 @@ void ShellTestPlatformViewVulkan::SimulateVSync() { vsync_clock_->SimulateVSync(); } +// |PlatformView| +std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingStudio() { + return std::make_unique(context_, this); +} + // |PlatformView| std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingSurface( int64_t view_id) { return std::make_unique(proc_table_, - shell_test_external_view_embedder_); + shell_test_external_view_embedder_, + context_); } // |PlatformView| @@ -71,66 +82,7 @@ PointerDataDispatcherMaker ShellTestPlatformViewVulkan::GetDispatcherMaker() { }; } -// TODO(gw280): This code was forked from vulkan_window.cc specifically for -// shell_test. -// We need to merge this functionality back into //vulkan. -// https://github.com/flutter/flutter/issues/51132 -ShellTestPlatformViewVulkan::OffScreenSurface::OffScreenSurface( - fml::RefPtr vk, - std::shared_ptr - shell_test_external_view_embedder) - : valid_(false), - vk_(std::move(vk)), - shell_test_external_view_embedder_( - std::move(shell_test_external_view_embedder)) { - if (!vk_ || !vk_->HasAcquiredMandatoryProcAddresses()) { - FML_DLOG(ERROR) << "Proc table has not acquired mandatory proc addresses."; - return; - } - - // Create the application instance. - std::vector extensions = { - VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME, - }; - - application_ = std::make_unique( - *vk_, "FlutterTest", std::move(extensions), VK_MAKE_VERSION(1, 0, 0), - VK_MAKE_VERSION(1, 1, 0), true); - - if (!application_->IsValid() || !vk_->AreInstanceProcsSetup()) { - // Make certain the application instance was created and it set up the - // instance proc table entries. - FML_DLOG(ERROR) << "Instance proc addresses have not been set up."; - return; - } - - // Create the device. - - logical_device_ = application_->AcquireFirstCompatibleLogicalDevice(); - - if (logical_device_ == nullptr || !logical_device_->IsValid() || - !vk_->AreDeviceProcsSetup()) { - // Make certain the device was created and it set up the device proc table - // entries. - FML_DLOG(ERROR) << "Device proc addresses have not been set up."; - return; - } - - memory_allocator_ = FlutterSkiaVulkanMemoryAllocator::Make( - application_->GetAPIVersion(), application_->GetInstance(), - logical_device_->GetPhysicalDeviceHandle(), logical_device_->GetHandle(), - vk_, true); - - // Create the Skia GrContext. - if (!CreateSkiaGrContext()) { - FML_DLOG(ERROR) << "Could not create Skia context."; - return; - } - - valid_ = true; -} - -bool ShellTestPlatformViewVulkan::OffScreenSurface::CreateSkiaGrContext() { +bool ShellTestPlatformViewVulkan::CreateSkiaGrContext() { GrVkBackendContext backend_context; if (!CreateSkiaBackendContext(&backend_context)) { @@ -156,9 +108,9 @@ bool ShellTestPlatformViewVulkan::OffScreenSurface::CreateSkiaGrContext() { return true; } -bool ShellTestPlatformViewVulkan::OffScreenSurface::CreateSkiaBackendContext( +bool ShellTestPlatformViewVulkan::CreateSkiaBackendContext( GrVkBackendContext* context) { - auto getProc = CreateSkiaGetProc(vk_); + auto getProc = CreateSkiaGetProc(proc_table_); if (getProc == nullptr) { FML_DLOG(ERROR) << "GetProcAddress is null"; @@ -186,6 +138,60 @@ bool ShellTestPlatformViewVulkan::OffScreenSurface::CreateSkiaBackendContext( return true; } +// TODO(gw280): This code was forked from vulkan_window.cc specifically for +// shell_test. +// We need to merge this functionality back into //vulkan. +// https://github.com/flutter/flutter/issues/51132 +ShellTestPlatformViewVulkan::OffScreenSurface::OffScreenSurface( + fml::RefPtr vk, + std::shared_ptr + shell_test_external_view_embedder) + : valid_(false), + vk_(std::move(vk)), + shell_test_external_view_embedder_( + std::move(shell_test_external_view_embedder)), + context_(context) { + if (!vk_ || !vk_->HasAcquiredMandatoryProcAddresses()) { + FML_DLOG(ERROR) << "Proc table has not acquired mandatory proc addresses."; + return; + } + + // Create the application instance. + std::vector extensions = { + VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME, + }; + + application_ = std::make_unique( + *vk_, "FlutterTest", std::move(extensions), VK_MAKE_VERSION(1, 0, 0), + VK_MAKE_VERSION(1, 1, 0), true); + + if (!application_->IsValid() || !vk_->AreInstanceProcsSetup()) { + // Make certain the application instance was created and it set up the + // instance proc table entries. + FML_DLOG(ERROR) << "Instance proc addresses have not been set up."; + return; + } + + // Create the device. + + logical_device_ = application_->AcquireFirstCompatibleLogicalDevice(); + + if (logical_device_ == nullptr || !logical_device_->IsValid() || + !vk_->AreDeviceProcsSetup()) { + // Make certain the device was created and it set up the device proc table + // entries. + FML_DLOG(ERROR) << "Device proc addresses have not been set up."; + return; + } + + memory_allocator_ = FlutterSkiaVulkanMemoryAllocator::Make( + application_->GetAPIVersion(), application_->GetInstance(), + logical_device_->GetPhysicalDeviceHandle(), logical_device_->GetHandle(), + vk_, true); + + valid_ = true; +} + ShellTestPlatformViewVulkan::OffScreenSurface::~OffScreenSurface() {} bool ShellTestPlatformViewVulkan::OffScreenSurface::IsValid() { diff --git a/shell/common/shell_test_platform_view_vulkan.h b/shell/common/shell_test_platform_view_vulkan.h index 323112ceba3ad..bd5b2db9a476d 100644 --- a/shell/common/shell_test_platform_view_vulkan.h +++ b/shell/common/shell_test_platform_view_vulkan.h @@ -33,7 +33,8 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { public: OffScreenSurface(fml::RefPtr vk, std::shared_ptr - shell_test_external_view_embedder); + shell_test_external_view_embedder, + sk_sp context); ~OffScreenSurface() override; @@ -58,9 +59,6 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { sk_sp memory_allocator_; sk_sp context_; - bool CreateSkiaGrContext(); - bool CreateSkiaBackendContext(GrVkBackendContext* context); - FML_DISALLOW_COPY_AND_ASSIGN(OffScreenSurface); }; @@ -73,6 +71,11 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { std::shared_ptr shell_test_external_view_embedder_; + sk_sp context_; + + // |PlatformView| + std::unique_ptr CreateRenderingStudio() override; + // |PlatformView| std::unique_ptr CreateRenderingSurface(int64_t view_id) override; @@ -85,6 +88,9 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { // |PlatformView| PointerDataDispatcherMaker GetDispatcherMaker() override; + bool CreateSkiaGrContext(); + bool CreateSkiaBackendContext(GrVkBackendContext* context); + FML_DISALLOW_COPY_AND_ASSIGN(ShellTestPlatformViewVulkan); }; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 3978336f07be9..4ec827a985711 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -117,15 +117,10 @@ class MockPlatformViewDelegate : public PlatformView::Delegate { AssetResolver::AssetResolverType type)); }; -class MockSurface : public Surface { +class MockStudio : public Studio { public: MOCK_METHOD0(IsValid, bool()); - MOCK_METHOD1(AcquireFrame, - std::unique_ptr(const SkISize& size)); - - MOCK_CONST_METHOD0(GetRootTransformation, SkMatrix()); - MOCK_METHOD0(GetContext, GrDirectContext*()); MOCK_METHOD0(MakeRenderContextCurrent, std::unique_ptr()); @@ -133,11 +128,24 @@ class MockSurface : public Surface { MOCK_METHOD0(ClearRenderContext, bool()); }; +class MockSurface : public Surface { + public: + MOCK_METHOD0(IsValid, bool()); + + MOCK_METHOD0(GetContext, GrDirectContext*()); + + MOCK_METHOD1(AcquireFrame, + std::unique_ptr(const SkISize& size)); + + MOCK_CONST_METHOD0(GetRootTransformation, SkMatrix()); +}; + class MockPlatformView : public PlatformView { public: MockPlatformView(MockPlatformViewDelegate& delegate, const TaskRunners& task_runners) : PlatformView(delegate, task_runners) {} + MOCK_METHOD0(CreateRenderingStudio, std::unique_ptr()); MOCK_METHOD1(CreateRenderingSurface, std::unique_ptr(int64_t)); MOCK_CONST_METHOD0(GetPlatformMessageHandler, std::shared_ptr()); @@ -147,6 +155,7 @@ class TestPlatformView : public PlatformView { public: TestPlatformView(Shell& shell, const TaskRunners& task_runners) : PlatformView(shell, task_runners) {} + MOCK_METHOD0(CreateRenderingStudio, std::unique_ptr()); MOCK_METHOD1(CreateRenderingSurface, std::unique_ptr(int64_t)); }; @@ -1597,16 +1606,24 @@ TEST_F(ShellTest, MultipleFluttersSetResourceCacheBytes) { [task_runners, main_context](flutter::Shell& shell) { auto result = std::make_unique(shell, task_runners); ON_CALL(*result, CreateRenderingSurface(0ll)) + .WillByDefault(::testing::Invoke( + [main_context] { + auto surface = std::make_unique(); + ON_CALL(*surface, GetContext()) + .WillByDefault(Return(main_context.get())); + return surface; + })); + ON_CALL(*result, CreateRenderingStudio()) .WillByDefault(::testing::Invoke([main_context] { - auto surface = std::make_unique(); - ON_CALL(*surface, GetContext()) + auto studio = std::make_unique(); + ON_CALL(*studio, GetContext()) .WillByDefault(Return(main_context.get())); - ON_CALL(*surface, IsValid()).WillByDefault(Return(true)); - ON_CALL(*surface, MakeRenderContextCurrent()) + ON_CALL(*studio, IsValid()).WillByDefault(Return(true)); + ON_CALL(*studio, MakeRenderContextCurrent()) .WillByDefault(::testing::Invoke([] { return std::make_unique(true); })); - return surface; + return studio; })); return result; }; @@ -3118,6 +3135,9 @@ TEST_F(ShellTest, Spawn) { ON_CALL(*result, CreateRenderingSurface(0ll)) .WillByDefault(::testing::Invoke( [] { return std::make_unique(); })); + ON_CALL(*result, CreateRenderingStudio()) + .WillByDefault(::testing::Invoke( + [] { return std::make_unique(); })); return result; }, [](Shell& shell) { return std::make_unique(shell); }); @@ -3230,6 +3250,9 @@ TEST_F(ShellTest, SpawnWithDartEntrypointArgs) { ON_CALL(*result, CreateRenderingSurface(0ll)) .WillByDefault(::testing::Invoke( [] { return std::make_unique(); })); + ON_CALL(*result, CreateRenderingStudio()) + .WillByDefault(::testing::Invoke( + [] { return std::make_unique(); })); return result; }, [](Shell& shell) { return std::make_unique(shell); }); @@ -3293,6 +3316,9 @@ TEST_F(ShellTest, IOManagerIsSharedBetweenParentAndSpawnedShell) { ON_CALL(*result, CreateRenderingSurface(0ll)) .WillByDefault(::testing::Invoke( [] { return std::make_unique(); })); + ON_CALL(*result, CreateRenderingStudio()) + .WillByDefault(::testing::Invoke( + [] { return std::make_unique(); })); return result; }, [](Shell& shell) { return std::make_unique(shell); }); @@ -3347,6 +3373,9 @@ TEST_F(ShellTest, IOManagerInSpawnedShellIsNotNullAfterParentShellDestroyed) { ON_CALL(*result, CreateRenderingSurface(0ll)) .WillByDefault(::testing::Invoke( [] { return std::make_unique(); })); + ON_CALL(*result, CreateRenderingStudio()) + .WillByDefault(::testing::Invoke( + [] { return std::make_unique(); })); return result; }, [](Shell& shell) { return std::make_unique(shell); }); @@ -3395,6 +3424,9 @@ TEST_F(ShellTest, ImageGeneratorRegistryNotNullAfterParentShellDestroyed) { ON_CALL(*result, CreateRenderingSurface(0ll)) .WillByDefault(::testing::Invoke( [] { return std::make_unique(); })); + ON_CALL(*result, CreateRenderingStudio()) + .WillByDefault(::testing::Invoke( + [] { return std::make_unique(); })); return result; }, [](Shell& shell) { return std::make_unique(shell); }); @@ -3891,8 +3923,11 @@ TEST_F(ShellTest, SpawnWorksWithOnError) { std::make_unique<::testing::NiceMock>( platform_view_delegate, shell.GetTaskRunners()); ON_CALL(*result, CreateRenderingSurface(0ll)) + .WillByDefault(::testing::Invoke( + [] { return std::make_unique(); })); + ON_CALL(*result, CreateRenderingStudio()) .WillByDefault(::testing::Invoke([] { - return std::make_unique<::testing::NiceMock>(); + return std::make_unique<::testing::NiceMock>(); })); return result; }, diff --git a/shell/platform/embedder/platform_view_embedder.h b/shell/platform/embedder/platform_view_embedder.h index b0e723dece794..9766f6bf8a8a6 100644 --- a/shell/platform/embedder/platform_view_embedder.h +++ b/shell/platform/embedder/platform_view_embedder.h @@ -14,21 +14,8 @@ #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/embedder/embedder_studio.h" #include "flutter/shell/platform/embedder/embedder_surface.h" -// #include "flutter/shell/platform/embedder/embedder_surface_software.h" #include "flutter/shell/platform/embedder/vsync_waiter_embedder.h" -// #ifdef SHELL_ENABLE_GL -// #include "flutter/shell/platform/embedder/embedder_surface_gl.h" -// #endif - -// #ifdef SHELL_ENABLE_METAL -// #include "flutter/shell/platform/embedder/embedder_surface_metal.h" -// #endif - -// #ifdef SHELL_ENABLE_VULKAN -// #include "flutter/shell/platform/embedder/embedder_surface_vulkan.h" -// #endif - namespace flutter { class PlatformViewEmbedder final : public PlatformView { From 0a1aa0a900d84d22311880ad47c4cfd948ba5c80 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 22:57:02 -0700 Subject: [PATCH 035/147] Format --- shell/common/shell_test_platform_view_gl.cc | 6 ++---- shell/common/shell_test_platform_view_metal.mm | 2 +- shell/common/shell_test_platform_view_vulkan.cc | 5 ++--- shell/common/shell_unittests.cc | 13 ++++++------- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/shell/common/shell_test_platform_view_gl.cc b/shell/common/shell_test_platform_view_gl.cc index 1ed6e9e47ffba..d4c182f406c2f 100644 --- a/shell/common/shell_test_platform_view_gl.cc +++ b/shell/common/shell_test_platform_view_gl.cc @@ -41,8 +41,7 @@ std::unique_ptr ShellTestPlatformViewGL::CreateRenderingStudio() { if (main_context_ == nullptr) { main_context_ = GPUSurfaceGLSkia::MakeGLContext(this); } - return std::make_unique( - main_context_, this); + return std::make_unique(main_context_, this); } // |PlatformView| @@ -51,8 +50,7 @@ std::unique_ptr ShellTestPlatformViewGL::CreateRenderingSurface( if (main_context_ == nullptr) { main_context_ = GPUSurfaceGLSkia::MakeGLContext(this); } - return std::make_unique( - main_context_, this, true); + return std::make_unique(main_context_, this, true); } // |PlatformView| diff --git a/shell/common/shell_test_platform_view_metal.mm b/shell/common/shell_test_platform_view_metal.mm index 51c74c65a950f..12a4a073170fd 100644 --- a/shell/common/shell_test_platform_view_metal.mm +++ b/shell/common/shell_test_platform_view_metal.mm @@ -9,8 +9,8 @@ #include #include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/shell/gpu/gpu_surface_metal_skia.h" #include "flutter/shell/gpu/gpu_studio_metal_skia.h" +#include "flutter/shell/gpu/gpu_surface_metal_skia.h" #include "flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalSkia.h" namespace flutter { diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index 94b05920547bb..dbb1ecd79ca3b 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -64,9 +64,8 @@ std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingStudio() { // |PlatformView| std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingSurface( int64_t view_id) { - return std::make_unique(proc_table_, - shell_test_external_view_embedder_, - context_); + return std::make_unique( + proc_table_, shell_test_external_view_embedder_, context_); } // |PlatformView| diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 4ec827a985711..ecfcd34b55388 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -1606,13 +1606,12 @@ TEST_F(ShellTest, MultipleFluttersSetResourceCacheBytes) { [task_runners, main_context](flutter::Shell& shell) { auto result = std::make_unique(shell, task_runners); ON_CALL(*result, CreateRenderingSurface(0ll)) - .WillByDefault(::testing::Invoke( - [main_context] { - auto surface = std::make_unique(); - ON_CALL(*surface, GetContext()) - .WillByDefault(Return(main_context.get())); - return surface; - })); + .WillByDefault(::testing::Invoke([main_context] { + auto surface = std::make_unique(); + ON_CALL(*surface, GetContext()) + .WillByDefault(Return(main_context.get())); + return surface; + })); ON_CALL(*result, CreateRenderingStudio()) .WillByDefault(::testing::Invoke([main_context] { auto studio = std::make_unique(); From 801b4f4269740bd4d6ae5800f72c5085a077052e Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 20 Mar 2023 23:41:56 -0700 Subject: [PATCH 036/147] Fix shell unittest --- shell/common/shell_unittests.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index ecfcd34b55388..99f5824b944fa 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -1606,10 +1606,9 @@ TEST_F(ShellTest, MultipleFluttersSetResourceCacheBytes) { [task_runners, main_context](flutter::Shell& shell) { auto result = std::make_unique(shell, task_runners); ON_CALL(*result, CreateRenderingSurface(0ll)) - .WillByDefault(::testing::Invoke([main_context] { + .WillByDefault(::testing::Invoke([] { auto surface = std::make_unique(); - ON_CALL(*surface, GetContext()) - .WillByDefault(Return(main_context.get())); + ON_CALL(*surface, IsValid()).WillByDefault(Return(true)); return surface; })); ON_CALL(*result, CreateRenderingStudio()) From aab8ece144c94644ee2cf14a71144d62fb6dad69 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 21 Mar 2023 01:11:53 -0700 Subject: [PATCH 037/147] SkSLPrecompiler --- shell/common/shell_test_platform_view_metal.h | 1 + .../common/shell_test_platform_view_metal.mm | 6 +++-- shell/gpu/gpu_studio_metal_skia.h | 6 +++-- shell/gpu/gpu_studio_metal_skia.mm | 21 +++++---------- shell/gpu/gpu_surface_metal_delegate.cc | 15 +++++++++++ shell/gpu/gpu_surface_metal_delegate.h | 11 ++++++++ shell/gpu/gpu_surface_metal_skia.h | 4 ++- shell/gpu/gpu_surface_metal_skia.mm | 26 +++++++------------ .../darwin/ios/ios_surface_metal_skia.h | 1 + .../darwin/ios/ios_surface_metal_skia.mm | 15 ++++++----- .../platform/embedder/embedder_studio_metal.h | 1 + .../embedder/embedder_studio_metal.mm | 6 +++-- .../embedder/embedder_surface_metal.h | 3 +++ .../embedder/embedder_surface_metal.mm | 13 ++++++---- 14 files changed, 78 insertions(+), 51 deletions(-) diff --git a/shell/common/shell_test_platform_view_metal.h b/shell/common/shell_test_platform_view_metal.h index 2372663f2d009..bffea1e20eeff 100644 --- a/shell/common/shell_test_platform_view_metal.h +++ b/shell/common/shell_test_platform_view_metal.h @@ -29,6 +29,7 @@ class ShellTestPlatformViewMetal final : public ShellTestPlatformView, private: const std::unique_ptr metal_context_; + std::shared_ptr sksl_precompiler_; const CreateVsyncWaiter create_vsync_waiter_; const std::shared_ptr vsync_clock_; const std::shared_ptr diff --git a/shell/common/shell_test_platform_view_metal.mm b/shell/common/shell_test_platform_view_metal.mm index 12a4a073170fd..f9c8deb131c99 100644 --- a/shell/common/shell_test_platform_view_metal.mm +++ b/shell/common/shell_test_platform_view_metal.mm @@ -67,6 +67,7 @@ GPUMTLTextureInfo offscreen_texture_info() const { vsync_clock_(std::move(vsync_clock)), shell_test_external_view_embedder_(std::move(shell_test_external_view_embedder)) { FML_CHECK([metal_context_->context() mainContext] != nil); + sksl_precompiler_ = std::make_shared(); } ShellTestPlatformViewMetal::~ShellTestPlatformViewMetal() = default; @@ -94,13 +95,14 @@ GPUMTLTextureInfo offscreen_texture_info() const { // |PlatformView| std::unique_ptr ShellTestPlatformViewMetal::CreateRenderingStudio() { - return std::make_unique(this, [metal_context_->context() mainContext]); + return std::make_unique(this, [metal_context_->context() mainContext], + sksl_precompiler_); } // |PlatformView| std::unique_ptr ShellTestPlatformViewMetal::CreateRenderingSurface(int64_t view_id) { return std::make_unique(this, [metal_context_->context() mainContext], - MsaaSampleCount::kNone); + MsaaSampleCount::kNone, sksl_precompiler_); } // |GPUSurfaceMetalDelegate| diff --git a/shell/gpu/gpu_studio_metal_skia.h b/shell/gpu/gpu_studio_metal_skia.h index 842a926e1d5e8..08d83ace65439 100644 --- a/shell/gpu/gpu_studio_metal_skia.h +++ b/shell/gpu/gpu_studio_metal_skia.h @@ -17,7 +17,9 @@ namespace flutter { class SK_API_AVAILABLE_CA_METAL_LAYER GPUStudioMetalSkia : public Studio { public: GPUStudioMetalSkia(GPUSurfaceMetalDelegate* delegate, - sk_sp context); + sk_sp context, + std::shared_ptr + sksl_precompiler); // |Studio| ~GPUStudioMetalSkia(); @@ -28,7 +30,7 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUStudioMetalSkia : public Studio { private: const GPUSurfaceMetalDelegate* delegate_; sk_sp context_; - GrDirectContext* precompiled_sksl_context_ = nullptr; + std::shared_ptr sksl_precompiler_; // |Studio| GrDirectContext* GetContext() override; diff --git a/shell/gpu/gpu_studio_metal_skia.mm b/shell/gpu/gpu_studio_metal_skia.mm index 91dc90e496fb8..58d9866d5997f 100644 --- a/shell/gpu/gpu_studio_metal_skia.mm +++ b/shell/gpu/gpu_studio_metal_skia.mm @@ -9,7 +9,6 @@ #include -#include "flutter/common/graphics/persistent_cache.h" #include "flutter/fml/make_copyable.h" #include "flutter/fml/platform/darwin/cf_utils.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" @@ -28,9 +27,11 @@ namespace flutter { -GPUStudioMetalSkia::GPUStudioMetalSkia(GPUSurfaceMetalDelegate* delegate, - sk_sp context) - : delegate_(delegate), context_(std::move(context)) {} +GPUStudioMetalSkia::GPUStudioMetalSkia( + GPUSurfaceMetalDelegate* delegate, + sk_sp context, + std::shared_ptr sksl_precompiler) + : delegate_(delegate), context_(std::move(context)), sksl_precompiler_(sksl_precompiler) {} GPUStudioMetalSkia::~GPUStudioMetalSkia() = default; @@ -39,16 +40,6 @@ return context_ != nullptr; } -void GPUStudioMetalSkia::PrecompileKnownSkSLsIfNecessary() { - auto* current_context = GetContext(); - if (current_context == precompiled_sksl_context_) { - // Known SkSLs have already been prepared in this context. - return; - } - precompiled_sksl_context_ = current_context; - flutter::PersistentCache::GetCacheForProcess()->PrecompileKnownSkSLs(precompiled_sksl_context_); -} - // |Studio| GrDirectContext* GPUStudioMetalSkia::GetContext() { return context_.get(); @@ -58,7 +49,7 @@ std::unique_ptr GPUStudioMetalSkia::MakeRenderContextCurrent() { // A context may either be necessary to render to the surface or to snapshot an offscreen // surface. Either way, SkSL precompilation must be attempted. - PrecompileKnownSkSLsIfNecessary(); + sksl_precompiler_->PrecompileKnownSkSLsIfNecessary(GetContext()); // This backend has no such concept. return std::make_unique(true); diff --git a/shell/gpu/gpu_surface_metal_delegate.cc b/shell/gpu/gpu_surface_metal_delegate.cc index 7a22d8de88a89..8b1fd17a3cab3 100644 --- a/shell/gpu/gpu_surface_metal_delegate.cc +++ b/shell/gpu/gpu_surface_metal_delegate.cc @@ -4,6 +4,8 @@ #include "flutter/shell/gpu/gpu_surface_metal_delegate.h" +#include "flutter/common/graphics/persistent_cache.h" + namespace flutter { GPUSurfaceMetalDelegate::GPUSurfaceMetalDelegate( @@ -20,4 +22,17 @@ bool GPUSurfaceMetalDelegate::AllowsDrawingWhenGpuDisabled() const { return true; } +GPUSurfaceMetalDelegate::SkSLPrecompiler::SkSLPrecompiler() {} + +void GPUSurfaceMetalDelegate::SkSLPrecompiler::PrecompileKnownSkSLsIfNecessary( + GrDirectContext* current_context) { + if (current_context == precompiled_sksl_context_) { + // Known SkSLs have already been prepared in this context. + return; + } + precompiled_sksl_context_ = current_context; + flutter::PersistentCache::GetCacheForProcess()->PrecompileKnownSkSLs( + precompiled_sksl_context_); +} + } // namespace flutter diff --git a/shell/gpu/gpu_surface_metal_delegate.h b/shell/gpu/gpu_surface_metal_delegate.h index 6d5e0852d8c85..0136dc71d785b 100644 --- a/shell/gpu/gpu_surface_metal_delegate.h +++ b/shell/gpu/gpu_surface_metal_delegate.h @@ -50,6 +50,17 @@ enum class MTLRenderTargetType { kMTLTexture, kCAMetalLayer }; /// class GPUSurfaceMetalDelegate { public: + class SkSLPrecompiler { + public: + SkSLPrecompiler(); + void PrecompileKnownSkSLsIfNecessary(GrDirectContext* current_context); + + private: + GrDirectContext* precompiled_sksl_context_; + + FML_DISALLOW_COPY_AND_ASSIGN(SkSLPrecompiler); + }; + //------------------------------------------------------------------------------ /// @brief Construct a new GPUSurfaceMetalDelegate object with the specified /// render_target type. diff --git a/shell/gpu/gpu_surface_metal_skia.h b/shell/gpu/gpu_surface_metal_skia.h index bd7d6a35135f3..c04f3213d51d2 100644 --- a/shell/gpu/gpu_surface_metal_skia.h +++ b/shell/gpu/gpu_surface_metal_skia.h @@ -18,6 +18,8 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUSurfaceMetalSkia : public Surface { GPUSurfaceMetalSkia(GPUSurfaceMetalDelegate* delegate, sk_sp context, MsaaSampleCount msaa_samples, + std::shared_ptr + sksl_precompiler, bool render_to_surface = true); // |Surface| @@ -30,8 +32,8 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUSurfaceMetalSkia : public Surface { const GPUSurfaceMetalDelegate* delegate_; const MTLRenderTargetType render_target_type_; sk_sp context_; - GrDirectContext* precompiled_sksl_context_ = nullptr; MsaaSampleCount msaa_samples_ = MsaaSampleCount::kNone; + std::shared_ptr sksl_precompiler_; // TODO(38466): Refactor GPU surface APIs take into account the fact that an // external view embedder may want to render to the root surface. This is a // hack to make avoid allocating resources for the root surface when an diff --git a/shell/gpu/gpu_surface_metal_skia.mm b/shell/gpu/gpu_surface_metal_skia.mm index 4f625a9e724a8..7356684d4f4c6 100644 --- a/shell/gpu/gpu_surface_metal_skia.mm +++ b/shell/gpu/gpu_surface_metal_skia.mm @@ -9,7 +9,6 @@ #include -#include "flutter/common/graphics/persistent_cache.h" #include "flutter/fml/make_copyable.h" #include "flutter/fml/platform/darwin/cf_utils.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" @@ -50,14 +49,17 @@ } } // namespace -GPUSurfaceMetalSkia::GPUSurfaceMetalSkia(GPUSurfaceMetalDelegate* delegate, - sk_sp context, - MsaaSampleCount msaa_samples, - bool render_to_surface) +GPUSurfaceMetalSkia::GPUSurfaceMetalSkia( + GPUSurfaceMetalDelegate* delegate, + sk_sp context, + MsaaSampleCount msaa_samples, + std::shared_ptr sksl_precompiler, + bool render_to_surface) : delegate_(delegate), render_target_type_(delegate->GetRenderTargetType()), context_(std::move(context)), msaa_samples_(msaa_samples), + sksl_precompiler_(sksl_precompiler), render_to_surface_(render_to_surface) { // If this preference is explicitly set, we allow for disabling partial repaint. NSNumber* disablePartialRepaint = @@ -74,16 +76,6 @@ return context_ != nullptr; } -void GPUSurfaceMetalSkia::PrecompileKnownSkSLsIfNecessary() { - auto* current_context = GetContext(); - if (current_context == precompiled_sksl_context_) { - // Known SkSLs have already been prepared in this context. - return; - } - precompiled_sksl_context_ = current_context; - flutter::PersistentCache::GetCacheForProcess()->PrecompileKnownSkSLs(precompiled_sksl_context_); -} - // |Surface| std::unique_ptr GPUSurfaceMetalSkia::AcquireFrame(const SkISize& frame_size) { if (!IsValid()) { @@ -102,7 +94,7 @@ [](const SurfaceFrame& surface_frame, DlCanvas* canvas) { return true; }, frame_size); } - PrecompileKnownSkSLsIfNecessary(); + sksl_precompiler_->PrecompileKnownSkSLsIfNecessary(GetContext()); switch (render_target_type_) { case MTLRenderTargetType::kCAMetalLayer: @@ -256,7 +248,7 @@ std::unique_ptr GPUSurfaceMetalSkia::MakeRenderContextCurrent() { // A context may either be necessary to render to the surface or to snapshot an offscreen // surface. Either way, SkSL precompilation must be attempted. - PrecompileKnownSkSLsIfNecessary(); + sksl_precompiler_->PrecompileKnownSkSLsIfNecessary(GetContext()); // This backend has no such concept. return std::make_unique(true); diff --git a/shell/platform/darwin/ios/ios_surface_metal_skia.h b/shell/platform/darwin/ios/ios_surface_metal_skia.h index 80c1f0f5cfbd3..43f6dd1268ae9 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_skia.h +++ b/shell/platform/darwin/ios/ios_surface_metal_skia.h @@ -28,6 +28,7 @@ class SK_API_AVAILABLE_CA_METAL_LAYER IOSSurfaceMetalSkia final : public IOSSurf id device_; id command_queue_; bool is_valid_ = false; + std::shared_ptr sksl_precompiler_; // |IOSSurface| bool IsValid() const override; diff --git a/shell/platform/darwin/ios/ios_surface_metal_skia.mm b/shell/platform/darwin/ios/ios_surface_metal_skia.mm index 7a64898ff865a..15b465528d221 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_skia.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_skia.mm @@ -25,6 +25,7 @@ auto darwin_context = metal_context->GetDarwinContext().get(); command_queue_ = darwin_context.commandQueue; device_ = darwin_context.device; + sksl_precompiler_ = std::make_shared(); } // |IOSSurface| @@ -43,18 +44,18 @@ // |IOSSurface| std::unique_ptr IOSSurfaceMetalSkia::CreateGPUStudio(GrDirectContext* context) { FML_DCHECK(context); - return std::make_unique(this, // delegate - sk_ref_sp(context) // context - ); + return std::make_unique(this, // delegate + sk_ref_sp(context), // context + sksl_precompiler_); } // |IOSSurface| std::unique_ptr IOSSurfaceMetalSkia::CreateGPUSurface(GrDirectContext* context) { FML_DCHECK(context); - return std::make_unique(this, // delegate - sk_ref_sp(context), // context - GetContext()->GetMsaaSampleCount() // sample count - ); + return std::make_unique(this, // delegate + sk_ref_sp(context), // context + GetContext()->GetMsaaSampleCount(), // sample count + sksl_precompiler_); } // |GPUSurfaceMetalDelegate| diff --git a/shell/platform/embedder/embedder_studio_metal.h b/shell/platform/embedder/embedder_studio_metal.h index eefc8e2745a12..6c7d9e6e838fe 100644 --- a/shell/platform/embedder/embedder_studio_metal.h +++ b/shell/platform/embedder/embedder_studio_metal.h @@ -64,6 +64,7 @@ class EmbedderStudioMetal final : public EmbedderStudio, sk_sp surface_; sk_sp main_context_; sk_sp resource_context_; + std::shared_ptr sksl_precompiler_; FML_DISALLOW_COPY_AND_ASSIGN(EmbedderStudioMetal); }; diff --git a/shell/platform/embedder/embedder_studio_metal.mm b/shell/platform/embedder/embedder_studio_metal.mm index 0425abdcbe0f0..6a7f3fb267815 100644 --- a/shell/platform/embedder/embedder_studio_metal.mm +++ b/shell/platform/embedder/embedder_studio_metal.mm @@ -30,6 +30,7 @@ resource_context_ = [FlutterDarwinContextMetalSkia createGrContext:(id)device commandQueue:(id)command_queue]; + sksl_precompiler_ = std::make_shared(); valid_ = main_context_ && resource_context_; } @@ -48,7 +49,7 @@ return nullptr; } - auto studio = std::make_unique(this, main_context_); + auto studio = std::make_unique(this, main_context_, sksl_precompiler_); if (!studio->IsValid()) { return nullptr; @@ -63,7 +64,8 @@ } const bool render_to_surface = !external_view_embedder_; return std::make_unique(main_context_, - this, // GPU surface GL delegate + this, // GPU surface GL delegate + sksl_precompiler_, render_to_surface // render to surface ); } diff --git a/shell/platform/embedder/embedder_surface_metal.h b/shell/platform/embedder/embedder_surface_metal.h index 54ad165e5e3c4..63bc238dbefd6 100644 --- a/shell/platform/embedder/embedder_surface_metal.h +++ b/shell/platform/embedder/embedder_surface_metal.h @@ -20,6 +20,8 @@ class EmbedderSurfaceMetal final : public EmbedderSurface { public: EmbedderSurfaceMetal(sk_sp main_context, EmbedderStudioMetal* studio, + std::shared_ptr + sksl_precompiler, bool render_to_surface); ~EmbedderSurfaceMetal() override; @@ -30,6 +32,7 @@ class EmbedderSurfaceMetal final : public EmbedderSurface { private: sk_sp main_context_; EmbedderStudioMetal* studio_; + std::shared_ptr sksl_precompiler_; bool render_to_surface_; // |EmbedderSurface| diff --git a/shell/platform/embedder/embedder_surface_metal.mm b/shell/platform/embedder/embedder_surface_metal.mm index 8560c2e5d6174..1f237eb5d3213 100644 --- a/shell/platform/embedder/embedder_surface_metal.mm +++ b/shell/platform/embedder/embedder_surface_metal.mm @@ -15,11 +15,14 @@ FLUTTER_ASSERT_NOT_ARC namespace flutter { -EmbedderSurfaceMetal::EmbedderSurfaceMetal(sk_sp main_context, - EmbedderStudioMetal* studio, - bool render_to_surface) +EmbedderSurfaceMetal::EmbedderSurfaceMetal( + sk_sp main_context, + EmbedderStudioMetal* studio, + std::shared_ptr sksl_precompiler, + bool render_to_surface) : main_context_(std::move(main_context)), studio_(studio), + sksl_precompiler_(sksl_precompiler), render_to_surface_(render_to_surface) {} EmbedderSurfaceMetal::~EmbedderSurfaceMetal() = default; @@ -37,8 +40,8 @@ return nullptr; } - auto surface = std::make_unique(studio_, main_context_, - MsaaSampleCount::kNone, render_to_surface_); + auto surface = std::make_unique( + studio_, main_context_, MsaaSampleCount::kNone, sksl_precompiler_, render_to_surface_); if (!surface->IsValid()) { return nullptr; From 172e4dd49d1e77075abfcd203c85c8300e9063d3 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 21 Mar 2023 12:29:28 -0700 Subject: [PATCH 038/147] Fix all tests --- shell/common/rasterizer.cc | 2 +- shell/common/rasterizer_unittests.cc | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index f29f02f25eed5..bb1734506957e 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -477,7 +477,7 @@ RasterStatus Rasterizer::DrawToSurface( FML_DCHECK(surface_); RasterStatus raster_status; - if (surface_->AllowsDrawingWhenGpuDisabled()) { + if (studio_->AllowsDrawingWhenGpuDisabled()) { raster_status = DrawToSurfaceUnsafe(frame_timings_recorder, layer_tree); } else { delegate_.GetIsGpuDisabledSyncSwitch()->Execute( diff --git a/shell/common/rasterizer_unittests.cc b/shell/common/rasterizer_unittests.cc index ae82d13127ce9..8569c83e44cec 100644 --- a/shell/common/rasterizer_unittests.cc +++ b/shell/common/rasterizer_unittests.cc @@ -899,7 +899,7 @@ TEST(RasterizerTest, [](const SurfaceFrame& frame, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); })); - ON_CALL(*surface, MakeRenderContextCurrent()) + ON_CALL(*studio, MakeRenderContextCurrent()) .WillByDefault(::testing::Invoke( [] { return std::make_unique(true); })); @@ -1070,7 +1070,7 @@ TEST(RasterizerTest, presentationTimeSetWhenVsyncTargetInFuture) { fml::CountDownLatch submit_latch(2); auto studio = std::make_unique>(); auto surface = std::make_unique(); - ON_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillByDefault(Return(true)); + ON_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillByDefault(Return(true)); ON_CALL(*surface, AcquireFrame(SkISize())) .WillByDefault(::testing::Invoke([&] { SurfaceFrame::FramebufferInfo framebuffer_info; @@ -1090,7 +1090,7 @@ TEST(RasterizerTest, presentationTimeSetWhenVsyncTargetInFuture) { /*frame_size=*/SkISize::Make(800, 600)); })); - ON_CALL(*surface, MakeRenderContextCurrent()) + ON_CALL(*studio, MakeRenderContextCurrent()) .WillByDefault(::testing::Invoke( [] { return std::make_unique(true); })); @@ -1154,7 +1154,7 @@ TEST(RasterizerTest, presentationTimeNotSetWhenVsyncTargetInPast) { fml::CountDownLatch submit_latch(1); auto studio = std::make_unique>(); auto surface = std::make_unique(); - ON_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillByDefault(Return(true)); + ON_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillByDefault(Return(true)); ON_CALL(*surface, AcquireFrame(SkISize())) .WillByDefault(::testing::Invoke([&] { SurfaceFrame::FramebufferInfo framebuffer_info; @@ -1172,7 +1172,7 @@ TEST(RasterizerTest, presentationTimeNotSetWhenVsyncTargetInPast) { /*frame_size=*/SkISize::Make(800, 600)); })); - ON_CALL(*surface, MakeRenderContextCurrent()) + ON_CALL(*studio, MakeRenderContextCurrent()) .WillByDefault(::testing::Invoke( [] { return std::make_unique(true); })); From 9489c4e744beaf76ebfe1cbbd7ef7e33b9278d9f Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 21 Mar 2023 13:35:38 -0700 Subject: [PATCH 039/147] Fix compile --- shell/common/shell_test_platform_view_gl.cc | 1 + .../common/shell_test_platform_view_vulkan.cc | 38 ++++++++++--------- .../common/shell_test_platform_view_vulkan.h | 15 ++++++-- shell/platform/fuchsia/flutter/engine.cc | 6 +++ 4 files changed, 39 insertions(+), 21 deletions(-) diff --git a/shell/common/shell_test_platform_view_gl.cc b/shell/common/shell_test_platform_view_gl.cc index d4c182f406c2f..02a205b455cf3 100644 --- a/shell/common/shell_test_platform_view_gl.cc +++ b/shell/common/shell_test_platform_view_gl.cc @@ -6,6 +6,7 @@ #include +#include "flutter/shell/gpu/gpu_studio_gl_skia.h" #include "flutter/shell/gpu/gpu_surface_gl_skia.h" namespace flutter { diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index dbb1ecd79ca3b..64c51a93675fa 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -6,9 +6,10 @@ #include -#include "flutter/common/graphics/persistent_cache.h" +// #include "flutter/common/graphics/persistent_cache.h" #include "flutter/flutter_vma/flutter_skia_vma.h" #include "flutter/shell/common/context_options.h" +#include "flutter/shell/gpu/gpu_studio_vulkan.h" #include "flutter/vulkan/vulkan_skia_proc_table.h" #include "flutter/vulkan/vulkan_utilities.h" @@ -40,6 +41,17 @@ ShellTestPlatformViewVulkan::ShellTestPlatformViewVulkan( proc_table_(fml::MakeRefCounted(VULKAN_SO_PATH)), shell_test_external_view_embedder_( std::move(shell_test_external_view_embedder)) { + // Create the application instance. + std::vector extensions = { + VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME, + }; + + application_ = std::make_unique( + *proc_table_, "FlutterTest", std::move(extensions), + VK_MAKE_VERSION(1, 0, 0), VK_MAKE_VERSION(1, 1, 0), true); + + // Create the device. + logical_device_ = application_->AcquireFirstCompatibleLogicalDevice(); // Create the Skia GrContext. if (!CreateSkiaGrContext()) { FML_DLOG(ERROR) << "Could not create Skia context."; @@ -65,7 +77,8 @@ std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingStudio() { std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingSurface( int64_t view_id) { return std::make_unique( - proc_table_, shell_test_external_view_embedder_, context_); + proc_table_, shell_test_external_view_embedder_, context_, application_, + logical_device_); } // |PlatformView| @@ -144,26 +157,21 @@ bool ShellTestPlatformViewVulkan::CreateSkiaBackendContext( ShellTestPlatformViewVulkan::OffScreenSurface::OffScreenSurface( fml::RefPtr vk, std::shared_ptr - shell_test_external_view_embedder) + shell_test_external_view_embedder, + sk_sp context, + const std::make_unique& application, + const std::unique_ptr& logical_device) : valid_(false), vk_(std::move(vk)), shell_test_external_view_embedder_( std::move(shell_test_external_view_embedder)), - context_(context) { + context_(context), +{ if (!vk_ || !vk_->HasAcquiredMandatoryProcAddresses()) { FML_DLOG(ERROR) << "Proc table has not acquired mandatory proc addresses."; return; } - // Create the application instance. - std::vector extensions = { - VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME, - }; - - application_ = std::make_unique( - *vk_, "FlutterTest", std::move(extensions), VK_MAKE_VERSION(1, 0, 0), - VK_MAKE_VERSION(1, 1, 0), true); - if (!application_->IsValid() || !vk_->AreInstanceProcsSetup()) { // Make certain the application instance was created and it set up the // instance proc table entries. @@ -171,10 +179,6 @@ ShellTestPlatformViewVulkan::OffScreenSurface::OffScreenSurface( return; } - // Create the device. - - logical_device_ = application_->AcquireFirstCompatibleLogicalDevice(); - if (logical_device_ == nullptr || !logical_device_->IsValid() || !vk_->AreDeviceProcsSetup()) { // Make certain the device was created and it set up the device proc table diff --git a/shell/common/shell_test_platform_view_vulkan.h b/shell/common/shell_test_platform_view_vulkan.h index bd5b2db9a476d..41a8ae95f279c 100644 --- a/shell/common/shell_test_platform_view_vulkan.h +++ b/shell/common/shell_test_platform_view_vulkan.h @@ -31,10 +31,13 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { private: class OffScreenSurface : public flutter::Surface { public: - OffScreenSurface(fml::RefPtr vk, - std::shared_ptr - shell_test_external_view_embedder, - sk_sp context); + OffScreenSurface( + fml::RefPtr vk, + std::shared_ptr + shell_test_external_view_embedder, + sk_sp context, + const std::make_unique& application, + const std::unique_ptr& logical_device); ~OffScreenSurface() override; @@ -73,6 +76,10 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { sk_sp context_; + std::make_unique application_; + + std::unique_ptr logical_device_; + // |PlatformView| std::unique_ptr CreateRenderingStudio() override; diff --git a/shell/platform/fuchsia/flutter/engine.cc b/shell/platform/fuchsia/flutter/engine.cc index 8146468c7b737..93695ccd6eef8 100644 --- a/shell/platform/fuchsia/flutter/engine.cc +++ b/shell/platform/fuchsia/flutter/engine.cc @@ -366,6 +366,9 @@ void Engine::Initialize( std::bind(&Engine::DestroyFlatlandView, this, std::placeholders::_1, std::placeholders::_2); + OnCreateSurface on_create_studio_callback = + std::bind(&Engine::CreateStudio, this); + OnCreateSurface on_create_surface_callback = std::bind(&Engine::CreateSurface, this); @@ -425,6 +428,7 @@ void Engine::Initialize( std::move(on_destroy_gfx_view_callback), on_destroy_flatland_view_callback = std::move(on_destroy_flatland_view_callback), + on_create_studio_callback = std::move(on_create_studio_callback), on_create_surface_callback = std::move(on_create_surface_callback), on_semantics_node_update_callback = std::move(on_semantics_node_update_callback), @@ -496,6 +500,7 @@ void Engine::Initialize( std::move(on_create_flatland_view_callback), std::move(on_update_view_callback), std::move(on_destroy_flatland_view_callback), + std::move(on_create_studio_callback), std::move(on_create_surface_callback), std::move(on_semantics_node_update_callback), std::move(on_request_announce_callback), @@ -516,6 +521,7 @@ void Engine::Initialize( std::move(on_create_gfx_view_callback), std::move(on_update_view_callback), std::move(on_destroy_gfx_view_callback), + std::move(on_create_studio_callback), std::move(on_create_surface_callback), std::move(on_semantics_node_update_callback), std::move(on_request_announce_callback), From 1eb7657a0004c8d1827d3a173f558d53200351ef Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 21 Mar 2023 21:38:31 +0000 Subject: [PATCH 040/147] Fix ShellTestPlatformViewVulkan compile --- .../common/shell_test_platform_view_vulkan.cc | 69 +++++++++---------- .../common/shell_test_platform_view_vulkan.h | 14 ++-- 2 files changed, 39 insertions(+), 44 deletions(-) diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index 64c51a93675fa..618e60ca910b6 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -41,6 +41,12 @@ ShellTestPlatformViewVulkan::ShellTestPlatformViewVulkan( proc_table_(fml::MakeRefCounted(VULKAN_SO_PATH)), shell_test_external_view_embedder_( std::move(shell_test_external_view_embedder)) { + + if (!proc_table_ || !proc_table_->HasAcquiredMandatoryProcAddresses()) { + FML_DLOG(ERROR) << "Proc table has not acquired mandatory proc addresses."; + return; + } + // Create the application instance. std::vector extensions = { VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME, @@ -50,8 +56,29 @@ ShellTestPlatformViewVulkan::ShellTestPlatformViewVulkan( *proc_table_, "FlutterTest", std::move(extensions), VK_MAKE_VERSION(1, 0, 0), VK_MAKE_VERSION(1, 1, 0), true); + if (!application_->IsValid() || !proc_table_->AreInstanceProcsSetup()) { + // Make certain the application instance was created and it set up the + // instance proc table entries. + FML_DLOG(ERROR) << "Instance proc addresses have not been set up."; + return; + } + // Create the device. logical_device_ = application_->AcquireFirstCompatibleLogicalDevice(); + + if (logical_device_ == nullptr || !logical_device_->IsValid() || + !proc_table_->AreDeviceProcsSetup()) { + // Make certain the device was created and it set up the device proc table + // entries. + FML_DLOG(ERROR) << "Device proc addresses have not been set up."; + return; + } + + memory_allocator_ = FlutterSkiaVulkanMemoryAllocator::Make( + application_->GetAPIVersion(), application_->GetInstance(), + logical_device_->GetPhysicalDeviceHandle(), logical_device_->GetHandle(), + proc_table_, true); + // Create the Skia GrContext. if (!CreateSkiaGrContext()) { FML_DLOG(ERROR) << "Could not create Skia context."; @@ -70,15 +97,14 @@ void ShellTestPlatformViewVulkan::SimulateVSync() { // |PlatformView| std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingStudio() { - return std::make_unique(context_, this); + return std::make_unique(context_); } // |PlatformView| std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingSurface( int64_t view_id) { return std::make_unique( - proc_table_, shell_test_external_view_embedder_, context_, application_, - logical_device_); + proc_table_, shell_test_external_view_embedder_, context_, memory_allocator_); } // |PlatformView| @@ -159,46 +185,17 @@ ShellTestPlatformViewVulkan::OffScreenSurface::OffScreenSurface( std::shared_ptr shell_test_external_view_embedder, sk_sp context, - const std::make_unique& application, - const std::unique_ptr& logical_device) - : valid_(false), - vk_(std::move(vk)), + sk_sp memory_allocator) + : vk_(std::move(vk)), shell_test_external_view_embedder_( std::move(shell_test_external_view_embedder)), context_(context), -{ - if (!vk_ || !vk_->HasAcquiredMandatoryProcAddresses()) { - FML_DLOG(ERROR) << "Proc table has not acquired mandatory proc addresses."; - return; - } - - if (!application_->IsValid() || !vk_->AreInstanceProcsSetup()) { - // Make certain the application instance was created and it set up the - // instance proc table entries. - FML_DLOG(ERROR) << "Instance proc addresses have not been set up."; - return; - } - - if (logical_device_ == nullptr || !logical_device_->IsValid() || - !vk_->AreDeviceProcsSetup()) { - // Make certain the device was created and it set up the device proc table - // entries. - FML_DLOG(ERROR) << "Device proc addresses have not been set up."; - return; - } - - memory_allocator_ = FlutterSkiaVulkanMemoryAllocator::Make( - application_->GetAPIVersion(), application_->GetInstance(), - logical_device_->GetPhysicalDeviceHandle(), logical_device_->GetHandle(), - vk_, true); - - valid_ = true; -} + memory_allocator_(memory_allocator) {} ShellTestPlatformViewVulkan::OffScreenSurface::~OffScreenSurface() {} bool ShellTestPlatformViewVulkan::OffScreenSurface::IsValid() { - return valid_; + return true; } std::unique_ptr diff --git a/shell/common/shell_test_platform_view_vulkan.h b/shell/common/shell_test_platform_view_vulkan.h index 41a8ae95f279c..bbb7e6929aeb7 100644 --- a/shell/common/shell_test_platform_view_vulkan.h +++ b/shell/common/shell_test_platform_view_vulkan.h @@ -36,8 +36,7 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { std::shared_ptr shell_test_external_view_embedder, sk_sp context, - const std::make_unique& application, - const std::unique_ptr& logical_device); + sk_sp memory_allocator); ~OffScreenSurface() override; @@ -53,14 +52,11 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { GrDirectContext* GetContext() 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 memory_allocator_; sk_sp context_; + sk_sp memory_allocator_; FML_DISALLOW_COPY_AND_ASSIGN(OffScreenSurface); }; @@ -76,9 +72,11 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { sk_sp context_; - std::make_unique application_; + std::unique_ptr application_; + + std::unique_ptr logical_device_; - std::unique_ptr logical_device_; + sk_sp memory_allocator_; // |PlatformView| std::unique_ptr CreateRenderingStudio() override; From 06ce3f475a1f29cd1b3c8c06b689f5882253dc64 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 21 Mar 2023 21:39:27 +0000 Subject: [PATCH 041/147] Format --- shell/common/shell_test_platform_view_vulkan.cc | 6 +++--- shell/common/shell_test_platform_view_vulkan.h | 11 +++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index 618e60ca910b6..1c3cc22cc684e 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -41,7 +41,6 @@ ShellTestPlatformViewVulkan::ShellTestPlatformViewVulkan( proc_table_(fml::MakeRefCounted(VULKAN_SO_PATH)), shell_test_external_view_embedder_( std::move(shell_test_external_view_embedder)) { - if (!proc_table_ || !proc_table_->HasAcquiredMandatoryProcAddresses()) { FML_DLOG(ERROR) << "Proc table has not acquired mandatory proc addresses."; return; @@ -103,8 +102,9 @@ std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingStudio() { // |PlatformView| std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingSurface( int64_t view_id) { - return std::make_unique( - proc_table_, shell_test_external_view_embedder_, context_, memory_allocator_); + return std::make_unique(proc_table_, + shell_test_external_view_embedder_, + context_, memory_allocator_); } // |PlatformView| diff --git a/shell/common/shell_test_platform_view_vulkan.h b/shell/common/shell_test_platform_view_vulkan.h index bbb7e6929aeb7..6d6f1dc540971 100644 --- a/shell/common/shell_test_platform_view_vulkan.h +++ b/shell/common/shell_test_platform_view_vulkan.h @@ -31,12 +31,11 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { private: class OffScreenSurface : public flutter::Surface { public: - OffScreenSurface( - fml::RefPtr vk, - std::shared_ptr - shell_test_external_view_embedder, - sk_sp context, - sk_sp memory_allocator); + OffScreenSurface(fml::RefPtr vk, + std::shared_ptr + shell_test_external_view_embedder, + sk_sp context, + sk_sp memory_allocator); ~OffScreenSurface() override; From cd14ba1a0744c9e28ae5cf449ae6853904d0fefb Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 21 Mar 2023 14:50:18 -0700 Subject: [PATCH 042/147] Fix a few tests --- shell/gpu/gpu_studio_metal_skia.mm | 2 +- shell/gpu/gpu_surface_metal_skia.mm | 2 +- .../embedder/embedder_surface_metal.mm | 2 +- shell/testing/tester_main.cc | 18 ++++++++++++++++-- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/shell/gpu/gpu_studio_metal_skia.mm b/shell/gpu/gpu_studio_metal_skia.mm index 58d9866d5997f..dc691fd8f62eb 100644 --- a/shell/gpu/gpu_studio_metal_skia.mm +++ b/shell/gpu/gpu_studio_metal_skia.mm @@ -31,7 +31,7 @@ GPUSurfaceMetalDelegate* delegate, sk_sp context, std::shared_ptr sksl_precompiler) - : delegate_(delegate), context_(std::move(context)), sksl_precompiler_(sksl_precompiler) {} + : delegate_(delegate), context_(std::move(context)), sksl_precompiler_(std::move(sksl_precompiler)) {} GPUStudioMetalSkia::~GPUStudioMetalSkia() = default; diff --git a/shell/gpu/gpu_surface_metal_skia.mm b/shell/gpu/gpu_surface_metal_skia.mm index 7356684d4f4c6..31a60f565b11b 100644 --- a/shell/gpu/gpu_surface_metal_skia.mm +++ b/shell/gpu/gpu_surface_metal_skia.mm @@ -59,7 +59,7 @@ render_target_type_(delegate->GetRenderTargetType()), context_(std::move(context)), msaa_samples_(msaa_samples), - sksl_precompiler_(sksl_precompiler), + sksl_precompiler_(std::move(sksl_precompiler)), render_to_surface_(render_to_surface) { // If this preference is explicitly set, we allow for disabling partial repaint. NSNumber* disablePartialRepaint = diff --git a/shell/platform/embedder/embedder_surface_metal.mm b/shell/platform/embedder/embedder_surface_metal.mm index 1f237eb5d3213..32b9a34409c4d 100644 --- a/shell/platform/embedder/embedder_surface_metal.mm +++ b/shell/platform/embedder/embedder_surface_metal.mm @@ -22,7 +22,7 @@ bool render_to_surface) : main_context_(std::move(main_context)), studio_(studio), - sksl_precompiler_(sksl_precompiler), + sksl_precompiler_(std::move(sksl_precompiler)), render_to_surface_(render_to_surface) {} EmbedderSurfaceMetal::~EmbedderSurfaceMetal() = default; diff --git a/shell/testing/tester_main.cc b/shell/testing/tester_main.cc index c2e470e66ad64..3e9f3d68beed2 100644 --- a/shell/testing/tester_main.cc +++ b/shell/testing/tester_main.cc @@ -24,6 +24,7 @@ #include "flutter/shell/common/switches.h" #include "flutter/shell/common/thread_host.h" #include "flutter/shell/gpu/gpu_surface_software.h" +#include "flutter/shell/gpu/gpu_studio_software.h" #include "third_party/dart/runtime/include/bin/dart_io_api.h" #include "third_party/dart/runtime/include/dart_api.h" @@ -67,13 +68,19 @@ class TesterExternalViewEmbedder : public ExternalViewEmbedder { DisplayListBuilder builder_; }; +class TesterGPUStudioSoftware : public GPUStudioSoftware { + public: + TesterGPUStudioSoftware(GPUSurfaceSoftwareDelegate* delegate) + : GPUStudioSoftware(delegate) {} + + bool EnableRasterCache() const override { return false; } +}; + class TesterGPUSurfaceSoftware : public GPUSurfaceSoftware { public: TesterGPUSurfaceSoftware(GPUSurfaceSoftwareDelegate* delegate, bool render_to_surface) : GPUSurfaceSoftware(delegate, render_to_surface) {} - - bool EnableRasterCache() const override { return false; } }; class TesterPlatformView : public PlatformView, @@ -82,6 +89,13 @@ class TesterPlatformView : public PlatformView, TesterPlatformView(Delegate& delegate, const TaskRunners& task_runners) : PlatformView(delegate, task_runners) {} + // |PlatformView| + std::unique_ptr CreateRenderingStudio() override { + auto studio = std::make_unique(this); + FML_DCHECK(studio->IsValid()); + return studio; + } + // |PlatformView| std::unique_ptr CreateRenderingSurface(int64_t view_id) override { auto surface = std::make_unique( From 283a451e1b737de943ea8a599967e99d58cbc746 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 21 Mar 2023 14:54:30 -0700 Subject: [PATCH 043/147] Format --- shell/gpu/gpu_studio_metal_skia.mm | 4 +++- shell/testing/tester_main.cc | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/shell/gpu/gpu_studio_metal_skia.mm b/shell/gpu/gpu_studio_metal_skia.mm index dc691fd8f62eb..61eaa1ebcbce9 100644 --- a/shell/gpu/gpu_studio_metal_skia.mm +++ b/shell/gpu/gpu_studio_metal_skia.mm @@ -31,7 +31,9 @@ GPUSurfaceMetalDelegate* delegate, sk_sp context, std::shared_ptr sksl_precompiler) - : delegate_(delegate), context_(std::move(context)), sksl_precompiler_(std::move(sksl_precompiler)) {} + : delegate_(delegate), + context_(std::move(context)), + sksl_precompiler_(std::move(sksl_precompiler)) {} GPUStudioMetalSkia::~GPUStudioMetalSkia() = default; diff --git a/shell/testing/tester_main.cc b/shell/testing/tester_main.cc index 3e9f3d68beed2..fff20be376814 100644 --- a/shell/testing/tester_main.cc +++ b/shell/testing/tester_main.cc @@ -23,8 +23,8 @@ #include "flutter/shell/common/shell.h" #include "flutter/shell/common/switches.h" #include "flutter/shell/common/thread_host.h" -#include "flutter/shell/gpu/gpu_surface_software.h" #include "flutter/shell/gpu/gpu_studio_software.h" +#include "flutter/shell/gpu/gpu_surface_software.h" #include "third_party/dart/runtime/include/bin/dart_io_api.h" #include "third_party/dart/runtime/include/dart_api.h" From 8e6ef11206b0288c0c70e24d16b51de9de24b5f8 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 21 Mar 2023 15:01:21 -0700 Subject: [PATCH 044/147] Fix fuchsia test --- shell/platform/fuchsia/flutter/engine.cc | 2 +- .../fuchsia/flutter/flatland_platform_view.h | 1 + .../fuchsia/flutter/platform_view_unittest.cc | 12 ++++++++++++ .../flutter/tests/flatland_platform_view_unittest.cc | 12 ++++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/shell/platform/fuchsia/flutter/engine.cc b/shell/platform/fuchsia/flutter/engine.cc index 93695ccd6eef8..b32bedc40897a 100644 --- a/shell/platform/fuchsia/flutter/engine.cc +++ b/shell/platform/fuchsia/flutter/engine.cc @@ -366,7 +366,7 @@ void Engine::Initialize( std::bind(&Engine::DestroyFlatlandView, this, std::placeholders::_1, std::placeholders::_2); - OnCreateSurface on_create_studio_callback = + OnCreateStudio on_create_studio_callback = std::bind(&Engine::CreateStudio, this); OnCreateSurface on_create_surface_callback = diff --git a/shell/platform/fuchsia/flutter/flatland_platform_view.h b/shell/platform/fuchsia/flutter/flatland_platform_view.h index afe0819842cd9..2315c2674e028 100644 --- a/shell/platform/fuchsia/flutter/flatland_platform_view.h +++ b/shell/platform/fuchsia/flutter/flatland_platform_view.h @@ -40,6 +40,7 @@ class FlatlandPlatformView final : public flutter_runner::PlatformView { OnCreateFlatlandView on_create_view_callback, OnUpdateView on_update_view_callback, OnDestroyFlatlandView on_destroy_view_callback, + OnCreateStudio on_create_studio_callback, OnCreateSurface on_create_surface_callback, OnSemanticsNodeUpdate on_semantics_node_update_callback, OnRequestAnnounce on_request_announce_callback, diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index 64902116300ac..be9bac1005a73 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -31,6 +31,7 @@ #include "gtest/gtest.h" #include "platform/assert.h" +#include "studio.h" #include "surface.h" #include "task_runner_adapter.h" #include "tests/fakes/focuser.h" @@ -296,6 +297,11 @@ class PlatformViewBuilder { return *this; } + PlatformViewBuilder& SetCreateStudioCallback(OnCreateStudio callback) { + on_create_studio_callback_ = std::move(callback); + return *this; + } + PlatformViewBuilder& SetCreateSurfaceCallback(OnCreateSurface callback) { on_create_surface_callback_ = std::move(callback); return *this; @@ -322,6 +328,7 @@ class PlatformViewBuilder { std::move(on_create_view_callback_), std::move(on_update_view_callback_), std::move(on_destroy_view_callback_), + std::move(on_create_studio_callback_), std::move(on_create_surface_callback_), std::move(on_semantics_node_update_callback_), std::move(on_request_announce_callback_), @@ -351,6 +358,7 @@ class PlatformViewBuilder { OnCreateGfxView on_create_view_callback_; OnUpdateView on_update_view_callback_; OnDestroyGfxView on_destroy_view_callback_; + OnCreateStudio on_create_studio_callback_; OnCreateSurface on_create_surface_callback_; OnSemanticsNodeUpdate on_semantics_node_update_callback_; OnRequestAnnounce on_request_announce_callback_; @@ -492,6 +500,9 @@ TEST_F(PlatformViewTests, CreateSurfaceTest) { flutter::MakeDefaultContextOptions(flutter::ContextType::kRender)); std::shared_ptr external_view_embedder = std::make_shared(); + auto CreateStudioCallback = [gr_context]() { + return std::make_unique(gr_context.get()); + }; auto CreateSurfaceCallback = [&external_view_embedder, gr_context]() { return std::make_unique( "PlatformViewTest", external_view_embedder, gr_context.get()); @@ -499,6 +510,7 @@ TEST_F(PlatformViewTests, CreateSurfaceTest) { flutter_runner::GfxPlatformView platform_view = PlatformViewBuilder(delegate, std::move(task_runners)) + .SetCreateStudioCallback(CreateStudioCallback) .SetCreateSurfaceCallback(CreateSurfaceCallback) .SetExternalViewEmbedder(external_view_embedder) .Build(); diff --git a/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc b/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc index 262ab27af1700..cc4ba273a6136 100644 --- a/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc @@ -33,6 +33,7 @@ #include "fakes/platform_message.h" #include "fakes/touch_source.h" #include "fakes/view_ref_focused.h" +#include "flutter/shell/platform/fuchsia/flutter/studio.h" #include "flutter/shell/platform/fuchsia/flutter/surface.h" #include "flutter/shell/platform/fuchsia/flutter/task_runner_adapter.h" #include "platform/assert.h" @@ -402,6 +403,11 @@ class PlatformViewBuilder { return *this; } + PlatformViewBuilder& SetCreateStudioCallback(OnCreateStudio callback) { + on_create_studio_callback_ = std::move(callback); + return *this; + } + PlatformViewBuilder& SetCreateSurfaceCallback(OnCreateSurface callback) { on_create_surface_callback_ = std::move(callback); return *this; @@ -427,6 +433,7 @@ class PlatformViewBuilder { std::move(on_create_view_callback_), std::move(on_update_view_callback_), std::move(on_destroy_view_callback_), + std::move(on_create_studio_callback_), std::move(on_create_surface_callback_), std::move(on_semantics_node_update_callback_), std::move(on_request_announce_callback_), @@ -456,6 +463,7 @@ class PlatformViewBuilder { OnCreateFlatlandView on_create_view_callback_; OnDestroyFlatlandView on_destroy_view_callback_; OnUpdateView on_update_view_callback_; + OnCreateStudio on_create_studio_callback_; OnCreateSurface on_create_surface_callback_; OnSemanticsNodeUpdate on_semantics_node_update_callback_; OnRequestAnnounce on_request_announce_callback_; @@ -611,12 +619,16 @@ TEST_F(FlatlandPlatformViewTests, CreateSurfaceTest) { flutter::MakeDefaultContextOptions(flutter::ContextType::kRender)); std::shared_ptr external_view_embedder = std::make_shared(); + auto CreateStudioCallback = [gr_context]() { + return std::make_unique(gr_context.get()); + }; auto CreateSurfaceCallback = [&external_view_embedder, gr_context]() { return std::make_unique( "PlatformViewTest", external_view_embedder, gr_context.get()); }; auto platform_view = PlatformViewBuilder(delegate, std::move(task_runners)) + .SetCreateStudioCallback(CreateStudioCallback) .SetCreateSurfaceCallback(CreateSurfaceCallback) .SetExternalViewEmbedder(external_view_embedder) .Build(); From 6c444b7d75ce319f40b07a2be3113929a5fe4d60 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 22 Mar 2023 05:06:09 +0000 Subject: [PATCH 045/147] Fix ocmpile and lint --- .../fuchsia/flutter/tests/flatland_platform_view_unittest.cc | 2 +- shell/testing/tester_main.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc b/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc index cc4ba273a6136..9b4a8524ae038 100644 --- a/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc @@ -620,7 +620,7 @@ TEST_F(FlatlandPlatformViewTests, CreateSurfaceTest) { std::shared_ptr external_view_embedder = std::make_shared(); auto CreateStudioCallback = [gr_context]() { - return std::make_unique(gr_context.get()); + return std::make_unique(gr_context.get()); }; auto CreateSurfaceCallback = [&external_view_embedder, gr_context]() { return std::make_unique( diff --git a/shell/testing/tester_main.cc b/shell/testing/tester_main.cc index fff20be376814..d0a3711505b44 100644 --- a/shell/testing/tester_main.cc +++ b/shell/testing/tester_main.cc @@ -70,7 +70,7 @@ class TesterExternalViewEmbedder : public ExternalViewEmbedder { class TesterGPUStudioSoftware : public GPUStudioSoftware { public: - TesterGPUStudioSoftware(GPUSurfaceSoftwareDelegate* delegate) + explicit TesterGPUStudioSoftware(GPUSurfaceSoftwareDelegate* delegate) : GPUStudioSoftware(delegate) {} bool EnableRasterCache() const override { return false; } From 842a1bb45ee951fb13c31e17a445851d4fe3ae3d Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 22 Mar 2023 18:06:36 +0000 Subject: [PATCH 046/147] Fix lint --- shell/common/shell_test_platform_view_vulkan.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index 1c3cc22cc684e..04f55c245e9b9 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -189,8 +189,8 @@ ShellTestPlatformViewVulkan::OffScreenSurface::OffScreenSurface( : vk_(std::move(vk)), shell_test_external_view_embedder_( std::move(shell_test_external_view_embedder)), - context_(context), - memory_allocator_(memory_allocator) {} + context_(std::move(context)), + memory_allocator_(std::move(memory_allocator)) {} ShellTestPlatformViewVulkan::OffScreenSurface::~OffScreenSurface() {} From 99116ef60f326ee43710d839024cfe0f2260963d Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 22 Mar 2023 19:42:54 +0000 Subject: [PATCH 047/147] Fix GL bug --- shell/platform/embedder/embedder_studio_gl.cc | 14 +++++++++----- shell/platform/embedder/embedder_studio_gl.h | 2 ++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/shell/platform/embedder/embedder_studio_gl.cc b/shell/platform/embedder/embedder_studio_gl.cc index 29693949ad619..c749c31515e92 100644 --- a/shell/platform/embedder/embedder_studio_gl.cc +++ b/shell/platform/embedder/embedder_studio_gl.cc @@ -104,7 +104,7 @@ std::unique_ptr EmbedderStudioGL::CreateGPUStudio() { if (!IsValid()) { return nullptr; } - auto studio = std::make_unique(main_context_, this); + auto studio = std::make_unique(MainContext(), this); if (!studio->IsValid()) { return nullptr; } @@ -114,12 +114,9 @@ std::unique_ptr EmbedderStudioGL::CreateGPUStudio() { // |EmbedderStudio| std::unique_ptr EmbedderStudioGL::CreateSurface() { - if (!main_context_) { - main_context_ = GPUSurfaceGLSkia::MakeGLContext(this); - } const bool render_to_surface = !external_view_embedder_; return std::make_unique( - main_context_, + MainContext(), this, // GPU surface GL delegate render_to_surface // render to surface ); @@ -148,4 +145,11 @@ sk_sp EmbedderStudioGL::CreateResourceContext() const { return nullptr; } +sk_sp EmbedderStudioGL::MainContext() { + if (!main_context_) { + main_context_ = GPUSurfaceGLSkia::MakeGLContext(this); + } + return main_context_; +} + } // namespace flutter diff --git a/shell/platform/embedder/embedder_studio_gl.h b/shell/platform/embedder/embedder_studio_gl.h index 4813cb48b0e3f..74b0620b80776 100644 --- a/shell/platform/embedder/embedder_studio_gl.h +++ b/shell/platform/embedder/embedder_studio_gl.h @@ -78,6 +78,8 @@ class EmbedderStudioGL final : public EmbedderStudio, std::shared_ptr external_view_embedder_; + sk_sp MainContext(); + FML_DISALLOW_COPY_AND_ASSIGN(EmbedderStudioGL); }; From de857a7a69b2e3cbebc56594b848a3479f6c970d Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 22 Mar 2023 19:43:17 +0000 Subject: [PATCH 048/147] Fix order --- shell/common/shell_test_platform_view_gl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/common/shell_test_platform_view_gl.h b/shell/common/shell_test_platform_view_gl.h index eb2c1b4ef55ec..635bba96efa03 100644 --- a/shell/common/shell_test_platform_view_gl.h +++ b/shell/common/shell_test_platform_view_gl.h @@ -42,10 +42,10 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView, sk_sp main_context_; // |PlatformView| - std::unique_ptr CreateRenderingSurface(int64_t view_id) override; + std::unique_ptr CreateRenderingStudio() override; // |PlatformView| - std::unique_ptr CreateRenderingStudio() override; + std::unique_ptr CreateRenderingSurface(int64_t view_id) override; // |PlatformView| std::shared_ptr CreateExternalViewEmbedder() override; From 4fa18ccae71b7d42502487ea2eb5440b956d3e82 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 22 Mar 2023 21:57:30 +0000 Subject: [PATCH 049/147] Try to revert ShellTestPlatformViewVulkan --- .../common/shell_test_platform_view_vulkan.cc | 120 +++++++++--------- .../common/shell_test_platform_view_vulkan.h | 26 ++-- 2 files changed, 65 insertions(+), 81 deletions(-) diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index 04f55c245e9b9..ce40fcb496c1f 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -6,10 +6,9 @@ #include -// #include "flutter/common/graphics/persistent_cache.h" +#include "flutter/common/graphics/persistent_cache.h" #include "flutter/flutter_vma/flutter_skia_vma.h" #include "flutter/shell/common/context_options.h" -#include "flutter/shell/gpu/gpu_studio_vulkan.h" #include "flutter/vulkan/vulkan_skia_proc_table.h" #include "flutter/vulkan/vulkan_utilities.h" @@ -39,9 +38,52 @@ ShellTestPlatformViewVulkan::ShellTestPlatformViewVulkan( create_vsync_waiter_(std::move(create_vsync_waiter)), vsync_clock_(std::move(vsync_clock)), proc_table_(fml::MakeRefCounted(VULKAN_SO_PATH)), + shell_test_external_view_embedder_( + std::move(shell_test_external_view_embedder)) {} + +ShellTestPlatformViewVulkan::~ShellTestPlatformViewVulkan() = default; + +std::unique_ptr ShellTestPlatformViewVulkan::CreateVSyncWaiter() { + return create_vsync_waiter_(); +} + +void ShellTestPlatformViewVulkan::SimulateVSync() { + vsync_clock_->SimulateVSync(); +} + +// |PlatformView| +std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingSurface( + int64_t view_id) { + return std::make_unique(proc_table_, + shell_test_external_view_embedder_); +} + +// |PlatformView| +std::shared_ptr +ShellTestPlatformViewVulkan::CreateExternalViewEmbedder() { + return shell_test_external_view_embedder_; +} + +// |PlatformView| +PointerDataDispatcherMaker ShellTestPlatformViewVulkan::GetDispatcherMaker() { + return [](DefaultPointerDataDispatcher::Delegate& delegate) { + return std::make_unique(delegate); + }; +} + +// TODO(gw280): This code was forked from vulkan_window.cc specifically for +// shell_test. +// We need to merge this functionality back into //vulkan. +// https://github.com/flutter/flutter/issues/51132 +ShellTestPlatformViewVulkan::OffScreenSurface::OffScreenSurface( + fml::RefPtr vk, + std::shared_ptr + shell_test_external_view_embedder) + : valid_(false), + vk_(std::move(vk)), shell_test_external_view_embedder_( std::move(shell_test_external_view_embedder)) { - if (!proc_table_ || !proc_table_->HasAcquiredMandatoryProcAddresses()) { + if (!vk_ || !vk_->HasAcquiredMandatoryProcAddresses()) { FML_DLOG(ERROR) << "Proc table has not acquired mandatory proc addresses."; return; } @@ -52,10 +94,10 @@ ShellTestPlatformViewVulkan::ShellTestPlatformViewVulkan( }; application_ = std::make_unique( - *proc_table_, "FlutterTest", std::move(extensions), - VK_MAKE_VERSION(1, 0, 0), VK_MAKE_VERSION(1, 1, 0), true); + *vk_, "FlutterTest", std::move(extensions), VK_MAKE_VERSION(1, 0, 0), + VK_MAKE_VERSION(1, 1, 0), true); - if (!application_->IsValid() || !proc_table_->AreInstanceProcsSetup()) { + if (!application_->IsValid() || !vk_->AreInstanceProcsSetup()) { // Make certain the application instance was created and it set up the // instance proc table entries. FML_DLOG(ERROR) << "Instance proc addresses have not been set up."; @@ -63,10 +105,11 @@ ShellTestPlatformViewVulkan::ShellTestPlatformViewVulkan( } // Create the device. + logical_device_ = application_->AcquireFirstCompatibleLogicalDevice(); if (logical_device_ == nullptr || !logical_device_->IsValid() || - !proc_table_->AreDeviceProcsSetup()) { + !vk_->AreDeviceProcsSetup()) { // Make certain the device was created and it set up the device proc table // entries. FML_DLOG(ERROR) << "Device proc addresses have not been set up."; @@ -76,51 +119,18 @@ ShellTestPlatformViewVulkan::ShellTestPlatformViewVulkan( memory_allocator_ = FlutterSkiaVulkanMemoryAllocator::Make( application_->GetAPIVersion(), application_->GetInstance(), logical_device_->GetPhysicalDeviceHandle(), logical_device_->GetHandle(), - proc_table_, true); + vk_, true); // Create the Skia GrContext. if (!CreateSkiaGrContext()) { FML_DLOG(ERROR) << "Could not create Skia context."; + return; } -} - -ShellTestPlatformViewVulkan::~ShellTestPlatformViewVulkan() = default; -std::unique_ptr ShellTestPlatformViewVulkan::CreateVSyncWaiter() { - return create_vsync_waiter_(); + valid_ = true; } -void ShellTestPlatformViewVulkan::SimulateVSync() { - vsync_clock_->SimulateVSync(); -} - -// |PlatformView| -std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingStudio() { - return std::make_unique(context_); -} - -// |PlatformView| -std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingSurface( - int64_t view_id) { - return std::make_unique(proc_table_, - shell_test_external_view_embedder_, - context_, memory_allocator_); -} - -// |PlatformView| -std::shared_ptr -ShellTestPlatformViewVulkan::CreateExternalViewEmbedder() { - return shell_test_external_view_embedder_; -} - -// |PlatformView| -PointerDataDispatcherMaker ShellTestPlatformViewVulkan::GetDispatcherMaker() { - return [](DefaultPointerDataDispatcher::Delegate& delegate) { - return std::make_unique(delegate); - }; -} - -bool ShellTestPlatformViewVulkan::CreateSkiaGrContext() { +bool ShellTestPlatformViewVulkan::OffScreenSurface::CreateSkiaGrContext() { GrVkBackendContext backend_context; if (!CreateSkiaBackendContext(&backend_context)) { @@ -146,9 +156,9 @@ bool ShellTestPlatformViewVulkan::CreateSkiaGrContext() { return true; } -bool ShellTestPlatformViewVulkan::CreateSkiaBackendContext( +bool ShellTestPlatformViewVulkan::OffScreenSurface::CreateSkiaBackendContext( GrVkBackendContext* context) { - auto getProc = CreateSkiaGetProc(proc_table_); + auto getProc = CreateSkiaGetProc(vk_); if (getProc == nullptr) { FML_DLOG(ERROR) << "GetProcAddress is null"; @@ -176,26 +186,10 @@ bool ShellTestPlatformViewVulkan::CreateSkiaBackendContext( return true; } -// TODO(gw280): This code was forked from vulkan_window.cc specifically for -// shell_test. -// We need to merge this functionality back into //vulkan. -// https://github.com/flutter/flutter/issues/51132 -ShellTestPlatformViewVulkan::OffScreenSurface::OffScreenSurface( - fml::RefPtr vk, - std::shared_ptr - shell_test_external_view_embedder, - sk_sp context, - sk_sp memory_allocator) - : vk_(std::move(vk)), - shell_test_external_view_embedder_( - std::move(shell_test_external_view_embedder)), - context_(std::move(context)), - memory_allocator_(std::move(memory_allocator)) {} - ShellTestPlatformViewVulkan::OffScreenSurface::~OffScreenSurface() {} bool ShellTestPlatformViewVulkan::OffScreenSurface::IsValid() { - return true; + return valid_; } std::unique_ptr diff --git a/shell/common/shell_test_platform_view_vulkan.h b/shell/common/shell_test_platform_view_vulkan.h index 6d6f1dc540971..323112ceba3ad 100644 --- a/shell/common/shell_test_platform_view_vulkan.h +++ b/shell/common/shell_test_platform_view_vulkan.h @@ -33,9 +33,7 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { public: OffScreenSurface(fml::RefPtr vk, std::shared_ptr - shell_test_external_view_embedder, - sk_sp context, - sk_sp memory_allocator); + shell_test_external_view_embedder); ~OffScreenSurface() override; @@ -51,11 +49,17 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { GrDirectContext* GetContext() override; private: + bool valid_; fml::RefPtr vk_; std::shared_ptr shell_test_external_view_embedder_; - sk_sp context_; + std::unique_ptr application_; + std::unique_ptr logical_device_; sk_sp memory_allocator_; + sk_sp context_; + + bool CreateSkiaGrContext(); + bool CreateSkiaBackendContext(GrVkBackendContext* context); FML_DISALLOW_COPY_AND_ASSIGN(OffScreenSurface); }; @@ -69,17 +73,6 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { std::shared_ptr shell_test_external_view_embedder_; - sk_sp context_; - - std::unique_ptr application_; - - std::unique_ptr logical_device_; - - sk_sp memory_allocator_; - - // |PlatformView| - std::unique_ptr CreateRenderingStudio() override; - // |PlatformView| std::unique_ptr CreateRenderingSurface(int64_t view_id) override; @@ -92,9 +85,6 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { // |PlatformView| PointerDataDispatcherMaker GetDispatcherMaker() override; - bool CreateSkiaGrContext(); - bool CreateSkiaBackendContext(GrVkBackendContext* context); - FML_DISALLOW_COPY_AND_ASSIGN(ShellTestPlatformViewVulkan); }; From 8048a3a1515f25d6c1d50598e0e82e2d1bfe5c27 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 23 Mar 2023 07:33:23 +0000 Subject: [PATCH 050/147] OffScreenContext --- .../common/shell_test_platform_view_vulkan.cc | 50 +++++++++------ .../common/shell_test_platform_view_vulkan.h | 64 ++++++++++++++++--- 2 files changed, 86 insertions(+), 28 deletions(-) diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index ce40fcb496c1f..3c7662fda42ca 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -9,6 +9,7 @@ #include "flutter/common/graphics/persistent_cache.h" #include "flutter/flutter_vma/flutter_skia_vma.h" #include "flutter/shell/common/context_options.h" +#include "flutter/shell/gpu/gpu_studio_vulkan.h" #include "flutter/vulkan/vulkan_skia_proc_table.h" #include "flutter/vulkan/vulkan_utilities.h" @@ -51,10 +52,21 @@ void ShellTestPlatformViewVulkan::SimulateVSync() { vsync_clock_->SimulateVSync(); } +// |PlatformView| +std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingStudio() { + if (!offscreen_context_) { + offscreen_context_ = std::unique_ptr(); + } + return std::make_unique(offscreen_context_); +} + // |PlatformView| std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingSurface( int64_t view_id) { - return std::make_unique(proc_table_, + if (!offscreen_context_) { + offscreen_context_ = std::unique_ptr(); + } + return std::make_unique(offscreen_context_, shell_test_external_view_embedder_); } @@ -75,14 +87,9 @@ PointerDataDispatcherMaker ShellTestPlatformViewVulkan::GetDispatcherMaker() { // shell_test. // We need to merge this functionality back into //vulkan. // https://github.com/flutter/flutter/issues/51132 -ShellTestPlatformViewVulkan::OffScreenSurface::OffScreenSurface( - fml::RefPtr vk, - std::shared_ptr - shell_test_external_view_embedder) - : valid_(false), - vk_(std::move(vk)), - shell_test_external_view_embedder_( - std::move(shell_test_external_view_embedder)) { +ShellTestPlatformViewVulkan::OffScreenContext::OffScreenContext( + fml::RefPtr vk) + : vk_(std::move(vk)) { if (!vk_ || !vk_->HasAcquiredMandatoryProcAddresses()) { FML_DLOG(ERROR) << "Proc table has not acquired mandatory proc addresses."; return; @@ -124,13 +131,12 @@ ShellTestPlatformViewVulkan::OffScreenSurface::OffScreenSurface( // Create the Skia GrContext. if (!CreateSkiaGrContext()) { FML_DLOG(ERROR) << "Could not create Skia context."; - return; } - - valid_ = true; } -bool ShellTestPlatformViewVulkan::OffScreenSurface::CreateSkiaGrContext() { +ShellTestPlatformViewVulkan::OffScreenContext::~OffScreenContext() {} + +bool ShellTestPlatformViewVulkan::OffScreenContext::CreateSkiaGrContext() { GrVkBackendContext backend_context; if (!CreateSkiaBackendContext(&backend_context)) { @@ -156,7 +162,7 @@ bool ShellTestPlatformViewVulkan::OffScreenSurface::CreateSkiaGrContext() { return true; } -bool ShellTestPlatformViewVulkan::OffScreenSurface::CreateSkiaBackendContext( +bool ShellTestPlatformViewVulkan::OffScreenContext::CreateSkiaBackendContext( GrVkBackendContext* context) { auto getProc = CreateSkiaGetProc(vk_); @@ -186,10 +192,18 @@ bool ShellTestPlatformViewVulkan::OffScreenSurface::CreateSkiaBackendContext( return true; } +ShellTestPlatformViewVulkan::OffScreenSurface::OffScreenSurface( + const std::shared_ptr& offscreen_context, + std::shared_ptr + shell_test_external_view_embedder) + : shell_test_external_view_embedder_( + std::move(shell_test_external_view_embedder)), + offscreen_context_(offscreen_context) {} + ShellTestPlatformViewVulkan::OffScreenSurface::~OffScreenSurface() {} bool ShellTestPlatformViewVulkan::OffScreenSurface::IsValid() { - return valid_; + return GetContext(); } std::unique_ptr @@ -197,8 +211,8 @@ ShellTestPlatformViewVulkan::OffScreenSurface::AcquireFrame( const SkISize& size) { auto image_info = SkImageInfo::Make(size, SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kOpaque_SkAlphaType); - auto surface = SkSurface::MakeRenderTarget( - context_.get(), skgpu::Budgeted::kNo, image_info, 0, nullptr); + auto surface = SkSurface::MakeRenderTarget(GetContext(), skgpu::Budgeted::kNo, + image_info, 0, nullptr); SurfaceFrame::SubmitCallback callback = [](const SurfaceFrame&, DlCanvas* canvas) -> bool { canvas->Flush(); @@ -214,7 +228,7 @@ ShellTestPlatformViewVulkan::OffScreenSurface::AcquireFrame( } GrDirectContext* ShellTestPlatformViewVulkan::OffScreenSurface::GetContext() { - return context_.get(); + return offscreen_context_->GetContext().get(); } SkMatrix ShellTestPlatformViewVulkan::OffScreenSurface::GetRootTransformation() diff --git a/shell/common/shell_test_platform_view_vulkan.h b/shell/common/shell_test_platform_view_vulkan.h index 323112ceba3ad..ff144b3202fba 100644 --- a/shell/common/shell_test_platform_view_vulkan.h +++ b/shell/common/shell_test_platform_view_vulkan.h @@ -29,9 +29,55 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { void SimulateVSync() override; private: + class OffScreenContext { + public: + OffScreenContext(fml::RefPtr vk); + + ~OffScreenContext(); + + sk_sp GetContext() { return context_; } + + private: + fml::RefPtr vk_; + std::unique_ptr application_; + std::unique_ptr logical_device_; + sk_sp memory_allocator_; + sk_sp context_; + + bool CreateSkiaGrContext(); + bool CreateSkiaBackendContext(GrVkBackendContext* context); + + FML_DISALLOW_COPY_AND_ASSIGN(OffScreenContext); + }; + + class OffScreenStudio : public flutter::Studio { + public: + //------------------------------------------------------------------------------ + /// @brief Create a GPUStudioVulkan while letting it reuse an existing + /// GrDirectContext. + /// + OffScreenStudio(const std::shared_ptr& offscreen_context) + : offscreen_context_(offscreen_context) {} + + ~OffScreenStudio() override = default; + + // |Studio| + bool IsValid() override { return GetContext(); } + + // |Studio| + GrDirectContext* GetContext() override { + return offscreen_context_->GetContext().get(); + } + + private: + std::shared_ptr offscreen_context_; + + FML_DISALLOW_COPY_AND_ASSIGN(OffScreenStudio); + }; + class OffScreenSurface : public flutter::Surface { public: - OffScreenSurface(fml::RefPtr vk, + OffScreenSurface(const std::shared_ptr& offscreen_context, std::shared_ptr shell_test_external_view_embedder); @@ -43,23 +89,16 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { // |Surface| std::unique_ptr AcquireFrame(const SkISize& size) override; + // |Surface| SkMatrix GetRootTransformation() const override; // |Surface| GrDirectContext* GetContext() 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 memory_allocator_; - sk_sp context_; - - bool CreateSkiaGrContext(); - bool CreateSkiaBackendContext(GrVkBackendContext* context); + std::shared_ptr offscreen_context_; FML_DISALLOW_COPY_AND_ASSIGN(OffScreenSurface); }; @@ -73,6 +112,11 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { std::shared_ptr shell_test_external_view_embedder_; + std::shared_ptr offscreen_context_; + + // |PlatformView| + std::unique_ptr CreateRenderingStudio() override; + // |PlatformView| std::unique_ptr CreateRenderingSurface(int64_t view_id) override; From 2c9c7e826e2a1d9c9468b02a981515900e98b637 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 23 Mar 2023 07:48:20 +0000 Subject: [PATCH 051/147] Fix vulkan bug --- shell/common/shell_test_platform_view_vulkan.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index 3c7662fda42ca..72d8016b33afe 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -55,7 +55,7 @@ void ShellTestPlatformViewVulkan::SimulateVSync() { // |PlatformView| std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingStudio() { if (!offscreen_context_) { - offscreen_context_ = std::unique_ptr(); + offscreen_context_ = std::make_shared(proc_table_); } return std::make_unique(offscreen_context_); } @@ -64,7 +64,7 @@ std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingStudio() { std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingSurface( int64_t view_id) { if (!offscreen_context_) { - offscreen_context_ = std::unique_ptr(); + offscreen_context_ = std::make_shared(proc_table_); } return std::make_unique(offscreen_context_, shell_test_external_view_embedder_); From adf3515e45d375621553234d2273ee825af3e45d Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 23 Mar 2023 20:57:03 +0000 Subject: [PATCH 052/147] Remove unnecessary methods of Surface. --- flow/surface.cc | 20 -------------- flow/surface.h | 10 ------- shell/common/rasterizer.h | 4 +-- shell/common/snapshot_controller.h | 3 ++- shell/common/snapshot_controller_impeller.cc | 6 ++--- shell/common/snapshot_controller_skia.cc | 26 +++++++------------ shell/gpu/gpu_surface_gl_impeller.cc | 25 ------------------ shell/gpu/gpu_surface_gl_impeller.h | 15 ----------- shell/gpu/gpu_surface_gl_skia.cc | 15 ----------- shell/gpu/gpu_surface_gl_skia.h | 9 ------- shell/gpu/gpu_surface_vulkan_impeller.cc | 17 ------------ shell/gpu/gpu_surface_vulkan_impeller.h | 9 ------- .../external_view_embedder_unittests.cc | 5 ---- 13 files changed, 15 insertions(+), 149 deletions(-) diff --git a/flow/surface.cc b/flow/surface.cc index fbd3d427c2de2..4461ce3888860 100644 --- a/flow/surface.cc +++ b/flow/surface.cc @@ -10,26 +10,6 @@ Surface::Surface() = default; Surface::~Surface() = default; -std::unique_ptr Surface::MakeRenderContextCurrent() { - return std::make_unique(true); -} - -bool Surface::ClearRenderContext() { - return false; -} - -bool Surface::AllowsDrawingWhenGpuDisabled() const { - return true; -} - -bool Surface::EnableRasterCache() const { - return true; -} - -impeller::AiksContext* Surface::GetAiksContext() const { - return nullptr; -} - Surface::SurfaceData Surface::GetSurfaceData() const { return {}; } diff --git a/flow/surface.h b/flow/surface.h index 5cc5d6def08a1..e594d08a01232 100644 --- a/flow/surface.h +++ b/flow/surface.h @@ -41,16 +41,6 @@ class Surface { virtual GrDirectContext* GetContext() = 0; - virtual std::unique_ptr MakeRenderContextCurrent(); - - virtual bool ClearRenderContext(); - - virtual bool AllowsDrawingWhenGpuDisabled() const; - - virtual bool EnableRasterCache() const; - - virtual impeller::AiksContext* GetAiksContext() const; - /// Capture the `SurfaceData` currently present in the surface. /// /// Not guaranteed to work on all setups and not intended to be used in diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index bc12025d05820..58d38e55174b5 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -503,9 +503,7 @@ class Rasterizer final : public SnapshotDelegate, fml::Milliseconds GetFrameBudget() const override; // |SnapshotController::Delegate| - const std::unique_ptr& GetSurface() const override { - return surface_; - } + Studio* GetStudio() const override { return studio_.get(); } // |SnapshotController::Delegate| const std::unique_ptr& GetSnapshotSurfaceProducer() diff --git a/shell/common/snapshot_controller.h b/shell/common/snapshot_controller.h index 11651325edd43..3d44f3bf5bbf5 100644 --- a/shell/common/snapshot_controller.h +++ b/shell/common/snapshot_controller.h @@ -7,6 +7,7 @@ #include "flutter/common/settings.h" #include "flutter/display_list/image/dl_image.h" +#include "flutter/flow/studio.h" #include "flutter/flow/surface.h" #include "flutter/fml/synchronization/sync_switch.h" #include "flutter/lib/ui/snapshot_delegate.h" @@ -19,7 +20,7 @@ class SnapshotController { class Delegate { public: virtual ~Delegate() = default; - virtual const std::unique_ptr& GetSurface() const = 0; + virtual Studio* GetStudio() const = 0; virtual const std::unique_ptr& GetSnapshotSurfaceProducer() const = 0; virtual std::shared_ptr GetIsGpuDisabledSyncSwitch() diff --git a/shell/common/snapshot_controller_impeller.cc b/shell/common/snapshot_controller_impeller.cc index 5e7317805812d..f909b8acd3c1b 100644 --- a/shell/common/snapshot_controller_impeller.cc +++ b/shell/common/snapshot_controller_impeller.cc @@ -36,10 +36,10 @@ sk_sp SnapshotControllerImpeller::DoMakeRasterSnapshot( impeller::DisplayListDispatcher dispatcher; display_list->Dispatch(dispatcher); impeller::Picture picture = dispatcher.EndRecordingAsPicture(); - if (GetDelegate().GetSurface() && - GetDelegate().GetSurface()->GetAiksContext()) { + if (GetDelegate().GetStudio() && + GetDelegate().GetStudio()->GetAiksContext()) { impeller::AiksContext* context = - GetDelegate().GetSurface()->GetAiksContext(); + GetDelegate().GetStudio()->GetAiksContext(); auto max_size = context->GetContext() ->GetResourceAllocator() diff --git a/shell/common/snapshot_controller_skia.cc b/shell/common/snapshot_controller_skia.cc index 720854885eff0..fc63f76f84702 100644 --- a/shell/common/snapshot_controller_skia.cc +++ b/shell/common/snapshot_controller_skia.cc @@ -53,20 +53,13 @@ sk_sp SnapshotControllerSkia::DoMakeRasterSnapshot( SkImageInfo image_info = SkImageInfo::MakeN32Premul( size.width(), size.height(), SkColorSpace::MakeSRGB()); - std::unique_ptr pbuffer_surface; - Surface* snapshot_surface = nullptr; + Studio* snapshot_studio = nullptr; auto& delegate = GetDelegate(); - if (delegate.GetSurface() && delegate.GetSurface()->GetContext()) { - snapshot_surface = delegate.GetSurface().get(); - } else if (delegate.GetSnapshotSurfaceProducer()) { - pbuffer_surface = - delegate.GetSnapshotSurfaceProducer()->CreateSnapshotSurface(); - if (pbuffer_surface && pbuffer_surface->GetContext()) { - snapshot_surface = pbuffer_surface.get(); - } + if (delegate.GetStudio() && delegate.GetStudio()->GetContext()) { + snapshot_studio = delegate.GetStudio(); } - if (!snapshot_surface) { + if (!snapshot_studio) { // Raster surface is fine if there is no on screen surface. This might // happen in case of software rendering. sk_sp sk_surface = SkSurface::MakeRaster(image_info); @@ -79,14 +72,13 @@ sk_sp SnapshotControllerSkia::DoMakeRasterSnapshot( result = DrawSnapshot(surface, draw_callback); }) .SetIfFalse([&] { - FML_DCHECK(snapshot_surface); - auto context_switch = - snapshot_surface->MakeRenderContextCurrent(); + FML_DCHECK(snapshot_studio); + auto context_switch = snapshot_studio->MakeRenderContextCurrent(); if (!context_switch->GetResult()) { return; } - GrRecordingContext* context = snapshot_surface->GetContext(); + GrRecordingContext* context = snapshot_studio->GetContext(); auto max_size = context->maxRenderTargetSize(); double scale_factor = std::min( 1.0, static_cast(max_size) / @@ -136,8 +128,8 @@ sk_sp SnapshotControllerSkia::ConvertToRasterImage( // If the rasterizer does not have a surface with a GrContext, then it will // be unable to render a cross-context SkImage. The caller will need to // create the raster image on the IO thread. - if (GetDelegate().GetSurface() == nullptr || - GetDelegate().GetSurface()->GetContext() == nullptr) { + if (GetDelegate().GetStudio() == nullptr || + GetDelegate().GetStudio()->GetContext() == nullptr) { return nullptr; } diff --git a/shell/gpu/gpu_surface_gl_impeller.cc b/shell/gpu/gpu_surface_gl_impeller.cc index 2a7386613c781..c7e6a5e039c84 100644 --- a/shell/gpu/gpu_surface_gl_impeller.cc +++ b/shell/gpu/gpu_surface_gl_impeller.cc @@ -139,29 +139,4 @@ GrDirectContext* GPUSurfaceGLImpeller::GetContext() { return nullptr; } -// |Surface| -std::unique_ptr -GPUSurfaceGLImpeller::MakeRenderContextCurrent() { - return delegate_->GLContextMakeCurrent(); -} - -// |Surface| -bool GPUSurfaceGLImpeller::ClearRenderContext() { - return delegate_->GLContextClearCurrent(); -} - -bool GPUSurfaceGLImpeller::AllowsDrawingWhenGpuDisabled() const { - return delegate_->AllowsDrawingWhenGpuDisabled(); -} - -// |Surface| -bool GPUSurfaceGLImpeller::EnableRasterCache() const { - return false; -} - -// |Surface| -impeller::AiksContext* GPUSurfaceGLImpeller::GetAiksContext() const { - return aiks_context_.get(); -} - } // namespace flutter diff --git a/shell/gpu/gpu_surface_gl_impeller.h b/shell/gpu/gpu_surface_gl_impeller.h index 367bad23835ac..abfc5dfb32231 100644 --- a/shell/gpu/gpu_surface_gl_impeller.h +++ b/shell/gpu/gpu_surface_gl_impeller.h @@ -43,21 +43,6 @@ class GPUSurfaceGLImpeller final : public Surface { // |Surface| GrDirectContext* GetContext() override; - // |Surface| - std::unique_ptr MakeRenderContextCurrent() override; - - // |Surface| - bool ClearRenderContext() override; - - // |Surface| - bool AllowsDrawingWhenGpuDisabled() const override; - - // |Surface| - bool EnableRasterCache() const override; - - // |Surface| - impeller::AiksContext* GetAiksContext() const override; - FML_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceGLImpeller); }; diff --git a/shell/gpu/gpu_surface_gl_skia.cc b/shell/gpu/gpu_surface_gl_skia.cc index e5662366b1ea6..e4b81f6605641 100644 --- a/shell/gpu/gpu_surface_gl_skia.cc +++ b/shell/gpu/gpu_surface_gl_skia.cc @@ -321,19 +321,4 @@ GrDirectContext* GPUSurfaceGLSkia::GetContext() { return context_.get(); } -// |Surface| -std::unique_ptr GPUSurfaceGLSkia::MakeRenderContextCurrent() { - return delegate_->GLContextMakeCurrent(); -} - -// |Surface| -bool GPUSurfaceGLSkia::ClearRenderContext() { - return delegate_->GLContextClearCurrent(); -} - -// |Surface| -bool GPUSurfaceGLSkia::AllowsDrawingWhenGpuDisabled() const { - return delegate_->AllowsDrawingWhenGpuDisabled(); -} - } // namespace flutter diff --git a/shell/gpu/gpu_surface_gl_skia.h b/shell/gpu/gpu_surface_gl_skia.h index 1cbb4c50ecd29..f242f76f02662 100644 --- a/shell/gpu/gpu_surface_gl_skia.h +++ b/shell/gpu/gpu_surface_gl_skia.h @@ -43,15 +43,6 @@ class GPUSurfaceGLSkia : public Surface { // |Surface| GrDirectContext* GetContext() override; - // |Surface| - std::unique_ptr MakeRenderContextCurrent() override; - - // |Surface| - bool ClearRenderContext() override; - - // |Surface| - bool AllowsDrawingWhenGpuDisabled() const override; - private: bool CreateOrUpdateSurfaces(const SkISize& size); diff --git a/shell/gpu/gpu_surface_vulkan_impeller.cc b/shell/gpu/gpu_surface_vulkan_impeller.cc index 0e3854900a54f..a2dc1607b77da 100644 --- a/shell/gpu/gpu_surface_vulkan_impeller.cc +++ b/shell/gpu/gpu_surface_vulkan_impeller.cc @@ -109,21 +109,4 @@ GrDirectContext* GPUSurfaceVulkanImpeller::GetContext() { return nullptr; } -// |Surface| -std::unique_ptr -GPUSurfaceVulkanImpeller::MakeRenderContextCurrent() { - // This backend has no such concept. - return std::make_unique(true); -} - -// |Surface| -bool GPUSurfaceVulkanImpeller::EnableRasterCache() const { - return false; -} - -// |Surface| -impeller::AiksContext* GPUSurfaceVulkanImpeller::GetAiksContext() const { - return aiks_context_.get(); -} - } // namespace flutter diff --git a/shell/gpu/gpu_surface_vulkan_impeller.h b/shell/gpu/gpu_surface_vulkan_impeller.h index cf72b2e0e18c9..1514c1368c9fb 100644 --- a/shell/gpu/gpu_surface_vulkan_impeller.h +++ b/shell/gpu/gpu_surface_vulkan_impeller.h @@ -40,15 +40,6 @@ class GPUSurfaceVulkanImpeller final : public Surface { // |Surface| GrDirectContext* GetContext() override; - // |Surface| - std::unique_ptr MakeRenderContextCurrent() override; - - // |Surface| - bool EnableRasterCache() const override; - - // |Surface| - impeller::AiksContext* GetAiksContext() const override; - FML_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceVulkanImpeller); }; 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 b9c527b16c710..af933e4251f2d 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 @@ -57,11 +57,6 @@ class SurfaceMock : public Surface { MOCK_METHOD(SkMatrix, GetRootTransformation, (), (const, override)); MOCK_METHOD(GrDirectContext*, GetContext, (), (override)); - - MOCK_METHOD(std::unique_ptr, - MakeRenderContextCurrent, - (), - (override)); }; fml::RefPtr GetThreadMergerFromPlatformThread( From 457894735fd6b3b9c784a23607e6c1b6b311a916 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 23 Mar 2023 22:54:26 +0000 Subject: [PATCH 053/147] Compile --- shell/gpu/gpu_surface_metal_impeller.h | 12 ------------ shell/gpu/gpu_surface_metal_impeller.mm | 20 -------------------- shell/gpu/gpu_surface_metal_skia.h | 5 ----- shell/gpu/gpu_surface_metal_skia.mm | 14 -------------- 4 files changed, 51 deletions(-) diff --git a/shell/gpu/gpu_surface_metal_impeller.h b/shell/gpu/gpu_surface_metal_impeller.h index 6254d851a5062..41340873638bb 100644 --- a/shell/gpu/gpu_surface_metal_impeller.h +++ b/shell/gpu/gpu_surface_metal_impeller.h @@ -44,18 +44,6 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUSurfaceMetalImpeller : public Surface { // |Surface| GrDirectContext* GetContext() override; - // |Surface| - std::unique_ptr MakeRenderContextCurrent() override; - - // |Surface| - bool AllowsDrawingWhenGpuDisabled() const override; - - // |Surface| - bool EnableRasterCache() const override; - - // |Surface| - impeller::AiksContext* GetAiksContext() const override; - FML_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceMetalImpeller); }; diff --git a/shell/gpu/gpu_surface_metal_impeller.mm b/shell/gpu/gpu_surface_metal_impeller.mm index bad9f26ca780d..8e3056e390514 100644 --- a/shell/gpu/gpu_surface_metal_impeller.mm +++ b/shell/gpu/gpu_surface_metal_impeller.mm @@ -113,26 +113,6 @@ return nullptr; } -// |Surface| -std::unique_ptr GPUSurfaceMetalImpeller::MakeRenderContextCurrent() { - // This backend has no such concept. - return std::make_unique(true); -} - -bool GPUSurfaceMetalImpeller::AllowsDrawingWhenGpuDisabled() const { - return delegate_->AllowsDrawingWhenGpuDisabled(); -} - -// |Surface| -bool GPUSurfaceMetalImpeller::EnableRasterCache() const { - return false; -} - -// |Surface| -impeller::AiksContext* GPUSurfaceMetalImpeller::GetAiksContext() const { - return aiks_context_.get(); -} - Surface::SurfaceData GPUSurfaceMetalImpeller::GetSurfaceData() const { if (!(last_drawable_ && [last_drawable_ conformsToProtocol:@protocol(CAMetalDrawable)])) { return {}; diff --git a/shell/gpu/gpu_surface_metal_skia.h b/shell/gpu/gpu_surface_metal_skia.h index c04f3213d51d2..da6cdc0efa24d 100644 --- a/shell/gpu/gpu_surface_metal_skia.h +++ b/shell/gpu/gpu_surface_metal_skia.h @@ -55,11 +55,6 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUSurfaceMetalSkia : public Surface { GrDirectContext* GetContext() override; // |Surface| - std::unique_ptr MakeRenderContextCurrent() override; - - // |Surface| - bool AllowsDrawingWhenGpuDisabled() const override; - std::unique_ptr AcquireFrameFromCAMetalLayer( const SkISize& frame_info); diff --git a/shell/gpu/gpu_surface_metal_skia.mm b/shell/gpu/gpu_surface_metal_skia.mm index 31a60f565b11b..ca8507c5546e8 100644 --- a/shell/gpu/gpu_surface_metal_skia.mm +++ b/shell/gpu/gpu_surface_metal_skia.mm @@ -244,18 +244,4 @@ return context_.get(); } -// |Surface| -std::unique_ptr GPUSurfaceMetalSkia::MakeRenderContextCurrent() { - // A context may either be necessary to render to the surface or to snapshot an offscreen - // surface. Either way, SkSL precompilation must be attempted. - sksl_precompiler_->PrecompileKnownSkSLsIfNecessary(GetContext()); - - // This backend has no such concept. - return std::make_unique(true); -} - -bool GPUSurfaceMetalSkia::AllowsDrawingWhenGpuDisabled() const { - return delegate_->AllowsDrawingWhenGpuDisabled(); -} - } // namespace flutter From c94c37198432466126a0eb7decd0f685114c59ab Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 24 Mar 2023 01:25:33 +0000 Subject: [PATCH 054/147] Try to fix Android snapshot --- shell/common/snapshot_controller_skia.cc | 6 ++++++ shell/common/snapshot_surface_producer.h | 4 ++-- shell/platform/android/android_context_gl_unittests.cc | 4 ++-- shell/platform/android/android_surface_gl_impeller.cc | 2 +- shell/platform/android/android_surface_gl_impeller.h | 2 +- shell/platform/android/android_surface_gl_skia.cc | 4 ++-- shell/platform/android/android_surface_gl_skia.h | 2 +- shell/platform/android/surface/android_surface.cc | 2 +- shell/platform/android/surface/android_surface.h | 2 +- shell/platform/android/surface/snapshot_surface_producer.cc | 5 ++--- shell/platform/android/surface/snapshot_surface_producer.h | 3 +-- 11 files changed, 20 insertions(+), 16 deletions(-) diff --git a/shell/common/snapshot_controller_skia.cc b/shell/common/snapshot_controller_skia.cc index fc63f76f84702..e5edaf65324c8 100644 --- a/shell/common/snapshot_controller_skia.cc +++ b/shell/common/snapshot_controller_skia.cc @@ -57,6 +57,12 @@ sk_sp SnapshotControllerSkia::DoMakeRasterSnapshot( auto& delegate = GetDelegate(); if (delegate.GetStudio() && delegate.GetStudio()->GetContext()) { snapshot_studio = delegate.GetStudio(); + } else if (delegate.GetSnapshotSurfaceProducer()) { + std::unique_ptr pbuffer_studio = + delegate.GetSnapshotSurfaceProducer()->CreateSnapshotStudio(); + if (pbuffer_studio && pbuffer_studio->GetContext()) { + snapshot_studio = pbuffer_studio.get(); + } } if (!snapshot_studio) { diff --git a/shell/common/snapshot_surface_producer.h b/shell/common/snapshot_surface_producer.h index ae505109beda8..9a00259b6e89e 100644 --- a/shell/common/snapshot_surface_producer.h +++ b/shell/common/snapshot_surface_producer.h @@ -7,7 +7,7 @@ #include -#include "flutter/flow/surface.h" +#include "flutter/flow/studio.h" namespace flutter { @@ -15,7 +15,7 @@ class SnapshotSurfaceProducer { public: virtual ~SnapshotSurfaceProducer() = default; - virtual std::unique_ptr CreateSnapshotSurface() = 0; + virtual std::unique_ptr CreateSnapshotStudio() = 0; }; } // namespace flutter diff --git a/shell/platform/android/android_context_gl_unittests.cc b/shell/platform/android/android_context_gl_unittests.cc index bf1c7a78407ec..faa9ed4820605 100644 --- a/shell/platform/android/android_context_gl_unittests.cc +++ b/shell/platform/android/android_context_gl_unittests.cc @@ -136,7 +136,7 @@ TEST(AndroidSurfaceGL, CreateSnapshopSurfaceWhenOnscreenSurfaceIsNotNull) { android_surface->SetNativeWindow(window); auto onscreen_surface = android_surface->GetOnscreenSurface(); EXPECT_NE(onscreen_surface, nullptr); - android_surface->CreateSnapshotSurface(); + android_surface->CreateSnapshotStudio(); EXPECT_EQ(onscreen_surface, android_surface->GetOnscreenSurface()); } @@ -160,7 +160,7 @@ TEST(AndroidSurfaceGL, CreateSnapshopSurfaceWhenOnscreenSurfaceIsNull) { auto android_surface = std::make_unique(android_context, jni); EXPECT_EQ(android_surface->GetOnscreenSurface(), nullptr); - android_surface->CreateSnapshotSurface(); + android_surface->CreateSnapshotStudio(); EXPECT_NE(android_surface->GetOnscreenSurface(), nullptr); } diff --git a/shell/platform/android/android_surface_gl_impeller.cc b/shell/platform/android/android_surface_gl_impeller.cc index 8cf716c594af9..5c6e74113deb9 100644 --- a/shell/platform/android/android_surface_gl_impeller.cc +++ b/shell/platform/android/android_surface_gl_impeller.cc @@ -252,7 +252,7 @@ bool AndroidSurfaceGLImpeller::SetNativeWindow( } // |AndroidSurface| -std::unique_ptr AndroidSurfaceGLImpeller::CreateSnapshotSurface() { +std::unique_ptr AndroidSurfaceGLImpeller::CreateSnapshotStudio() { FML_UNREACHABLE(); } diff --git a/shell/platform/android/android_surface_gl_impeller.h b/shell/platform/android/android_surface_gl_impeller.h index d455242ff46c7..8164bf7416bda 100644 --- a/shell/platform/android/android_surface_gl_impeller.h +++ b/shell/platform/android/android_surface_gl_impeller.h @@ -50,7 +50,7 @@ class AndroidSurfaceGLImpeller final : public GPUSurfaceGLDelegate, bool SetNativeWindow(fml::RefPtr window) override; // |AndroidSurface| - std::unique_ptr CreateSnapshotSurface() override; + std::unique_ptr CreateSnapshotStudio() override; // |AndroidSurface| std::shared_ptr GetImpellerContext() override; diff --git a/shell/platform/android/android_surface_gl_skia.cc b/shell/platform/android/android_surface_gl_skia.cc index 5b094bef6f342..3fc3e60c1d6ea 100644 --- a/shell/platform/android/android_surface_gl_skia.cc +++ b/shell/platform/android/android_surface_gl_skia.cc @@ -227,7 +227,7 @@ AndroidContextGLSkia* AndroidSurfaceGLSkia::GLContextPtr() const { return reinterpret_cast(android_context_.get()); } -std::unique_ptr AndroidSurfaceGLSkia::CreateSnapshotSurface() { +std::unique_ptr AndroidSurfaceGLSkia::CreateSnapshotStudio() { if (!onscreen_surface_ || !onscreen_surface_->IsValid()) { onscreen_surface_ = GLContextPtr()->CreatePbufferSurface(); } @@ -238,7 +238,7 @@ std::unique_ptr AndroidSurfaceGLSkia::CreateSnapshotSurface() { GLContextPtr()->SetMainSkiaContext(main_skia_context); } - return std::make_unique(main_skia_context, this, true); + return std::make_unique(main_skia_context, this); } } // namespace flutter diff --git a/shell/platform/android/android_surface_gl_skia.h b/shell/platform/android/android_surface_gl_skia.h index 43b731a9829d6..d7e8a843cbf46 100644 --- a/shell/platform/android/android_surface_gl_skia.h +++ b/shell/platform/android/android_surface_gl_skia.h @@ -53,7 +53,7 @@ class AndroidSurfaceGLSkia final : public GPUSurfaceGLDelegate, bool SetNativeWindow(fml::RefPtr window) override; // |AndroidSurface| - virtual std::unique_ptr CreateSnapshotSurface() override; + virtual std::unique_ptr CreateSnapshotStudio() override; // |GPUSurfaceGLDelegate| std::unique_ptr GLContextMakeCurrent() override; diff --git a/shell/platform/android/surface/android_surface.cc b/shell/platform/android/surface/android_surface.cc index ab96387edbd77..6d158abf885f2 100644 --- a/shell/platform/android/surface/android_surface.cc +++ b/shell/platform/android/surface/android_surface.cc @@ -15,7 +15,7 @@ AndroidSurface::AndroidSurface( AndroidSurface::~AndroidSurface() = default; -std::unique_ptr AndroidSurface::CreateSnapshotSurface() { +std::unique_ptr AndroidSurface::CreateSnapshotStudio() { return nullptr; } diff --git a/shell/platform/android/surface/android_surface.h b/shell/platform/android/surface/android_surface.h index bc5414a1bd78e..8f69472c3fbde 100644 --- a/shell/platform/android/surface/android_surface.h +++ b/shell/platform/android/surface/android_surface.h @@ -45,7 +45,7 @@ class AndroidSurface { virtual bool SetNativeWindow(fml::RefPtr window) = 0; - virtual std::unique_ptr CreateSnapshotSurface(); + virtual std::unique_ptr CreateSnapshotStudio(); virtual std::shared_ptr GetImpellerContext(); diff --git a/shell/platform/android/surface/snapshot_surface_producer.cc b/shell/platform/android/surface/snapshot_surface_producer.cc index 3ca4c37f3ce6a..500db668e3570 100644 --- a/shell/platform/android/surface/snapshot_surface_producer.cc +++ b/shell/platform/android/surface/snapshot_surface_producer.cc @@ -10,9 +10,8 @@ AndroidSnapshotSurfaceProducer::AndroidSnapshotSurfaceProducer( AndroidSurface& android_surface) : android_surface_(android_surface) {} -std::unique_ptr -AndroidSnapshotSurfaceProducer::CreateSnapshotSurface() { - return android_surface_.CreateSnapshotSurface(); +std::unique_ptr AndroidSnapshotSurfaceProducer::CreateSnapshotStudio() { + return android_surface_.CreateSnapshotStudio(); } } // namespace flutter diff --git a/shell/platform/android/surface/snapshot_surface_producer.h b/shell/platform/android/surface/snapshot_surface_producer.h index 5ec8c68b2f457..4c7a9acee49d9 100644 --- a/shell/platform/android/surface/snapshot_surface_producer.h +++ b/shell/platform/android/surface/snapshot_surface_producer.h @@ -5,7 +5,6 @@ #ifndef SHELL_PLATFORM_ANDROID_SURFACE_SNAPSHOT_SURFACE_PRODUCER_H_ #define SHELL_PLATFORM_ANDROID_SURFACE_SNAPSHOT_SURFACE_PRODUCER_H_ -#include "flutter/flow/surface.h" #include "flutter/shell/common/snapshot_surface_producer.h" #include "flutter/shell/platform/android/surface/android_surface.h" @@ -16,7 +15,7 @@ class AndroidSnapshotSurfaceProducer : public SnapshotSurfaceProducer { explicit AndroidSnapshotSurfaceProducer(AndroidSurface& android_surface); // |SnapshotSurfaceProducer| - std::unique_ptr CreateSnapshotSurface() override; + std::unique_ptr CreateSnapshotStudio() override; private: AndroidSurface& android_surface_; From d00abbf189a43b231d32ae008c16c609e8fca3ab Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 24 Mar 2023 01:30:38 +0000 Subject: [PATCH 055/147] Fix compile --- shell/platform/android/android_surface_gl_impeller.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/android/android_surface_gl_impeller.cc b/shell/platform/android/android_surface_gl_impeller.cc index 5c6e74113deb9..0bd9d88bc1533 100644 --- a/shell/platform/android/android_surface_gl_impeller.cc +++ b/shell/platform/android/android_surface_gl_impeller.cc @@ -252,7 +252,7 @@ bool AndroidSurfaceGLImpeller::SetNativeWindow( } // |AndroidSurface| -std::unique_ptr AndroidSurfaceGLImpeller::CreateSnapshotStudio() { +std::unique_ptr AndroidSurfaceGLImpeller::CreateSnapshotStudio() { FML_UNREACHABLE(); } From 841ba568f7c5f111ae0c10283323fbfe23faea9e Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 24 Mar 2023 18:50:31 +0000 Subject: [PATCH 056/147] Try fix android snapshotting --- shell/common/snapshot_controller_skia.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/common/snapshot_controller_skia.cc b/shell/common/snapshot_controller_skia.cc index e5edaf65324c8..5af46b0dfddb7 100644 --- a/shell/common/snapshot_controller_skia.cc +++ b/shell/common/snapshot_controller_skia.cc @@ -53,12 +53,13 @@ sk_sp SnapshotControllerSkia::DoMakeRasterSnapshot( SkImageInfo image_info = SkImageInfo::MakeN32Premul( size.width(), size.height(), SkColorSpace::MakeSRGB()); + std::unique_ptr pbuffer_studio; Studio* snapshot_studio = nullptr; auto& delegate = GetDelegate(); if (delegate.GetStudio() && delegate.GetStudio()->GetContext()) { snapshot_studio = delegate.GetStudio(); } else if (delegate.GetSnapshotSurfaceProducer()) { - std::unique_ptr pbuffer_studio = + pbuffer_studio = delegate.GetSnapshotSurfaceProducer()->CreateSnapshotStudio(); if (pbuffer_studio && pbuffer_studio->GetContext()) { snapshot_studio = pbuffer_studio.get(); From e59b8b8fff161a5eb87870daf771c1bca88e0909 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 24 Mar 2023 20:23:15 +0000 Subject: [PATCH 057/147] Split Setup and RegisterSurface --- shell/common/rasterizer.cc | 11 ++++-- shell/common/rasterizer.h | 4 ++- shell/common/rasterizer_unittests.cc | 51 +++++++++++++++++++--------- shell/common/shell.cc | 4 ++- 4 files changed, 49 insertions(+), 21 deletions(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index ac79241ddb77f..dce2fe23e1728 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -51,10 +51,8 @@ fml::TaskRunnerAffineWeakPtr Rasterizer::GetSnapshotDelegate() return weak_factory_.GetWeakPtr(); } -void Rasterizer::Setup(std::unique_ptr studio, - std::unique_ptr surface) { +void Rasterizer::Setup(std::unique_ptr studio) { studio_ = std::move(studio); - surface_ = std::move(surface); if (max_cache_bytes_.has_value()) { SetResourceCacheMaxBytes(max_cache_bytes_.value(), @@ -146,6 +144,13 @@ void Rasterizer::NotifyLowMemoryWarning() const { context->performDeferredCleanup(std::chrono::milliseconds(0)); } +void Rasterizer::RegisterSurface(int64_t view_id, + std::unique_ptr surface) { + // TODO(dkwingsmt) + FML_DCHECK(view_id == 0ll); + surface_ = std::move(surface); +} + std::shared_ptr Rasterizer::GetTextureRegistry() { return compositor_context_->texture_registry(); } diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 58d38e55174b5..ab6c4841326bd 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -153,7 +153,9 @@ class Rasterizer final : public SnapshotDelegate, /// /// @param[in] surface The on-screen render surface. /// - void Setup(std::unique_ptr studio, std::unique_ptr surface); + void Setup(std::unique_ptr studio); + + void RegisterSurface(int64_t view_id, std::unique_ptr surface); //---------------------------------------------------------------------------- /// @brief Releases the previously set up on-screen render surface and diff --git a/shell/common/rasterizer_unittests.cc b/shell/common/rasterizer_unittests.cc index 8569c83e44cec..38764cc0467da 100644 --- a/shell/common/rasterizer_unittests.cc +++ b/shell/common/rasterizer_unittests.cc @@ -27,6 +27,9 @@ using testing::ReturnRef; namespace flutter { namespace { + +constexpr int64_t kDefaultViewId = 0; + class MockDelegate : public Rasterizer::Delegate { public: MOCK_METHOD1(OnFrameRasterized, void(const FrameTiming& frame_timing)); @@ -129,7 +132,8 @@ TEST(RasterizerTest, drawEmptyPipeline) { auto surface = std::make_unique>(); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - rasterizer->Setup(std::move(studio), std::move(surface)); + rasterizer->Setup(std::move(studio)); + rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -191,7 +195,8 @@ TEST(RasterizerTest, nullptr))) .Times(1); - rasterizer->Setup(std::move(studio), std::move(surface)); + rasterizer->Setup(std::move(studio)); + rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -257,7 +262,8 @@ TEST( /*raster_thread_merger=*/_)) .Times(1); - rasterizer->Setup(std::move(studio), std::move(surface)); + rasterizer->Setup(std::move(studio)); + rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -330,7 +336,8 @@ TEST( /*raster_thread_merger=*/_)) .Times(1); - rasterizer->Setup(std::move(studio), std::move(surface)); + rasterizer->Setup(std::move(studio)); + rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), @@ -405,7 +412,8 @@ TEST(RasterizerTest, /*raster_thread_merger=*/_)) .Times(2); - rasterizer->Setup(std::move(studio), std::move(surface)); + rasterizer->Setup(std::move(studio)); + rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), @@ -496,7 +504,8 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNotUsedThisFrame) { std::shared_ptr> external_view_embedder = std::make_shared>(); rasterizer->SetExternalViewEmbedder(external_view_embedder); - rasterizer->Setup(std::move(studio), std::move(surface)); + rasterizer->Setup(std::move(studio)); + rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); EXPECT_CALL(*external_view_embedder, BeginFrame(/*frame_size=*/SkISize(), /*context=*/nullptr, @@ -554,7 +563,8 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenPipelineIsEmpty) { std::shared_ptr> external_view_embedder = std::make_shared>(); rasterizer->SetExternalViewEmbedder(external_view_embedder); - rasterizer->Setup(std::move(studio), std::move(surface)); + rasterizer->Setup(std::move(studio)); + rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); EXPECT_CALL( *external_view_embedder, @@ -613,7 +623,8 @@ TEST(RasterizerTest, EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - rasterizer->Setup(std::move(studio), std::move(surface)); + rasterizer->Setup(std::move(studio)); + rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -671,7 +682,8 @@ TEST( EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - rasterizer->Setup(std::move(studio), std::move(surface)); + rasterizer->Setup(std::move(studio)); + rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -729,7 +741,8 @@ TEST( EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - rasterizer->Setup(std::move(studio), std::move(surface)); + rasterizer->Setup(std::move(studio)); + rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -786,7 +799,8 @@ TEST( EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - rasterizer->Setup(std::move(studio), std::move(surface)); + rasterizer->Setup(std::move(studio)); + rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -842,7 +856,8 @@ TEST( EXPECT_CALL(*surface, AcquireFrame(SkISize())); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - rasterizer->Setup(std::move(studio), std::move(surface)); + rasterizer->Setup(std::move(studio)); + rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -922,7 +937,8 @@ TEST(RasterizerTest, }); thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - rasterizer->Setup(std::move(studio), std::move(surface)); + rasterizer->Setup(std::move(studio)); + rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); for (int i = 0; i < 2; i++) { auto layer_tree = @@ -977,7 +993,8 @@ TEST(RasterizerTest, TeardownFreesResourceCache) { }); EXPECT_CALL(*studio, GetContext()).WillRepeatedly(Return(context.get())); - rasterizer->Setup(std::move(studio), std::move(surface)); + rasterizer->Setup(std::move(studio)); + rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); EXPECT_EQ(context->getResourceCacheLimit(), 0ul); rasterizer->SetResourceCacheMaxBytes(10000000, false); @@ -1095,7 +1112,8 @@ TEST(RasterizerTest, presentationTimeSetWhenVsyncTargetInFuture) { [] { return std::make_unique(true); })); thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - rasterizer->Setup(std::move(studio), std::move(surface)); + rasterizer->Setup(std::move(studio)); + rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); for (int i = 0; i < 2; i++) { auto layer_tree = @@ -1177,7 +1195,8 @@ TEST(RasterizerTest, presentationTimeNotSetWhenVsyncTargetInPast) { [] { return std::make_unique(true); })); thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - rasterizer->Setup(std::move(studio), std::move(surface)); + rasterizer->Setup(std::move(studio)); + rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), /*device_pixel_ratio=*/2.0f); diff --git a/shell/common/shell.cc b/shell/common/shell.cc index f92c8c2091f34..164c9b99e8187 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -792,7 +792,9 @@ void Shell::OnPlatformViewCreated() { // Enables the thread merger which may be used by the external view // embedder. rasterizer->EnableThreadMergerIfNeeded(); - rasterizer->Setup(std::move(studio), std::move(surface)); + rasterizer->Setup(std::move(studio)); + // TODO(dkwingsmt): Change 0ll + rasterizer->RegisterSurface(0ll, std::move(surface)); } waiting_for_first_frame.store(true); From a671009572550ee509ecf50efe3376b321b42457 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 24 Mar 2023 21:41:34 +0000 Subject: [PATCH 058/147] Replace GetLastLayerTree with Has*, add enable_leaf_layer_tracing to DrawLastLayerTree --- shell/common/rasterizer.cc | 15 +++++++++++---- shell/common/rasterizer.h | 5 +++-- shell/common/shell.cc | 7 +++---- shell/common/shell_unittests.cc | 2 +- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index dce2fe23e1728..5bb7f8f0cb501 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -159,15 +159,19 @@ GrDirectContext* Rasterizer::GetGrContext() { return studio_ ? studio_->GetContext() : nullptr; } -flutter::LayerTree* Rasterizer::GetLastLayerTree() { - return last_layer_tree_.get(); +bool Rasterizer::HasLastLayerTree() const { + return !!last_layer_tree_; } void Rasterizer::DrawLastLayerTree( - std::unique_ptr frame_timings_recorder) { + std::unique_ptr frame_timings_recorder, + bool enable_leaf_layer_tracing) { if (!last_layer_tree_ || !studio_) { return; } + if (enable_leaf_layer_tracing) { + last_layer_tree_->enable_leaf_layer_tracing(true); + } RasterStatus raster_status = DrawToSurface(*frame_timings_recorder, *last_layer_tree_); @@ -178,6 +182,9 @@ void Rasterizer::DrawLastLayerTree( external_view_embedder_->EndFrame(should_resubmit_frame, raster_thread_merger_); } + if (enable_leaf_layer_tracing) { + last_layer_tree_->enable_leaf_layer_tracing(false); + } } RasterStatus Rasterizer::Draw( @@ -724,7 +731,7 @@ sk_sp Rasterizer::ScreenshotLayerTreeAsImage( Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree( Rasterizer::ScreenshotType type, bool base64_encode) { - auto* layer_tree = GetLastLayerTree(); + auto* layer_tree = last_layer_tree_.get(); if (layer_tree == nullptr) { FML_LOG(ERROR) << "Last layer tree was null when screenshotting."; return {}; diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index ab6c4841326bd..7d401e259275b 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -203,7 +203,7 @@ class Rasterizer final : public SnapshotDelegate, /// @return A pointer to the last layer or `nullptr` if this rasterizer /// has never rendered a frame. /// - flutter::LayerTree* GetLastLayerTree(); + bool HasLastLayerTree() const; //---------------------------------------------------------------------------- /// @brief Draws a last layer tree to the render surface. This may seem @@ -219,7 +219,8 @@ class Rasterizer final : public SnapshotDelegate, /// to generate the layer tree describing the same contents. /// void DrawLastLayerTree( - std::unique_ptr frame_timings_recorder); + std::unique_ptr frame_timings_recorder, + bool enable_leaf_layer_tracing = false); // |SnapshotDelegate| GrDirectContext* GetGrContext() override; diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 164c9b99e8187..359d94a3d4723 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -1896,7 +1896,7 @@ bool Shell::OnServiceProtocolRenderFrameWithRasterStats( rapidjson::Document* response) { FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); - if (auto last_layer_tree = rasterizer_->GetLastLayerTree()) { + if (rasterizer_->HasLastLayerTree()) { auto& allocator = response->GetAllocator(); response->SetObject(); response->AddMember("type", "RenderFrameWithRasterStats", allocator); @@ -1910,9 +1910,8 @@ bool Shell::OnServiceProtocolRenderFrameWithRasterStats( frame_timings_recorder->RecordBuildStart(now); frame_timings_recorder->RecordBuildEnd(now); - last_layer_tree->enable_leaf_layer_tracing(true); - rasterizer_->DrawLastLayerTree(std::move(frame_timings_recorder)); - last_layer_tree->enable_leaf_layer_tracing(false); + rasterizer_->DrawLastLayerTree(std::move(frame_timings_recorder), + /* enable_leaf_layer_tracing=*/true); rapidjson::Value snapshots; snapshots.SetArray(); diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 99f5824b944fa..d36677bdf5a18 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -274,7 +274,7 @@ static bool RasterizerHasLayerTree(Shell* shell) { fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetRasterTaskRunner(), [shell, &latch, &has_layer_tree]() { - has_layer_tree = shell->GetRasterizer()->GetLastLayerTree() != nullptr; + has_layer_tree = shell->GetRasterizer()->HasLastLayerTree(); latch.Signal(); }); latch.Wait(); From bd58880c9cef4080aa93a5dede6f4aca9262ae95 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 24 Mar 2023 22:12:08 +0000 Subject: [PATCH 059/147] Better flow DrawLastLayerTree --- shell/common/rasterizer.cc | 12 +++++++----- shell/common/rasterizer.h | 12 ++++++------ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 5bb7f8f0cb501..6750456eb1312 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -169,11 +169,16 @@ void Rasterizer::DrawLastLayerTree( if (!last_layer_tree_ || !studio_) { return; } + // TODO(dkwingsmt): Apply to all last trees + flutter::LayerTree& layer_tree = *last_layer_tree_; if (enable_leaf_layer_tracing) { - last_layer_tree_->enable_leaf_layer_tracing(true); + layer_tree.enable_leaf_layer_tracing(true); } RasterStatus raster_status = - DrawToSurface(*frame_timings_recorder, *last_layer_tree_); + DrawToSurface(*frame_timings_recorder, layer_tree); + if (enable_leaf_layer_tracing) { + layer_tree.enable_leaf_layer_tracing(false); + } // EndFrame should perform cleanups for the external_view_embedder. if (external_view_embedder_ && external_view_embedder_->GetUsedThisFrame()) { @@ -182,9 +187,6 @@ void Rasterizer::DrawLastLayerTree( external_view_embedder_->EndFrame(should_resubmit_frame, raster_thread_merger_); } - if (enable_leaf_layer_tracing) { - last_layer_tree_->enable_leaf_layer_tracing(false); - } } RasterStatus Rasterizer::Draw( diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 7d401e259275b..92f8f999c65ce 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -206,12 +206,12 @@ class Rasterizer final : public SnapshotDelegate, bool HasLastLayerTree() const; //---------------------------------------------------------------------------- - /// @brief Draws a last layer tree to the render surface. This may seem - /// entirely redundant at first glance. After all, on surface loss - /// and re-acquisition, the framework generates a new layer tree. - /// Otherwise, why render the same contents to the screen again? - /// This is used as an optimization in cases where there are - /// external textures (video or camera streams for example) in + /// @brief Draws to all render surfaces their last layer trees. This may + /// seem entirely redundant at first glance. After all, on surface + /// loss and re-acquisition, the framework generates a new layer + /// tree. Otherwise, why render the same contents to the screen + /// again? This is used as an optimization in cases where there + /// are external textures (video or camera streams for example) in /// referenced in the layer tree. These textures may be updated at /// a cadence different from that of the Flutter application. /// Flutter can re-render the layer tree with just the updated From dc6be1f31d4a8553660d913c6da77983041e7847 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 24 Mar 2023 22:36:25 +0000 Subject: [PATCH 060/147] Remove resubmitted; replace with DoDrawResult --- shell/common/rasterizer.cc | 52 ++++++++++++++++++++++++-------------- shell/common/rasterizer.h | 17 ++++++++----- 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 6750456eb1312..c34c0e40cc12c 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -202,16 +202,17 @@ RasterStatus Rasterizer::Draw( .GetRasterTaskRunner() ->RunsTasksOnCurrentThread()); - RasterStatus raster_status = RasterStatus::kFailed; + DoDrawResult draw_result; LayerTreePipeline::Consumer consumer = - [&](std::unique_ptr item) { + [this, &draw_result, + &discard_callback](std::unique_ptr item) { std::shared_ptr layer_tree = std::move(item->layer_tree); std::unique_ptr frame_timings_recorder = std::move(item->frame_timings_recorder); if (discard_callback(*layer_tree.get())) { - raster_status = RasterStatus::kDiscarded; + draw_result.raster_status = RasterStatus::kDiscarded; } else { - raster_status = + draw_result = DoDraw(std::move(frame_timings_recorder), std::move(layer_tree)); } }; @@ -223,17 +224,18 @@ RasterStatus Rasterizer::Draw( // if the raster status is to resubmit the frame, we push the frame to the // front of the queue and also change the consume status to more available. - bool should_resubmit_frame = ShouldResubmitFrame(raster_status); + bool should_resubmit_frame = ShouldResubmitFrame(draw_result.raster_status); if (should_resubmit_frame) { auto resubmitted_layer_tree_item = std::make_unique( - std::move(resubmitted_layer_tree_), std::move(resubmitted_recorder_)); + std::move(draw_result.resubmitted_layer_tree), + std::move(draw_result.resubmitted_recorder)); auto front_continuation = pipeline->ProduceIfEmpty(); - PipelineProduceResult result = + PipelineProduceResult pipeline_result = front_continuation.Complete(std::move(resubmitted_layer_tree_item)); - if (result.success) { + if (pipeline_result.success) { consume_result = PipelineConsumeResult::MoreAvailable; } - } else if (raster_status == RasterStatus::kEnqueuePipeline) { + } else if (draw_result.raster_status == RasterStatus::kEnqueuePipeline) { consume_result = PipelineConsumeResult::MoreAvailable; } @@ -262,7 +264,7 @@ RasterStatus Rasterizer::Draw( break; } - return raster_status; + return draw_result.raster_status; } bool Rasterizer::ShouldResubmitFrame(const RasterStatus& raster_status) { @@ -383,7 +385,7 @@ fml::Milliseconds Rasterizer::GetFrameBudget() const { return delegate_.GetFrameBudget(); }; -RasterStatus Rasterizer::DoDraw( +Rasterizer::DoDrawResult Rasterizer::DoDraw( std::unique_ptr frame_timings_recorder, std::shared_ptr layer_tree) { TRACE_EVENT_WITH_FRAME_NUMBER(frame_timings_recorder, "flutter", @@ -393,7 +395,9 @@ RasterStatus Rasterizer::DoDraw( ->RunsTasksOnCurrentThread()); if (!layer_tree || !surface_) { - return RasterStatus::kFailed; + return DoDrawResult{ + .raster_status = RasterStatus::kFailed, + }; } PersistentCache* persistent_cache = PersistentCache::GetCacheForProcess(); @@ -404,12 +408,16 @@ RasterStatus Rasterizer::DoDraw( if (raster_status == RasterStatus::kSuccess) { last_layer_tree_ = std::move(layer_tree); } else if (ShouldResubmitFrame(raster_status)) { - resubmitted_layer_tree_ = std::move(layer_tree); - resubmitted_recorder_ = frame_timings_recorder->CloneUntil( - FrameTimingsRecorder::State::kBuildEnd); - return raster_status; + return DoDrawResult{ + .raster_status = raster_status, + .resubmitted_layer_tree = std::move(layer_tree), + .resubmitted_recorder = frame_timings_recorder->CloneUntil( + FrameTimingsRecorder::State::kBuildEnd), + }; } else if (raster_status == RasterStatus::kDiscarded) { - return raster_status; + return DoDrawResult{ + .raster_status = raster_status, + }; } if (persistent_cache->IsDumpingSkp() && @@ -477,11 +485,15 @@ RasterStatus Rasterizer::DoDraw( if (raster_thread_merger_) { if (raster_thread_merger_->DecrementLease() == fml::RasterThreadStatus::kUnmergedNow) { - return RasterStatus::kEnqueuePipeline; + return DoDrawResult{ + .raster_status = RasterStatus::kEnqueuePipeline, + }; } } - return raster_status; + return DoDrawResult{ + .raster_status = raster_status, + }; } RasterStatus Rasterizer::DrawToSurface( @@ -733,6 +745,8 @@ sk_sp Rasterizer::ScreenshotLayerTreeAsImage( Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree( Rasterizer::ScreenshotType type, bool base64_encode) { + // TODO(dkwingsmt): Probably screenshot all layer trees instead of just + // the first one and put them together. auto* layer_tree = last_layer_tree_.get(); if (layer_tree == nullptr) { FML_LOG(ERROR) << "Last layer tree was null when screenshotting."; diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 92f8f999c65ce..83bd020883339 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -487,6 +487,16 @@ class Rasterizer final : public SnapshotDelegate, void DisableThreadMergerIfNeeded(); private: + struct DoDrawResult { + RasterStatus raster_status = RasterStatus::kFailed; + // Set when we need attempt to rasterize the layer tree again. This + // layer_tree has not successfully rasterized. This can happen due to the + // change in the thread configuration. This will be inserted to the front of + // the pipeline. + std::shared_ptr resubmitted_layer_tree; + std::unique_ptr resubmitted_recorder; + }; + // |SnapshotDelegate| std::unique_ptr MakeSkiaGpuImage( sk_sp display_list, @@ -526,7 +536,7 @@ class Rasterizer final : public SnapshotDelegate, GrDirectContext* surface_context, bool compressed); - RasterStatus DoDraw( + DoDrawResult DoDraw( std::unique_ptr frame_timings_recorder, std::shared_ptr layer_tree); @@ -549,11 +559,6 @@ class Rasterizer final : public SnapshotDelegate, std::unique_ptr compositor_context_; // This is the last successfully rasterized layer tree. std::shared_ptr last_layer_tree_; - // Set when we need attempt to rasterize the layer tree again. This layer_tree - // has not successfully rasterized. This can happen due to the change in the - // thread configuration. This will be inserted to the front of the pipeline. - std::shared_ptr resubmitted_layer_tree_; - std::unique_ptr resubmitted_recorder_; fml::closure next_frame_callback_; bool user_override_resource_cache_bytes_; std::optional max_cache_bytes_; From 0bd3a34ad2ddd1cc3134b24fe1ca0c7b12dec264 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Sat, 25 Mar 2023 21:08:56 +0000 Subject: [PATCH 061/147] Rasterizer has multiple surfaces --- shell/common/rasterizer.cc | 136 ++++++++++++++++++++++++------------- shell/common/rasterizer.h | 71 +++++++++++++------ shell/common/shell.cc | 27 ++++---- 3 files changed, 154 insertions(+), 80 deletions(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index c34c0e40cc12c..f27ab9492b07b 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -101,9 +101,7 @@ void Rasterizer::Teardown() { } studio_.reset(); } - surface_.reset(); - - last_layer_tree_.reset(); + surfaces_.clear(); if (raster_thread_merger_.get() != nullptr && raster_thread_merger_.get()->IsMerged()) { @@ -146,9 +144,16 @@ void Rasterizer::NotifyLowMemoryWarning() const { void Rasterizer::RegisterSurface(int64_t view_id, std::unique_ptr surface) { - // TODO(dkwingsmt) - FML_DCHECK(view_id == 0ll); - surface_ = std::move(surface); + bool insertion_happened = + surfaces_ + .try_emplace(view_id, + std::make_unique(std::move(surface))) + .second; + if (!insertion_happened) { + FML_DLOG(INFO) + << "Rasterizer::RegisterSurface called with an existing view ID " + << view_id << "."; + } } std::shared_ptr Rasterizer::GetTextureRegistry() { @@ -160,33 +165,50 @@ GrDirectContext* Rasterizer::GetGrContext() { } bool Rasterizer::HasLastLayerTree() const { - return !!last_layer_tree_; + // TODO(dkwingsmt): This method is only available in unittests now + for (auto& record_pair : surfaces_) { + auto& layer_tree = record_pair.second->last_tree; + if (layer_tree) { + return true; + } + } + return false; } -void Rasterizer::DrawLastLayerTree( +int Rasterizer::DrawLastLayerTree( std::unique_ptr frame_timings_recorder, bool enable_leaf_layer_tracing) { - if (!last_layer_tree_ || !studio_) { - return; - } - // TODO(dkwingsmt): Apply to all last trees - flutter::LayerTree& layer_tree = *last_layer_tree_; - if (enable_leaf_layer_tracing) { - layer_tree.enable_leaf_layer_tracing(true); - } - RasterStatus raster_status = - DrawToSurface(*frame_timings_recorder, layer_tree); - if (enable_leaf_layer_tracing) { - layer_tree.enable_leaf_layer_tracing(false); + if (!studio_) { + return 0; + } + int success_count = 0; + bool should_resubmit_frame = false; + for (auto& record_pair : surfaces_) { + Surface* surface = record_pair.second->surface.get(); + flutter::LayerTree* layer_tree = record_pair.second->last_tree.get(); + if (!surface || !layer_tree) { + continue; + } + if (enable_leaf_layer_tracing) { + layer_tree->enable_leaf_layer_tracing(true); + } + RasterStatus raster_status = DrawToSurface( + *frame_timings_recorder, layer_tree, record_pair.second.get()); + if (enable_leaf_layer_tracing) { + layer_tree->enable_leaf_layer_tracing(false); + } + should_resubmit_frame = + should_resubmit_frame || ShouldResubmitFrame(raster_status); + success_count += 1; } // EndFrame should perform cleanups for the external_view_embedder. if (external_view_embedder_ && external_view_embedder_->GetUsedThisFrame()) { - bool should_resubmit_frame = ShouldResubmitFrame(raster_status); external_view_embedder_->SetUsedThisFrame(false); external_view_embedder_->EndFrame(should_resubmit_frame, raster_thread_merger_); } + return success_count; } RasterStatus Rasterizer::Draw( @@ -212,8 +234,10 @@ RasterStatus Rasterizer::Draw( if (discard_callback(*layer_tree.get())) { draw_result.raster_status = RasterStatus::kDiscarded; } else { - draw_result = - DoDraw(std::move(frame_timings_recorder), std::move(layer_tree)); + // TODO(dkwingsmt) + int64_t view_id = 0ll; + draw_result = DoDraw(view_id, std::move(frame_timings_recorder), + std::move(layer_tree)); } }; @@ -386,6 +410,7 @@ fml::Milliseconds Rasterizer::GetFrameBudget() const { }; Rasterizer::DoDrawResult Rasterizer::DoDraw( + int64_t view_id, std::unique_ptr frame_timings_recorder, std::shared_ptr layer_tree) { TRACE_EVENT_WITH_FRAME_NUMBER(frame_timings_recorder, "flutter", @@ -393,8 +418,9 @@ Rasterizer::DoDrawResult Rasterizer::DoDraw( FML_DCHECK(delegate_.GetTaskRunners() .GetRasterTaskRunner() ->RunsTasksOnCurrentThread()); + SurfaceRecord* surface_record = GetSurface(view_id); - if (!layer_tree || !surface_) { + if (!layer_tree || !surface_record) { return DoDrawResult{ .raster_status = RasterStatus::kFailed, }; @@ -404,9 +430,9 @@ Rasterizer::DoDrawResult Rasterizer::DoDraw( persistent_cache->ResetStoredNewShaders(); RasterStatus raster_status = - DrawToSurface(*frame_timings_recorder, *layer_tree); + DrawToSurface(*frame_timings_recorder, layer_tree.get(), surface_record); if (raster_status == RasterStatus::kSuccess) { - last_layer_tree_ = std::move(layer_tree); + surface_record->last_tree = std::move(layer_tree); } else if (ShouldResubmitFrame(raster_status)) { return DoDrawResult{ .raster_status = raster_status, @@ -422,8 +448,8 @@ Rasterizer::DoDrawResult Rasterizer::DoDraw( if (persistent_cache->IsDumpingSkp() && persistent_cache->StoredNewShaders()) { - auto screenshot = - ScreenshotLastLayerTree(ScreenshotType::SkiaPicture, false); + auto screenshot = ScreenshotLayerTree(ScreenshotType::SkiaPicture, false, + *surface_record); persistent_cache->DumpSkp(*screenshot.data); } @@ -498,20 +524,22 @@ Rasterizer::DoDrawResult Rasterizer::DoDraw( RasterStatus Rasterizer::DrawToSurface( FrameTimingsRecorder& frame_timings_recorder, - flutter::LayerTree& layer_tree) { + flutter::LayerTree* layer_tree, + SurfaceRecord* surface_record) { TRACE_EVENT0("flutter", "Rasterizer::DrawToSurface"); - FML_DCHECK(surface_); + FML_DCHECK(surface_record); RasterStatus raster_status; if (studio_->AllowsDrawingWhenGpuDisabled()) { - raster_status = DrawToSurfaceUnsafe(frame_timings_recorder, layer_tree); + raster_status = + DrawToSurfaceUnsafe(frame_timings_recorder, layer_tree, surface_record); } else { delegate_.GetIsGpuDisabledSyncSwitch()->Execute( fml::SyncSwitch::Handlers() .SetIfTrue([&] { raster_status = RasterStatus::kDiscarded; }) .SetIfFalse([&] { - raster_status = - DrawToSurfaceUnsafe(frame_timings_recorder, layer_tree); + raster_status = DrawToSurfaceUnsafe(frame_timings_recorder, + layer_tree, surface_record); })); } @@ -523,8 +551,11 @@ RasterStatus Rasterizer::DrawToSurface( /// \see Rasterizer::DrawToSurface RasterStatus Rasterizer::DrawToSurfaceUnsafe( FrameTimingsRecorder& frame_timings_recorder, - flutter::LayerTree& layer_tree) { - FML_DCHECK(surface_); + flutter::LayerTree* layer_tree, + SurfaceRecord* surface_record) { + Surface* surface = surface_record->surface.get(); + flutter::LayerTree* last_layer_tree = surface_record->last_tree.get(); + FML_DCHECK(surface); compositor_context_->ui_time().SetLapTime( frame_timings_recorder.GetBuildDuration()); @@ -534,8 +565,8 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( FML_DCHECK(!external_view_embedder_->GetUsedThisFrame()); external_view_embedder_->SetUsedThisFrame(true); external_view_embedder_->BeginFrame( - layer_tree.frame_size(), studio_->GetContext(), - layer_tree.device_pixel_ratio(), raster_thread_merger_); + layer_tree->frame_size(), studio_->GetContext(), + layer_tree->device_pixel_ratio(), raster_thread_merger_); embedder_root_canvas = external_view_embedder_->GetRootCanvas(); } @@ -545,7 +576,7 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( // // Deleting a surface also clears the GL context. Therefore, acquire the // frame after calling `BeginFrame` as this operation resets the GL context. - auto frame = surface_->AcquireFrame(layer_tree.frame_size()); + auto frame = surface->AcquireFrame(layer_tree->frame_size()); if (frame == nullptr) { frame_timings_recorder.RecordRasterEnd( &compositor_context_->raster_cache()); @@ -556,7 +587,7 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( // root surface transformation is set by the embedder instead of // having to apply it here. SkMatrix root_surface_transformation = - embedder_root_canvas ? SkMatrix{} : surface_->GetRootTransformation(); + embedder_root_canvas ? SkMatrix{} : surface->GetRootTransformation(); auto root_surface_canvas = embedder_root_canvas ? embedder_root_canvas : frame->Canvas(); @@ -580,7 +611,7 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( // when leaf layer tracing is enabled we wish to repaint the whole frame // for accurate performance metrics. if (frame->framebuffer_info().supports_partial_repaint && - !layer_tree.is_leaf_layer_tracing_enabled()) { + !layer_tree->is_leaf_layer_tracing_enabled()) { // Disable partial repaint if external_view_embedder_ SubmitFrame is // involved - ExternalViewEmbedder unconditionally clears the entire // surface and also partial repaint with platform view present is @@ -591,7 +622,7 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( damage = std::make_unique(); if (frame->framebuffer_info().existing_damage && !force_full_repaint) { - damage->SetPreviousLayerTree(last_layer_tree_.get()); + damage->SetPreviousLayerTree(last_layer_tree); damage->AddAdditionalDamage(*frame->framebuffer_info().existing_damage); damage->SetClipAlignment( frame->framebuffer_info().horizontal_clip_alignment, @@ -601,12 +632,12 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( bool ignore_raster_cache = true; if (studio_->EnableRasterCache() && - !layer_tree.is_leaf_layer_tracing_enabled()) { + !layer_tree->is_leaf_layer_tracing_enabled()) { ignore_raster_cache = false; } RasterStatus raster_status = - compositor_frame->Raster(layer_tree, // layer tree + compositor_frame->Raster(*layer_tree, // layer tree ignore_raster_cache, // ignore raster cache damage.get() // frame damage ); @@ -745,13 +776,22 @@ sk_sp Rasterizer::ScreenshotLayerTreeAsImage( Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree( Rasterizer::ScreenshotType type, bool base64_encode) { - // TODO(dkwingsmt): Probably screenshot all layer trees instead of just - // the first one and put them together. - auto* layer_tree = last_layer_tree_.get(); - if (layer_tree == nullptr) { + // TODO(dkwingsmt): Probably screenshot all layer trees and put them together + // instead of just the first one. + SurfaceRecord* surface_record = GetFirstSurface(); + if (surface_record == nullptr || surface_record->last_tree == nullptr) { FML_LOG(ERROR) << "Last layer tree was null when screenshotting."; return {}; } + return ScreenshotLayerTree(type, base64_encode, *surface_record); +} + +Rasterizer::Screenshot Rasterizer::ScreenshotLayerTree( + ScreenshotType type, + bool base64_encode, + SurfaceRecord& surface_record) { + auto& surface = surface_record.surface; + auto* layer_tree = surface_record.last_tree.get(); sk_sp data = nullptr; std::string format; @@ -774,7 +814,7 @@ Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree( surface_context, true); break; case ScreenshotType::SurfaceData: { - Surface::SurfaceData surface_data = surface_->GetSurfaceData(); + Surface::SurfaceData surface_data = surface->GetSurfaceData(); format = surface_data.pixel_format; data = surface_data.data; break; diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 83bd020883339..14fae960d48b6 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -7,6 +7,7 @@ #include #include +#include #include "flutter/common/settings.h" #include "flutter/common/task_runners.h" @@ -139,24 +140,22 @@ class Rasterizer final : public SnapshotDelegate, ~Rasterizer(); //---------------------------------------------------------------------------- - /// @brief Rasterizers may be created well before an on-screen surface is + /// @brief Rasterizers may be created well before an on-screen studio is /// available for rendering. Shells usually create a rasterizer in - /// their constructors. Once an on-screen surface is available + /// their constructors. Once an on-screen studio is available /// however, one may be provided to the rasterizer using this - /// call. No rendering may occur before this call. The surface is + /// call. No rendering may occur before this call. The studio is /// held till the balancing call to `Rasterizer::Teardown` is - /// made. Calling a setup before tearing down the previous surface + /// made. Calling a setup before tearing down the studio /// (if this is not the first time the surface has been set up) is /// user error. /// /// @see `Rasterizer::Teardown` /// - /// @param[in] surface The on-screen render surface. + /// @param[in] studio The on-screen render studio. /// void Setup(std::unique_ptr studio); - void RegisterSurface(int64_t view_id, std::unique_ptr surface); - //---------------------------------------------------------------------------- /// @brief Releases the previously set up on-screen render surface and /// collects associated resources. No more rendering may occur @@ -190,13 +189,9 @@ class Rasterizer final : public SnapshotDelegate, fml::TaskRunnerAffineWeakPtr GetSnapshotDelegate() const; + void RegisterSurface(int64_t view_id, std::unique_ptr surface); + //---------------------------------------------------------------------------- - /// @brief Sometimes, it may be necessary to render the same frame again - /// without having to wait for the framework to build a whole new - /// layer tree describing the same contents. One such case is when - /// external textures (video or camera streams for example) are - /// updated in an otherwise static layer tree. To support this use - /// case, the rasterizer holds onto the last rendered layer tree. /// /// @bug https://github.com/flutter/flutter/issues/33939 /// @@ -218,7 +213,8 @@ class Rasterizer final : public SnapshotDelegate, /// textures instead of waiting for the framework to do the work /// to generate the layer tree describing the same contents. /// - void DrawLastLayerTree( + /// @return The number of surfaces that are drawn this way. + int DrawLastLayerTree( std::unique_ptr frame_timings_recorder, bool enable_leaf_layer_tracing = false); @@ -497,6 +493,35 @@ class Rasterizer final : public SnapshotDelegate, std::unique_ptr resubmitted_recorder; }; + struct SurfaceRecord { + explicit SurfaceRecord(std::unique_ptr surface) + : surface(std::move(surface)) {} + + std::unique_ptr surface; + // This is the information for the last successfully drawing. + // + // Sometimes, it may be necessary to render the same frame again without + // having to wait for the framework to build a whole new layer tree + // describing the same contents. One such case is when external textures + // (video or camera streams for example) are updated in an otherwise static + // layer tree. To support this use case, the rasterizer holds onto the last + // rendered layer tree. + std::shared_ptr last_tree; + }; + + SurfaceRecord* GetSurface(int64_t view_id) { + auto found_surface = surfaces_.find(view_id); + if (found_surface == surfaces_.end()) { + return nullptr; + } + return found_surface->second.get(); + } + + SurfaceRecord* GetFirstSurface() { + // TODO(dkwingsmt) + return GetSurface(0ll); + } + // |SnapshotDelegate| std::unique_ptr MakeSkiaGpuImage( sk_sp display_list, @@ -537,14 +562,21 @@ class Rasterizer final : public SnapshotDelegate, bool compressed); DoDrawResult DoDraw( + int64_t view_id, std::unique_ptr frame_timings_recorder, std::shared_ptr layer_tree); RasterStatus DrawToSurface(FrameTimingsRecorder& frame_timings_recorder, - flutter::LayerTree& layer_tree); + flutter::LayerTree* layer_tree, + SurfaceRecord* surface_record); RasterStatus DrawToSurfaceUnsafe(FrameTimingsRecorder& frame_timings_recorder, - flutter::LayerTree& layer_tree); + flutter::LayerTree* layer_tree, + SurfaceRecord* surface_record); + + Screenshot ScreenshotLayerTree(ScreenshotType type, + bool base64_encode, + SurfaceRecord& surface_record); void FireNextFrameCallbackIfPresent(); @@ -554,11 +586,12 @@ class Rasterizer final : public SnapshotDelegate, Delegate& delegate_; MakeGpuImageBehavior gpu_image_behavior_; std::unique_ptr studio_; - std::unique_ptr surface_; + std::unordered_map> surfaces_; std::unique_ptr snapshot_surface_producer_; std::unique_ptr compositor_context_; - // This is the last successfully rasterized layer tree. - std::shared_ptr last_layer_tree_; + // Set when we need attempt to rasterize the layer tree again. This layer_tree + // has not successfully rasterized. This can happen due to the change in the + // thread configuration. This will be inserted to the front of the pipeline. fml::closure next_frame_callback_; bool user_override_resource_cache_bytes_; std::optional max_cache_bytes_; diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 359d94a3d4723..b3d22259f9587 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -1896,23 +1896,24 @@ bool Shell::OnServiceProtocolRenderFrameWithRasterStats( rapidjson::Document* response) { FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); - if (rasterizer_->HasLastLayerTree()) { + // When rendering the last layer tree, we do not need to build a frame, + // invariants in FrameTimingRecorder enforce that raster timings can not be + // set before build-end. + auto frame_timings_recorder = std::make_unique(); + const auto now = fml::TimePoint::Now(); + frame_timings_recorder->RecordVsync(now, now); + frame_timings_recorder->RecordBuildStart(now); + frame_timings_recorder->RecordBuildEnd(now); + + int success_count = + rasterizer_->DrawLastLayerTree(std::move(frame_timings_recorder), + /* enable_leaf_layer_tracing=*/true); + + if (success_count > 0) { auto& allocator = response->GetAllocator(); response->SetObject(); response->AddMember("type", "RenderFrameWithRasterStats", allocator); - // When rendering the last layer tree, we do not need to build a frame, - // invariants in FrameTimingRecorder enforce that raster timings can not be - // set before build-end. - auto frame_timings_recorder = std::make_unique(); - const auto now = fml::TimePoint::Now(); - frame_timings_recorder->RecordVsync(now, now); - frame_timings_recorder->RecordBuildStart(now); - frame_timings_recorder->RecordBuildEnd(now); - - rasterizer_->DrawLastLayerTree(std::move(frame_timings_recorder), - /* enable_leaf_layer_tracing=*/true); - rapidjson::Value snapshots; snapshots.SetArray(); From 644d50acb723a0d22f61f350e1b65ac58b9ea8f6 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Sat, 25 Mar 2023 21:52:18 +0000 Subject: [PATCH 062/147] surfaces_ contains objects instead of unique_ptrs --- shell/common/rasterizer.cc | 15 ++++++--------- shell/common/rasterizer.h | 4 ++-- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index f27ab9492b07b..b49fec6ee44e7 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -145,10 +145,7 @@ void Rasterizer::NotifyLowMemoryWarning() const { void Rasterizer::RegisterSurface(int64_t view_id, std::unique_ptr surface) { bool insertion_happened = - surfaces_ - .try_emplace(view_id, - std::make_unique(std::move(surface))) - .second; + surfaces_.try_emplace(view_id, std::move(surface)).second; if (!insertion_happened) { FML_DLOG(INFO) << "Rasterizer::RegisterSurface called with an existing view ID " @@ -167,7 +164,7 @@ GrDirectContext* Rasterizer::GetGrContext() { bool Rasterizer::HasLastLayerTree() const { // TODO(dkwingsmt): This method is only available in unittests now for (auto& record_pair : surfaces_) { - auto& layer_tree = record_pair.second->last_tree; + auto& layer_tree = record_pair.second.last_tree; if (layer_tree) { return true; } @@ -184,16 +181,16 @@ int Rasterizer::DrawLastLayerTree( int success_count = 0; bool should_resubmit_frame = false; for (auto& record_pair : surfaces_) { - Surface* surface = record_pair.second->surface.get(); - flutter::LayerTree* layer_tree = record_pair.second->last_tree.get(); + Surface* surface = record_pair.second.surface.get(); + flutter::LayerTree* layer_tree = record_pair.second.last_tree.get(); if (!surface || !layer_tree) { continue; } if (enable_leaf_layer_tracing) { layer_tree->enable_leaf_layer_tracing(true); } - RasterStatus raster_status = DrawToSurface( - *frame_timings_recorder, layer_tree, record_pair.second.get()); + RasterStatus raster_status = + DrawToSurface(*frame_timings_recorder, layer_tree, &record_pair.second); if (enable_leaf_layer_tracing) { layer_tree->enable_leaf_layer_tracing(false); } diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 14fae960d48b6..8f2a7dae5a7b2 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -514,7 +514,7 @@ class Rasterizer final : public SnapshotDelegate, if (found_surface == surfaces_.end()) { return nullptr; } - return found_surface->second.get(); + return &found_surface->second; } SurfaceRecord* GetFirstSurface() { @@ -586,7 +586,7 @@ class Rasterizer final : public SnapshotDelegate, Delegate& delegate_; MakeGpuImageBehavior gpu_image_behavior_; std::unique_ptr studio_; - std::unordered_map> surfaces_; + std::unordered_map surfaces_; std::unique_ptr snapshot_surface_producer_; std::unique_ptr compositor_context_; // Set when we need attempt to rasterize the layer tree again. This layer_tree From 253f3c98b2fb4cd1d5fc81bd631dacb4b730991e Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 27 Mar 2023 18:49:19 +0000 Subject: [PATCH 063/147] Split out CreateSurface --- shell/common/platform_view.cc | 42 ++++++++++++++++++++++++----------- shell/common/platform_view.h | 4 ++-- shell/common/shell.cc | 5 ++--- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index b9c73f5323d2b..fac06cee3b7b1 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -65,10 +65,8 @@ void PlatformView::NotifyDestroyed() { constexpr int64_t kFlutterDefaultViewId = 0; -std::pair, std::unique_ptr> -PlatformView::CreateStudioAndSurface() { +std::unique_ptr PlatformView::CreateStudio() { std::unique_ptr studio; - std::unique_ptr surface; // Threading: We want to use the platform view on the non-platform thread. // Using the weak pointer is illegal. But, we are going to introduce a latch // so that the platform view is not collected till the studio and the surface @@ -76,25 +74,43 @@ PlatformView::CreateStudioAndSurface() { auto* platform_view = this; fml::ManualResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask( - task_runners_.GetRasterTaskRunner(), - [platform_view, &studio, &surface, &latch]() { + task_runners_.GetRasterTaskRunner(), [platform_view, &studio, &latch]() { studio = platform_view->CreateRenderingStudio(); + if (!studio || !studio->IsValid()) { + studio.reset(); + } + latch.Signal(); + }); + latch.Wait(); + if (!studio) { + FML_LOG(ERROR) << "Failed to create platform view rendering studio"; + return nullptr; + } + return studio; +} + +std::unique_ptr PlatformView::CreateSurface() { + std::unique_ptr surface; + // Threading: We want to use the platform view on the non-platform thread. + // Using the weak pointer is illegal. But, we are going to introduce a latch + // so that the platform view is not collected till the studio and the surface + // are obtained. + auto* platform_view = this; + fml::ManualResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetRasterTaskRunner(), [platform_view, &surface, &latch]() { surface = platform_view->CreateRenderingSurface(kFlutterDefaultViewId); - if (!studio || !studio->IsValid() || !surface || !surface->IsValid()) { + if (!surface || !surface->IsValid()) { surface.reset(); - studio.reset(); } latch.Signal(); }); latch.Wait(); - std::pair, std::unique_ptr> result; - if (!studio || !surface) { + if (!surface) { FML_LOG(ERROR) << "Failed to create platform view rendering surface"; - return result; + return nullptr; } - result.first = std::move(studio); - result.second = std::move(surface); - return result; + return surface; } void PlatformView::ScheduleFrame() { diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 77088d57eceb6..985a25bcfe014 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -494,8 +494,8 @@ class PlatformView { /// virtual void NotifyDestroyed(); - std::pair, std::unique_ptr> - CreateStudioAndSurface(); + std::unique_ptr CreateStudio(); + std::unique_ptr CreateSurface(); //---------------------------------------------------------------------------- /// @brief Used by embedders to schedule a frame. In response to this diff --git a/shell/common/shell.cc b/shell/common/shell.cc index b3d22259f9587..3bf10ce28d615 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -750,9 +750,8 @@ void Shell::OnPlatformViewCreated() { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); - auto studio_and_surface = platform_view_->CreateStudioAndSurface(); - std::unique_ptr studio = std::move(studio_and_surface.first); - std::unique_ptr surface = std::move(studio_and_surface.second); + std::unique_ptr studio = platform_view_->CreateStudio(); + std::unique_ptr surface = platform_view_->CreateSurface(); if (studio == nullptr || surface == nullptr) { // TODO(dkwingsmt): This case is observed in windows unit tests. Anyway, // we're probably not creating the surface in this callback eventually. From 9dc2cebb92f0bd5008b96c4cb9418db4bffec28a Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 27 Mar 2023 22:12:46 +0000 Subject: [PATCH 064/147] Embedder.AddRenderSurface --- shell/common/rasterizer.cc | 8 +++---- shell/common/rasterizer.h | 2 +- shell/common/rasterizer_unittests.cc | 32 ++++++++++++++-------------- shell/common/shell.cc | 26 ++++++++++++++++++++-- shell/common/shell.h | 2 ++ shell/platform/embedder/embedder.cc | 16 ++++++++++++++ shell/platform/embedder/embedder.h | 13 +++++++++++ 7 files changed, 75 insertions(+), 24 deletions(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index b49fec6ee44e7..4de7e9038613d 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -142,14 +142,12 @@ void Rasterizer::NotifyLowMemoryWarning() const { context->performDeferredCleanup(std::chrono::milliseconds(0)); } -void Rasterizer::RegisterSurface(int64_t view_id, - std::unique_ptr surface) { +void Rasterizer::AddSurface(int64_t view_id, std::unique_ptr surface) { bool insertion_happened = surfaces_.try_emplace(view_id, std::move(surface)).second; if (!insertion_happened) { - FML_DLOG(INFO) - << "Rasterizer::RegisterSurface called with an existing view ID " - << view_id << "."; + FML_DLOG(INFO) << "Rasterizer::AddSurface called with an existing view ID " + << view_id << "."; } } diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 8f2a7dae5a7b2..6ff0d74dff670 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -189,7 +189,7 @@ class Rasterizer final : public SnapshotDelegate, fml::TaskRunnerAffineWeakPtr GetSnapshotDelegate() const; - void RegisterSurface(int64_t view_id, std::unique_ptr surface); + void AddSurface(int64_t view_id, std::unique_ptr surface); //---------------------------------------------------------------------------- /// diff --git a/shell/common/rasterizer_unittests.cc b/shell/common/rasterizer_unittests.cc index 38764cc0467da..b923611896ee0 100644 --- a/shell/common/rasterizer_unittests.cc +++ b/shell/common/rasterizer_unittests.cc @@ -133,7 +133,7 @@ TEST(RasterizerTest, drawEmptyPipeline) { EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio)); - rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -196,7 +196,7 @@ TEST(RasterizerTest, .Times(1); rasterizer->Setup(std::move(studio)); - rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -263,7 +263,7 @@ TEST( .Times(1); rasterizer->Setup(std::move(studio)); - rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -337,7 +337,7 @@ TEST( .Times(1); rasterizer->Setup(std::move(studio)); - rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), @@ -413,7 +413,7 @@ TEST(RasterizerTest, .Times(2); rasterizer->Setup(std::move(studio)); - rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), @@ -505,7 +505,7 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNotUsedThisFrame) { std::make_shared>(); rasterizer->SetExternalViewEmbedder(external_view_embedder); rasterizer->Setup(std::move(studio)); - rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface)); EXPECT_CALL(*external_view_embedder, BeginFrame(/*frame_size=*/SkISize(), /*context=*/nullptr, @@ -564,7 +564,7 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenPipelineIsEmpty) { std::make_shared>(); rasterizer->SetExternalViewEmbedder(external_view_embedder); rasterizer->Setup(std::move(studio)); - rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface)); EXPECT_CALL( *external_view_embedder, @@ -624,7 +624,7 @@ TEST(RasterizerTest, .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio)); - rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -683,7 +683,7 @@ TEST( .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio)); - rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -742,7 +742,7 @@ TEST( .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio)); - rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -800,7 +800,7 @@ TEST( .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio)); - rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -857,7 +857,7 @@ TEST( EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio)); - rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -938,7 +938,7 @@ TEST(RasterizerTest, thread_host.raster_thread->GetTaskRunner()->PostTask([&] { rasterizer->Setup(std::move(studio)); - rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); for (int i = 0; i < 2; i++) { auto layer_tree = @@ -994,7 +994,7 @@ TEST(RasterizerTest, TeardownFreesResourceCache) { EXPECT_CALL(*studio, GetContext()).WillRepeatedly(Return(context.get())); rasterizer->Setup(std::move(studio)); - rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface)); EXPECT_EQ(context->getResourceCacheLimit(), 0ul); rasterizer->SetResourceCacheMaxBytes(10000000, false); @@ -1113,7 +1113,7 @@ TEST(RasterizerTest, presentationTimeSetWhenVsyncTargetInFuture) { thread_host.raster_thread->GetTaskRunner()->PostTask([&] { rasterizer->Setup(std::move(studio)); - rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); for (int i = 0; i < 2; i++) { auto layer_tree = @@ -1196,7 +1196,7 @@ TEST(RasterizerTest, presentationTimeNotSetWhenVsyncTargetInPast) { thread_host.raster_thread->GetTaskRunner()->PostTask([&] { rasterizer->Setup(std::move(studio)); - rasterizer->RegisterSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), /*device_pixel_ratio=*/2.0f); diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 3bf10ce28d615..0f328503ba32a 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -744,6 +744,8 @@ DartVM* Shell::GetDartVM() { return &vm_; } +constexpr int64_t kFlutterDefaultViewId = 0ll; + // |PlatformView::Delegate| void Shell::OnPlatformViewCreated() { TRACE_EVENT0("flutter", "Shell::OnPlatformViewCreated"); @@ -792,8 +794,7 @@ void Shell::OnPlatformViewCreated() { // embedder. rasterizer->EnableThreadMergerIfNeeded(); rasterizer->Setup(std::move(studio)); - // TODO(dkwingsmt): Change 0ll - rasterizer->RegisterSurface(0ll, std::move(surface)); + rasterizer->AddSurface(kFlutterDefaultViewId, std::move(surface)); } waiting_for_first_frame.store(true); @@ -1982,6 +1983,27 @@ bool Shell::OnServiceProtocolReloadAssetFonts( return true; } +void Shell::AddRenderSurface(int64_t view_id) { + TRACE_EVENT0("flutter", "Shell::AddRenderSurface"); + FML_DCHECK(is_setup_); + FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + std::unique_ptr surface = platform_view_->CreateSurface(); + fml::AutoResetWaitableEvent latch; + task_runners_.GetRasterTaskRunner()->PostTask( + fml::MakeCopyable([&latch, // + rasterizer = rasterizer_->GetWeakPtr(), // + surface = std::move(surface), // + view_id // + ]() mutable { + if (rasterizer) { + rasterizer->AddSurface(view_id, std::move(surface)); + } + latch.Signal(); + })); + latch.Wait(); +} + Rasterizer::Screenshot Shell::Screenshot( Rasterizer::ScreenshotType screenshot_type, bool base64_encode) { diff --git a/shell/common/shell.h b/shell/common/shell.h index 6a1c9852e233b..9642a7e3b428c 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -297,6 +297,8 @@ class Shell final : public PlatformView::Delegate, /// bool IsSetup() const; + void AddRenderSurface(int64_t view_id); + //---------------------------------------------------------------------------- /// @brief Captures a screenshot and optionally Base64 encodes the data /// of the last layer tree rendered by the rasterizer in this diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 051b3f3811ae9..eb7e72d7135db 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -2046,6 +2046,22 @@ FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine) return kSuccess; } +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineAddRenderSurface( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterRendererConfig* config, + void* user_data, + int64_t view_id) { + if (engine == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); + } + flutter::EmbedderEngine* embedder_engine = + reinterpret_cast(engine); + embedder_engine->GetShell().AddRenderSurface(view_id); + + return kSuccess; +} + FlutterEngineResult FlutterEngineSendWindowMetricsEvent( FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterWindowMetricsEvent* flutter_metrics) { diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 5cdba06ef505d..d296fab874add 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -2243,6 +2243,13 @@ FLUTTER_EXPORT FlutterEngineResult FlutterEngineRunInitialized( FLUTTER_API_SYMBOL(FlutterEngine) engine); +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineAddRenderSurface( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterRendererConfig* config, + void* user_data, + int64_t view_id); + FLUTTER_EXPORT FlutterEngineResult FlutterEngineSendWindowMetricsEvent( FLUTTER_API_SYMBOL(FlutterEngine) engine, @@ -2817,6 +2824,11 @@ typedef FlutterEngineResult (*FlutterEngineDeinitializeFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine); typedef FlutterEngineResult (*FlutterEngineRunInitializedFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine); +typedef FlutterEngineResult (*FlutterEngineAddRenderSurfaceFnPtr)( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterRendererConfig* config, + void* user_data, + int64_t view_id); typedef FlutterEngineResult (*FlutterEngineSendWindowMetricsEventFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterWindowMetricsEvent* event); @@ -2926,6 +2938,7 @@ typedef struct { FlutterEngineInitializeFnPtr Initialize; FlutterEngineDeinitializeFnPtr Deinitialize; FlutterEngineRunInitializedFnPtr RunInitialized; + FlutterEngineAddRenderSurfaceFnPtr AddRenderSurface; FlutterEngineSendWindowMetricsEventFnPtr SendWindowMetricsEvent; FlutterEngineSendPointerEventFnPtr SendPointerEvent; FlutterEngineSendKeyEventFnPtr SendKeyEvent; From 13b3a95f46eaea9f812d092703538b4137ebeab5 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 27 Mar 2023 22:23:47 +0000 Subject: [PATCH 065/147] Fix embedder test --- shell/platform/embedder/embedder.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index eb7e72d7135db..18fb98c1c8f75 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -3099,6 +3099,7 @@ FlutterEngineResult FlutterEngineGetProcAddresses( SET_PROC(Initialize, FlutterEngineInitialize); SET_PROC(Deinitialize, FlutterEngineDeinitialize); SET_PROC(RunInitialized, FlutterEngineRunInitialized); + SET_PROC(AddRenderSurface, FlutterEngineAddRenderSurface); SET_PROC(SendWindowMetricsEvent, FlutterEngineSendWindowMetricsEvent); SET_PROC(SendPointerEvent, FlutterEngineSendPointerEvent); SET_PROC(SendKeyEvent, FlutterEngineSendKeyEvent); From 23d99de0d55db8cd61e4fe385af0fbd22b895c21 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 27 Mar 2023 22:32:39 +0000 Subject: [PATCH 066/147] Move MakeGLContext to studio --- shell/common/shell_test_platform_view_gl.cc | 4 +- shell/gpu/gpu_studio_gl_skia.cc | 41 +++++++++++++++---- shell/gpu/gpu_studio_gl_skia.h | 2 + shell/gpu/gpu_surface_gl_skia.cc | 34 --------------- shell/gpu/gpu_surface_gl_skia.h | 2 - .../android/android_surface_gl_skia.cc | 4 +- shell/platform/embedder/embedder_studio_gl.cc | 2 +- 7 files changed, 40 insertions(+), 49 deletions(-) diff --git a/shell/common/shell_test_platform_view_gl.cc b/shell/common/shell_test_platform_view_gl.cc index 02a205b455cf3..1e36400d96939 100644 --- a/shell/common/shell_test_platform_view_gl.cc +++ b/shell/common/shell_test_platform_view_gl.cc @@ -40,7 +40,7 @@ void ShellTestPlatformViewGL::SimulateVSync() { // |PlatformView| std::unique_ptr ShellTestPlatformViewGL::CreateRenderingStudio() { if (main_context_ == nullptr) { - main_context_ = GPUSurfaceGLSkia::MakeGLContext(this); + main_context_ = GPUStudioGLSkia::MakeGLContext(this); } return std::make_unique(main_context_, this); } @@ -49,7 +49,7 @@ std::unique_ptr ShellTestPlatformViewGL::CreateRenderingStudio() { std::unique_ptr ShellTestPlatformViewGL::CreateRenderingSurface( int64_t view_id) { if (main_context_ == nullptr) { - main_context_ = GPUSurfaceGLSkia::MakeGLContext(this); + main_context_ = GPUStudioGLSkia::MakeGLContext(this); } return std::make_unique(main_context_, this, true); } diff --git a/shell/gpu/gpu_studio_gl_skia.cc b/shell/gpu/gpu_studio_gl_skia.cc index 00c36c7b6e29c..60ae52e808c91 100644 --- a/shell/gpu/gpu_studio_gl_skia.cc +++ b/shell/gpu/gpu_studio_gl_skia.cc @@ -17,16 +17,41 @@ #include "third_party/skia/include/core/SkColorType.h" #include "third_party/skia/include/gpu/GrContextOptions.h" -// These are common defines present on all OpenGL headers. However, we don't -// want to perform GL header reasolution on each platform we support. So just -// define these upfront. It is unlikely we will need more. But, if we do, we can -// add the same here. -#define GPU_GL_RGBA8 0x8058 -#define GPU_GL_RGBA4 0x8056 -#define GPU_GL_RGB565 0x8D62 - namespace flutter { +// Default maximum number of bytes of GPU memory of budgeted resources in the +// cache. +// The shell will dynamically increase or decrease this cache based on the +// viewport size, unless a user has specifically requested a size on the Skia +// system channel. +static const size_t kGrCacheMaxByteSize = 24 * (1 << 20); + +sk_sp GPUStudioGLSkia::MakeGLContext( + GPUSurfaceGLDelegate* delegate) { + auto context_switch = delegate->GLContextMakeCurrent(); + if (!context_switch->GetResult()) { + FML_LOG(ERROR) + << "Could not make the context current to set up the Gr context."; + return nullptr; + } + + const auto options = + MakeDefaultContextOptions(ContextType::kRender, GrBackendApi::kOpenGL); + + auto context = GrDirectContext::MakeGL(delegate->GetGLInterface(), options); + + if (!context) { + FML_LOG(ERROR) << "Failed to set up Skia Gr context."; + return nullptr; + } + + context->setResourceCacheLimit(kGrCacheMaxByteSize); + + PersistentCache::GetCacheForProcess()->PrecompileKnownSkSLs(context.get()); + + return context; +} + GPUStudioGLSkia::GPUStudioGLSkia(const sk_sp& gr_context, GPUSurfaceGLDelegate* delegate) : delegate_(delegate), context_(gr_context) { diff --git a/shell/gpu/gpu_studio_gl_skia.h b/shell/gpu/gpu_studio_gl_skia.h index 25ee264d1cfbf..2744e4e794404 100644 --- a/shell/gpu/gpu_studio_gl_skia.h +++ b/shell/gpu/gpu_studio_gl_skia.h @@ -22,6 +22,8 @@ namespace flutter { class GPUStudioGLSkia : public Studio { public: + static sk_sp MakeGLContext(GPUSurfaceGLDelegate* delegate); + GPUStudioGLSkia(const sk_sp& gr_context, GPUSurfaceGLDelegate* delegate); diff --git a/shell/gpu/gpu_surface_gl_skia.cc b/shell/gpu/gpu_surface_gl_skia.cc index e4b81f6605641..d828efdced2d0 100644 --- a/shell/gpu/gpu_surface_gl_skia.cc +++ b/shell/gpu/gpu_surface_gl_skia.cc @@ -4,7 +4,6 @@ #include "flutter/shell/gpu/gpu_surface_gl_skia.h" -#include "flutter/common/graphics/persistent_cache.h" #include "flutter/fml/base32.h" #include "flutter/fml/logging.h" #include "flutter/fml/size.h" @@ -29,39 +28,6 @@ namespace flutter { -// Default maximum number of bytes of GPU memory of budgeted resources in the -// cache. -// The shell will dynamically increase or decrease this cache based on the -// viewport size, unless a user has specifically requested a size on the Skia -// system channel. -static const size_t kGrCacheMaxByteSize = 24 * (1 << 20); - -sk_sp GPUSurfaceGLSkia::MakeGLContext( - GPUSurfaceGLDelegate* delegate) { - auto context_switch = delegate->GLContextMakeCurrent(); - if (!context_switch->GetResult()) { - FML_LOG(ERROR) - << "Could not make the context current to set up the Gr context."; - return nullptr; - } - - const auto options = - MakeDefaultContextOptions(ContextType::kRender, GrBackendApi::kOpenGL); - - auto context = GrDirectContext::MakeGL(delegate->GetGLInterface(), options); - - if (!context) { - FML_LOG(ERROR) << "Failed to set up Skia Gr context."; - return nullptr; - } - - context->setResourceCacheLimit(kGrCacheMaxByteSize); - - PersistentCache::GetCacheForProcess()->PrecompileKnownSkSLs(context.get()); - - return context; -} - GPUSurfaceGLSkia::GPUSurfaceGLSkia(const sk_sp& gr_context, GPUSurfaceGLDelegate* delegate, bool render_to_surface) diff --git a/shell/gpu/gpu_surface_gl_skia.h b/shell/gpu/gpu_surface_gl_skia.h index f242f76f02662..3d340ce4171c4 100644 --- a/shell/gpu/gpu_surface_gl_skia.h +++ b/shell/gpu/gpu_surface_gl_skia.h @@ -22,8 +22,6 @@ namespace flutter { class GPUSurfaceGLSkia : public Surface { public: - static sk_sp MakeGLContext(GPUSurfaceGLDelegate* delegate); - GPUSurfaceGLSkia(const sk_sp& gr_context, GPUSurfaceGLDelegate* delegate, bool render_to_surface); diff --git a/shell/platform/android/android_surface_gl_skia.cc b/shell/platform/android/android_surface_gl_skia.cc index 3fc3e60c1d6ea..f266c2daa7217 100644 --- a/shell/platform/android/android_surface_gl_skia.cc +++ b/shell/platform/android/android_surface_gl_skia.cc @@ -58,7 +58,7 @@ sk_sp AndroidSurfaceGLSkia::UseExistingMainContextOrCreate( sk_sp main_skia_context = GLContextPtr()->GetMainSkiaContext(); if (!main_skia_context) { - main_skia_context = GPUSurfaceGLSkia::MakeGLContext(this); + main_skia_context = GPUStudioGLSkia::MakeGLContext(this); GLContextPtr()->SetMainSkiaContext(main_skia_context); } FML_DCHECK(GLContextPtr()->GetMainSkiaContext() == main_skia_context); @@ -234,7 +234,7 @@ std::unique_ptr AndroidSurfaceGLSkia::CreateSnapshotStudio() { sk_sp main_skia_context = GLContextPtr()->GetMainSkiaContext(); if (!main_skia_context) { - main_skia_context = GPUSurfaceGLSkia::MakeGLContext(this); + main_skia_context = GPUStudioGLSkia::MakeGLContext(this); GLContextPtr()->SetMainSkiaContext(main_skia_context); } diff --git a/shell/platform/embedder/embedder_studio_gl.cc b/shell/platform/embedder/embedder_studio_gl.cc index c749c31515e92..36885f9400866 100644 --- a/shell/platform/embedder/embedder_studio_gl.cc +++ b/shell/platform/embedder/embedder_studio_gl.cc @@ -147,7 +147,7 @@ sk_sp EmbedderStudioGL::CreateResourceContext() const { sk_sp EmbedderStudioGL::MainContext() { if (!main_context_) { - main_context_ = GPUSurfaceGLSkia::MakeGLContext(this); + main_context_ = GPUStudioGLSkia::MakeGLContext(this); } return main_context_; } From a9ff44c063cf1cb6e27d44426f8f90afa4639a03 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 27 Mar 2023 23:53:08 +0000 Subject: [PATCH 067/147] Remove config --- shell/platform/embedder/embedder.cc | 1 - shell/platform/embedder/embedder.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 18fb98c1c8f75..17a754fb0f577 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -2049,7 +2049,6 @@ FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine) FLUTTER_EXPORT FlutterEngineResult FlutterEngineAddRenderSurface( FLUTTER_API_SYMBOL(FlutterEngine) engine, - const FlutterRendererConfig* config, void* user_data, int64_t view_id) { if (engine == nullptr) { diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index d296fab874add..06f8217a93f14 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -2246,7 +2246,6 @@ FlutterEngineResult FlutterEngineRunInitialized( FLUTTER_EXPORT FlutterEngineResult FlutterEngineAddRenderSurface( FLUTTER_API_SYMBOL(FlutterEngine) engine, - const FlutterRendererConfig* config, void* user_data, int64_t view_id); @@ -2826,7 +2825,6 @@ typedef FlutterEngineResult (*FlutterEngineRunInitializedFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine); typedef FlutterEngineResult (*FlutterEngineAddRenderSurfaceFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine, - const FlutterRendererConfig* config, void* user_data, int64_t view_id); typedef FlutterEngineResult (*FlutterEngineSendWindowMetricsEventFnPtr)( From ceb0e1177a35bf1dee3ede9993732c8d291c7bd8 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 27 Mar 2023 18:07:14 -0700 Subject: [PATCH 068/147] Add view id to many --- lib/ui/dart_ui.cc | 2 +- lib/ui/window.dart | 6 ++--- lib/ui/window/platform_configuration.cc | 5 ++-- lib/ui/window/platform_configuration.h | 4 ++-- runtime/runtime_controller.cc | 4 ++-- runtime/runtime_controller.h | 2 +- runtime/runtime_delegate.h | 3 ++- shell/common/animator.cc | 5 ++-- shell/common/animator.h | 2 +- shell/common/animator_unittests.cc | 6 +++-- shell/common/engine.cc | 5 ++-- shell/common/engine.h | 3 ++- shell/common/engine_unittests.cc | 2 +- shell/common/pipeline.h | 7 ++++-- shell/common/rasterizer.cc | 5 ++-- shell/common/rasterizer.h | 1 + shell/common/rasterizer_unittests.cc | 31 ++++++++++++++----------- shell/common/shell_test.cc | 4 +++- 18 files changed, 57 insertions(+), 40 deletions(-) diff --git a/lib/ui/dart_ui.cc b/lib/ui/dart_ui.cc index 57242c11ac9aa..e26aeb307fd55 100644 --- a/lib/ui/dart_ui.cc +++ b/lib/ui/dart_ui.cc @@ -98,7 +98,7 @@ typedef CanvasPath Path; V(PlatformConfigurationNativeApi::ImplicitViewEnabled, 0) \ V(PlatformConfigurationNativeApi::DefaultRouteName, 0) \ V(PlatformConfigurationNativeApi::ScheduleFrame, 0) \ - V(PlatformConfigurationNativeApi::Render, 1) \ + V(PlatformConfigurationNativeApi::Render, 2) \ V(PlatformConfigurationNativeApi::UpdateSemantics, 1) \ V(PlatformConfigurationNativeApi::SetNeedsReportTimings, 1) \ V(PlatformConfigurationNativeApi::SetIsolateDebugName, 1) \ diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 19f40c6e1ee6a..44389d09f8f39 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -289,10 +289,10 @@ class FlutterView { /// scheduling of frames. /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. - void render(Scene scene) => _render(scene); + void render(Scene scene) => _render(viewId == 0 ? 0 : viewId.hashCode, scene); - @Native)>(symbol: 'PlatformConfigurationNativeApi::Render') - external static void _render(Scene scene); + @Native)>(symbol: 'PlatformConfigurationNativeApi::Render') + external static void _render(int viewId, Scene scene); /// Change the retained semantics data about this [FlutterView]. /// diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 4e6e583745d4d..25124cd9d3458 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -280,9 +280,10 @@ void PlatformConfiguration::CompletePlatformMessageResponse( response->Complete(std::make_unique(std::move(data))); } -void PlatformConfigurationNativeApi::Render(Scene* scene) { +void PlatformConfigurationNativeApi::Render(int64_t view_id, Scene* scene) { UIDartState::ThrowIfUIOperationsProhibited(); - UIDartState::Current()->platform_configuration()->client()->Render(scene); + UIDartState::Current()->platform_configuration()->client()->Render(view_id, + scene); } void PlatformConfigurationNativeApi::SetNeedsReportTimings(bool value) { diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index 381b8e02e1072..57c939647715f 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -78,7 +78,7 @@ class PlatformConfigurationClient { /// @brief Updates the client's rendering on the GPU with the newly /// provided Scene. /// - virtual void Render(Scene* scene) = 0; + virtual void Render(int64_t view_id, Scene* scene) = 0; //-------------------------------------------------------------------------- /// @brief Receives an updated semantics tree from the Framework. @@ -485,7 +485,7 @@ class PlatformConfigurationNativeApi { static void ScheduleFrame(); - static void Render(Scene* scene); + static void Render(int64_t view_id, Scene* scene); static void UpdateSemantics(SemanticsUpdate* update); diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 996cc381149bf..0b934f635da1f 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -314,8 +314,8 @@ void RuntimeController::ScheduleFrame() { } // |PlatformConfigurationClient| -void RuntimeController::Render(Scene* scene) { - client_.Render(scene->takeLayerTree()); +void RuntimeController::Render(int64_t view_id, Scene* scene) { + client_.Render(view_id, scene->takeLayerTree()); } // |PlatformConfigurationClient| diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index b3fc828f8264c..0312294f01732 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -622,7 +622,7 @@ class RuntimeController : public PlatformConfigurationClient { void ScheduleFrame() override; // |PlatformConfigurationClient| - void Render(Scene* scene) override; + void Render(int64_t view_id, Scene* scene) override; // |PlatformConfigurationClient| void UpdateSemantics(SemanticsUpdate* update) override; diff --git a/runtime/runtime_delegate.h b/runtime/runtime_delegate.h index 0fbb43391e113..c34f99ef6cc16 100644 --- a/runtime/runtime_delegate.h +++ b/runtime/runtime_delegate.h @@ -27,7 +27,8 @@ class RuntimeDelegate { virtual void ScheduleFrame(bool regenerate_layer_tree = true) = 0; - virtual void Render(std::shared_ptr layer_tree) = 0; + virtual void Render(int64_t view_id, + std::shared_ptr layer_tree) = 0; virtual void UpdateSemantics(SemanticsNodeUpdates update, CustomAccessibilityActionUpdates actions) = 0; diff --git a/shell/common/animator.cc b/shell/common/animator.cc index 0b969c2ccbc0b..fa1a36d2aae79 100644 --- a/shell/common/animator.cc +++ b/shell/common/animator.cc @@ -128,7 +128,8 @@ void Animator::BeginFrame( } } -void Animator::Render(std::shared_ptr layer_tree) { +void Animator::Render(int64_t view_id, + std::shared_ptr layer_tree) { has_rendered_ = true; last_layer_tree_size_ = layer_tree->frame_size(); @@ -148,7 +149,7 @@ void Animator::Render(std::shared_ptr layer_tree) { frame_timings_recorder_->GetVsyncTargetTime()); auto layer_tree_item = std::make_unique( - std::move(layer_tree), std::move(frame_timings_recorder_)); + view_id, std::move(layer_tree), std::move(frame_timings_recorder_)); // Commit the pending continuation. PipelineProduceResult result = producer_continuation_.Complete(std::move(layer_tree_item)); diff --git a/shell/common/animator.h b/shell/common/animator.h index dfacb4a786bf3..a89a94b8fcc32 100644 --- a/shell/common/animator.h +++ b/shell/common/animator.h @@ -54,7 +54,7 @@ class Animator final { void RequestFrame(bool regenerate_layer_tree = true); - void Render(std::shared_ptr layer_tree); + void Render(int64_t view_id, std::shared_ptr layer_tree); const std::weak_ptr GetVsyncWaiter() const; diff --git a/shell/common/animator_unittests.cc b/shell/common/animator_unittests.cc index 05c728c57ae16..8323cd49033a0 100644 --- a/shell/common/animator_unittests.cc +++ b/shell/common/animator_unittests.cc @@ -23,6 +23,8 @@ namespace flutter { namespace testing { +constexpr int64_t kDefaultViewId = 0; + class FakeAnimatorDelegate : public Animator::Delegate { public: MOCK_METHOD2(OnAnimatorBeginFrame, @@ -157,7 +159,7 @@ TEST_F(ShellTest, AnimatorDoesNotNotifyIdleBeforeRender) { ASSERT_FALSE(delegate.notify_idle_called_); auto layer_tree = std::make_shared(SkISize::Make(600, 800), 1.0); - animator->Render(std::move(layer_tree)); + animator->Render(kDefaultViewId, std::move(layer_tree)); task_runners.GetPlatformTaskRunner()->PostTask(flush_vsync_task); }, // See kNotifyIdleTaskWaitTime in animator.cc. @@ -240,7 +242,7 @@ TEST_F(ShellTest, AnimatorDoesNotNotifyDelegateIfPipelineIsNotEmpty) { PostTaskSync(task_runners.GetUITaskRunner(), [&] { auto layer_tree = std::make_shared(SkISize::Make(600, 800), 1.0); - animator->Render(std::move(layer_tree)); + animator->Render(kDefaultViewId, std::move(layer_tree)); }); } diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 522ee89a52a7b..a141d09cd1fa7 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -450,7 +450,8 @@ void Engine::ScheduleFrame(bool regenerate_layer_tree) { animator_->RequestFrame(regenerate_layer_tree); } -void Engine::Render(std::shared_ptr layer_tree) { +void Engine::Render(int64_t view_id, + std::shared_ptr layer_tree) { if (!layer_tree) { return; } @@ -461,7 +462,7 @@ void Engine::Render(std::shared_ptr layer_tree) { return; } - animator_->Render(std::move(layer_tree)); + animator_->Render(view_id, std::move(layer_tree)); } void Engine::UpdateSemantics(SemanticsNodeUpdates update, diff --git a/shell/common/engine.h b/shell/common/engine.h index c6ff37eae37a5..8c5a07379f86b 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -895,7 +895,8 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { std::string DefaultRouteName() override; // |RuntimeDelegate| - void Render(std::shared_ptr layer_tree) override; + void Render(int64_t view_id, + std::shared_ptr layer_tree) override; // |RuntimeDelegate| void UpdateSemantics(SemanticsNodeUpdates update, diff --git a/shell/common/engine_unittests.cc b/shell/common/engine_unittests.cc index 3bfdd000c37eb..85c4656506485 100644 --- a/shell/common/engine_unittests.cc +++ b/shell/common/engine_unittests.cc @@ -51,7 +51,7 @@ class MockRuntimeDelegate : public RuntimeDelegate { MOCK_METHOD0(ImplicitViewEnabled, bool()); MOCK_METHOD0(DefaultRouteName, std::string()); MOCK_METHOD1(ScheduleFrame, void(bool)); - MOCK_METHOD1(Render, void(std::shared_ptr)); + MOCK_METHOD2(Render, void(int64_t, std::shared_ptr)); MOCK_METHOD2(UpdateSemantics, void(SemanticsNodeUpdates, CustomAccessibilityActionUpdates)); MOCK_METHOD1(HandlePlatformMessage, void(std::unique_ptr)); diff --git a/shell/common/pipeline.h b/shell/common/pipeline.h index 1de4498e7f08c..f58d57ab263f8 100644 --- a/shell/common/pipeline.h +++ b/shell/common/pipeline.h @@ -252,10 +252,13 @@ class Pipeline { }; struct LayerTreeItem { - LayerTreeItem(std::shared_ptr layer_tree, + LayerTreeItem(int64_t view_id, + std::shared_ptr layer_tree, std::unique_ptr frame_timings_recorder) - : layer_tree(std::move(layer_tree)), + : view_id(view_id), + layer_tree(std::move(layer_tree)), frame_timings_recorder(std::move(frame_timings_recorder)) {} + int64_t view_id; std::shared_ptr layer_tree; std::unique_ptr frame_timings_recorder; }; diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 4de7e9038613d..c222b1837b1a5 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -226,11 +226,10 @@ RasterStatus Rasterizer::Draw( std::shared_ptr layer_tree = std::move(item->layer_tree); std::unique_ptr frame_timings_recorder = std::move(item->frame_timings_recorder); + int64_t view_id = item->view_id; if (discard_callback(*layer_tree.get())) { draw_result.raster_status = RasterStatus::kDiscarded; } else { - // TODO(dkwingsmt) - int64_t view_id = 0ll; draw_result = DoDraw(view_id, std::move(frame_timings_recorder), std::move(layer_tree)); } @@ -246,6 +245,7 @@ RasterStatus Rasterizer::Draw( bool should_resubmit_frame = ShouldResubmitFrame(draw_result.raster_status); if (should_resubmit_frame) { auto resubmitted_layer_tree_item = std::make_unique( + draw_result.resubmitted_view_id, std::move(draw_result.resubmitted_layer_tree), std::move(draw_result.resubmitted_recorder)); auto front_continuation = pipeline->ProduceIfEmpty(); @@ -431,6 +431,7 @@ Rasterizer::DoDrawResult Rasterizer::DoDraw( } else if (ShouldResubmitFrame(raster_status)) { return DoDrawResult{ .raster_status = raster_status, + .resubmitted_view_id = view_id, .resubmitted_layer_tree = std::move(layer_tree), .resubmitted_recorder = frame_timings_recorder->CloneUntil( FrameTimingsRecorder::State::kBuildEnd), diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 6ff0d74dff670..c0047902c5660 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -485,6 +485,7 @@ class Rasterizer final : public SnapshotDelegate, private: struct DoDrawResult { RasterStatus raster_status = RasterStatus::kFailed; + int64_t resubmitted_view_id; // Set when we need attempt to rasterize the layer tree again. This // layer_tree has not successfully rasterized. This can happen due to the // change in the thread configuration. This will be inserted to the front of diff --git a/shell/common/rasterizer_unittests.cc b/shell/common/rasterizer_unittests.cc index b923611896ee0..f54e52644fc56 100644 --- a/shell/common/rasterizer_unittests.cc +++ b/shell/common/rasterizer_unittests.cc @@ -203,7 +203,7 @@ TEST(RasterizerTest, auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder()); + kDefaultViewId, std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -270,7 +270,7 @@ TEST( auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder()); + kDefaultViewId, std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -343,7 +343,7 @@ TEST( auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder()); + kDefaultViewId, std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -419,7 +419,7 @@ TEST(RasterizerTest, auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder()); + kDefaultViewId, std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -468,7 +468,7 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNoSurfaceIsSet) { auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder()); + kDefaultViewId, std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -525,7 +525,7 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNotUsedThisFrame) { auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder()); + kDefaultViewId, std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -631,7 +631,7 @@ TEST(RasterizerTest, auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder()); + kDefaultViewId, std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -690,7 +690,7 @@ TEST( auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder()); + kDefaultViewId, std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -749,7 +749,7 @@ TEST( auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder()); + kDefaultViewId, std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -807,7 +807,7 @@ TEST( auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder()); + kDefaultViewId, std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -864,7 +864,7 @@ TEST( auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder()); + kDefaultViewId, std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -945,7 +945,8 @@ TEST(RasterizerTest, std::make_shared(/*frame_size=*/SkISize(), /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(timestamps[i])); + kDefaultViewId, std::move(layer_tree), + CreateFinishedBuildRecorder(timestamps[i])); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -1120,7 +1121,8 @@ TEST(RasterizerTest, presentationTimeSetWhenVsyncTargetInFuture) { std::make_shared(/*frame_size=*/SkISize(), /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(timestamps[i])); + kDefaultViewId, std::move(layer_tree), + CreateFinishedBuildRecorder(timestamps[i])); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); @@ -1201,7 +1203,8 @@ TEST(RasterizerTest, presentationTimeNotSetWhenVsyncTargetInPast) { auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( - std::move(layer_tree), CreateFinishedBuildRecorder(first_timestamp)); + kDefaultViewId, std::move(layer_tree), + CreateFinishedBuildRecorder(first_timestamp)); PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index a799e6bcb4811..d5fab1f5be7b9 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -20,6 +20,8 @@ namespace flutter { namespace testing { +constexpr int64_t kDefaultViewId = 0; + ShellTest::ShellTest() : thread_host_("io.flutter.test." + GetCurrentTestName() + ".", ThreadHost::Type::Platform | ThreadHost::Type::IO | @@ -208,7 +210,7 @@ void ShellTest::PumpOneFrame(Shell* shell, if (builder) { builder(root_layer); } - runtime_delegate->Render(std::move(layer_tree)); + runtime_delegate->Render(kDefaultViewId, std::move(layer_tree)); latch.Signal(); }); latch.Wait(); From 2c69edaf6979f3896545dfa2f2c81e60c07fab9d Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 27 Mar 2023 21:59:55 -0700 Subject: [PATCH 069/147] Fix ios --- shell/common/rasterizer.cc | 3 +-- shell/common/rasterizer.h | 3 ++- shell/platform/darwin/ios/ios_surface_metal_impeller.mm | 9 ++++----- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index c222b1837b1a5..738f76cdb18fa 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -550,7 +550,6 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( flutter::LayerTree* layer_tree, SurfaceRecord* surface_record) { Surface* surface = surface_record->surface.get(); - flutter::LayerTree* last_layer_tree = surface_record->last_tree.get(); FML_DCHECK(surface); compositor_context_->ui_time().SetLapTime( @@ -618,7 +617,7 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( damage = std::make_unique(); if (frame->framebuffer_info().existing_damage && !force_full_repaint) { - damage->SetPreviousLayerTree(last_layer_tree); + damage->SetPreviousLayerTree(surface_record->last_tree.get()); damage->AddAdditionalDamage(*frame->framebuffer_info().existing_damage); damage->SetClipAlignment( frame->framebuffer_info().horizontal_clip_alignment, diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index c0047902c5660..27be24a0abb14 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -575,7 +575,8 @@ class Rasterizer final : public SnapshotDelegate, flutter::LayerTree* layer_tree, SurfaceRecord* surface_record); - Screenshot ScreenshotLayerTree(ScreenshotType type, + Screenshot ScreenshotLayerTree(view_id, + ScreenshotType type, bool base64_encode, SurfaceRecord& surface_record); diff --git a/shell/platform/darwin/ios/ios_surface_metal_impeller.mm b/shell/platform/darwin/ios/ios_surface_metal_impeller.mm index 67be44b0f3fd6..e48623e943135 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_impeller.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_impeller.mm @@ -47,11 +47,10 @@ // |IOSSurface| std::unique_ptr IOSSurfaceMetalImpeller::CreateGPUStudio(GrDirectContext*) { - auto context = std::make_shared( - impeller_context_, - InferOffscreenLayerPixelFormat(FromMTLPixelFormat(layer_.get().pixelFormat))); - return std::make_unique(this, // - context // + impeller_context_->UpdateOffscreenLayerPixelFormat( + InferOffscreenLayerPixelFormat(impeller::FromMTLPixelFormat(layer_.get().pixelFormat))); + return std::make_unique(this, // + impeller_context_ // ); } From 31c1068315b6b8c1e3133d20877198d6ccc471e5 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 27 Mar 2023 22:07:24 -0700 Subject: [PATCH 070/147] Fix compile --- shell/common/rasterizer.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 27be24a0abb14..c0047902c5660 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -575,8 +575,7 @@ class Rasterizer final : public SnapshotDelegate, flutter::LayerTree* layer_tree, SurfaceRecord* surface_record); - Screenshot ScreenshotLayerTree(view_id, - ScreenshotType type, + Screenshot ScreenshotLayerTree(ScreenshotType type, bool base64_encode, SurfaceRecord& surface_record); From 45109abaeb868b2c7492073d131a7a14b7087aeb Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 29 Mar 2023 15:37:47 -0700 Subject: [PATCH 071/147] Add view_id to acquireFrame --- flow/surface.h | 3 +- shell/common/rasterizer.cc | 8 +++-- shell/common/rasterizer.h | 6 ++-- shell/common/rasterizer_unittests.cc | 31 ++++++++++--------- .../common/shell_test_platform_view_vulkan.cc | 1 + .../common/shell_test_platform_view_vulkan.h | 3 +- shell/common/shell_unittests.cc | 5 +-- shell/gpu/gpu_surface_gl_impeller.cc | 1 + shell/gpu/gpu_surface_gl_impeller.h | 3 +- shell/gpu/gpu_surface_gl_skia.cc | 1 + shell/gpu/gpu_surface_gl_skia.h | 3 +- shell/gpu/gpu_surface_metal_impeller.h | 3 +- shell/gpu/gpu_surface_metal_impeller.mm | 3 +- shell/gpu/gpu_surface_metal_skia.h | 4 ++- shell/gpu/gpu_surface_metal_skia.mm | 6 ++-- shell/gpu/gpu_surface_software.cc | 1 + shell/gpu/gpu_surface_software.h | 3 +- shell/gpu/gpu_surface_vulkan.cc | 1 + shell/gpu/gpu_surface_vulkan.h | 3 +- shell/gpu/gpu_surface_vulkan_impeller.cc | 1 + shell/gpu/gpu_surface_vulkan_impeller.h | 3 +- .../external_view_embedder.cc | 7 ++++- .../external_view_embedder.h | 1 + .../external_view_embedder_unittests.cc | 18 ++++++----- .../framework/Source/FlutterPlatformViews.mm | 5 ++- .../Source/FlutterPlatformViewsTest.mm | 25 ++++++++------- .../Source/FlutterPlatformViews_Internal.h | 2 ++ .../darwin/ios/ios_external_view_embedder.mm | 6 +++- 28 files changed, 102 insertions(+), 55 deletions(-) diff --git a/flow/surface.h b/flow/surface.h index e594d08a01232..947a6b8603a10 100644 --- a/flow/surface.h +++ b/flow/surface.h @@ -35,7 +35,8 @@ class Surface { virtual bool IsValid() = 0; - virtual std::unique_ptr AcquireFrame(const SkISize& size) = 0; + virtual std::unique_ptr AcquireFrame(int64_t view_id, + const SkISize& size) = 0; virtual SkMatrix GetRootTransformation() const = 0; diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 738f76cdb18fa..5e6bcc53d7e01 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -144,7 +144,10 @@ void Rasterizer::NotifyLowMemoryWarning() const { void Rasterizer::AddSurface(int64_t view_id, std::unique_ptr surface) { bool insertion_happened = - surfaces_.try_emplace(view_id, std::move(surface)).second; + surfaces_ + .try_emplace(/* map key=*/view_id, /*constructor args:*/ view_id, + std::move(surface)) + .second; if (!insertion_happened) { FML_DLOG(INFO) << "Rasterizer::AddSurface called with an existing view ID " << view_id << "."; @@ -571,7 +574,8 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( // // Deleting a surface also clears the GL context. Therefore, acquire the // frame after calling `BeginFrame` as this operation resets the GL context. - auto frame = surface->AcquireFrame(layer_tree->frame_size()); + auto frame = + surface->AcquireFrame(surface_record->view_id, layer_tree->frame_size()); if (frame == nullptr) { frame_timings_recorder.RecordRasterEnd( &compositor_context_->raster_cache()); diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index c0047902c5660..5c4bb2639a48c 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -495,8 +495,10 @@ class Rasterizer final : public SnapshotDelegate, }; struct SurfaceRecord { - explicit SurfaceRecord(std::unique_ptr surface) - : surface(std::move(surface)) {} + SurfaceRecord(int64_t view_id, std::unique_ptr surface) + : view_id(view_id), surface(std::move(surface)) {} + + int64_t view_id; std::unique_ptr surface; // This is the information for the last successfully drawing. diff --git a/shell/common/rasterizer_unittests.cc b/shell/common/rasterizer_unittests.cc index f54e52644fc56..b6efdf9df19f3 100644 --- a/shell/common/rasterizer_unittests.cc +++ b/shell/common/rasterizer_unittests.cc @@ -55,8 +55,9 @@ class MockStudio : public Studio { class MockSurface : public Surface { public: MOCK_METHOD0(IsValid, bool()); - MOCK_METHOD1(AcquireFrame, - std::unique_ptr(const SkISize& size)); + MOCK_METHOD2(AcquireFrame, + std::unique_ptr(int64_t view_id, + const SkISize& size)); MOCK_CONST_METHOD0(GetRootTransformation, SkMatrix()); MOCK_METHOD0(GetContext, GrDirectContext*()); MOCK_METHOD0(GetExternalViewEmbedder, ExternalViewEmbedder*()); @@ -176,7 +177,7 @@ TEST(RasterizerTest, /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); - EXPECT_CALL(*surface, AcquireFrame(SkISize())) + EXPECT_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame)))); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); @@ -247,7 +248,7 @@ TEST( /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); - EXPECT_CALL(*surface, AcquireFrame(SkISize())) + EXPECT_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame)))); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); @@ -319,7 +320,7 @@ TEST( /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); - EXPECT_CALL(*surface, AcquireFrame(SkISize())) + EXPECT_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame)))); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); @@ -394,7 +395,7 @@ TEST(RasterizerTest, EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()) .WillRepeatedly(Return(true)); // Prepare two frames for Draw() and DrawLastLayerTree(). - EXPECT_CALL(*surface, AcquireFrame(SkISize())) + EXPECT_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame1)))) .WillOnce(Return(ByMove(std::move(surface_frame2)))); EXPECT_CALL(*studio, MakeRenderContextCurrent()) @@ -618,7 +619,7 @@ TEST(RasterizerTest, ON_CALL(delegate, GetIsGpuDisabledSyncSwitch()) .WillByDefault(Return(is_gpu_disabled_sync_switch)); EXPECT_CALL(delegate, GetIsGpuDisabledSyncSwitch()).Times(0); - EXPECT_CALL(*surface, AcquireFrame(SkISize())) + EXPECT_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame)))); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); @@ -677,7 +678,7 @@ TEST( ON_CALL(delegate, GetIsGpuDisabledSyncSwitch()) .WillByDefault(Return(is_gpu_disabled_sync_switch)); EXPECT_CALL(delegate, GetIsGpuDisabledSyncSwitch()).Times(0); - EXPECT_CALL(*surface, AcquireFrame(SkISize())) + EXPECT_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame)))); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); @@ -736,7 +737,7 @@ TEST( EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(false)); EXPECT_CALL(delegate, GetIsGpuDisabledSyncSwitch()) .WillOnce(Return(is_gpu_disabled_sync_switch)); - EXPECT_CALL(*surface, AcquireFrame(SkISize())) + EXPECT_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame)))); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); @@ -795,7 +796,7 @@ TEST( EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(false)); EXPECT_CALL(delegate, GetIsGpuDisabledSyncSwitch()) .WillOnce(Return(is_gpu_disabled_sync_switch)); - EXPECT_CALL(*surface, AcquireFrame(SkISize())).Times(0); + EXPECT_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())).Times(0); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); @@ -851,9 +852,9 @@ TEST( std::make_shared(false); ON_CALL(delegate, GetIsGpuDisabledSyncSwitch()) .WillByDefault(Return(is_gpu_disabled_sync_switch)); - ON_CALL(*surface, AcquireFrame(SkISize())) + ON_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) .WillByDefault(::testing::Invoke([] { return nullptr; })); - EXPECT_CALL(*surface, AcquireFrame(SkISize())); + EXPECT_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio)); @@ -904,7 +905,7 @@ TEST(RasterizerTest, auto surface = std::make_unique>(); EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()) .WillRepeatedly(Return(true)); - ON_CALL(*surface, AcquireFrame(SkISize())) + ON_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) .WillByDefault(::testing::Invoke([] { SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; @@ -1089,7 +1090,7 @@ TEST(RasterizerTest, presentationTimeSetWhenVsyncTargetInFuture) { auto studio = std::make_unique>(); auto surface = std::make_unique(); ON_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillByDefault(Return(true)); - ON_CALL(*surface, AcquireFrame(SkISize())) + ON_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) .WillByDefault(::testing::Invoke([&] { SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; @@ -1175,7 +1176,7 @@ TEST(RasterizerTest, presentationTimeNotSetWhenVsyncTargetInPast) { auto studio = std::make_unique>(); auto surface = std::make_unique(); ON_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillByDefault(Return(true)); - ON_CALL(*surface, AcquireFrame(SkISize())) + ON_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) .WillByDefault(::testing::Invoke([&] { SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index 72d8016b33afe..7c4c0ac62f38f 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -208,6 +208,7 @@ bool ShellTestPlatformViewVulkan::OffScreenSurface::IsValid() { std::unique_ptr ShellTestPlatformViewVulkan::OffScreenSurface::AcquireFrame( + int64_t view_id, const SkISize& size) { auto image_info = SkImageInfo::Make(size, SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kOpaque_SkAlphaType); diff --git a/shell/common/shell_test_platform_view_vulkan.h b/shell/common/shell_test_platform_view_vulkan.h index ff144b3202fba..54067aa11de76 100644 --- a/shell/common/shell_test_platform_view_vulkan.h +++ b/shell/common/shell_test_platform_view_vulkan.h @@ -87,7 +87,8 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { bool IsValid() override; // |Surface| - std::unique_ptr AcquireFrame(const SkISize& size) override; + std::unique_ptr AcquireFrame(int64_t view_id, + const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index d36677bdf5a18..2b487ae02b2d9 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -134,8 +134,9 @@ class MockSurface : public Surface { MOCK_METHOD0(GetContext, GrDirectContext*()); - MOCK_METHOD1(AcquireFrame, - std::unique_ptr(const SkISize& size)); + MOCK_METHOD2(AcquireFrame, + std::unique_ptr(int64_t view_id, + const SkISize& size)); MOCK_CONST_METHOD0(GetRootTransformation, SkMatrix()); }; diff --git a/shell/gpu/gpu_surface_gl_impeller.cc b/shell/gpu/gpu_surface_gl_impeller.cc index c7e6a5e039c84..ee6bb0d146e10 100644 --- a/shell/gpu/gpu_surface_gl_impeller.cc +++ b/shell/gpu/gpu_surface_gl_impeller.cc @@ -51,6 +51,7 @@ bool GPUSurfaceGLImpeller::IsValid() { // |Surface| std::unique_ptr GPUSurfaceGLImpeller::AcquireFrame( + int64_t view_id, const SkISize& size) { if (!IsValid()) { FML_LOG(ERROR) << "OpenGL surface was invalid."; diff --git a/shell/gpu/gpu_surface_gl_impeller.h b/shell/gpu/gpu_surface_gl_impeller.h index abfc5dfb32231..aaec9cb13c57d 100644 --- a/shell/gpu/gpu_surface_gl_impeller.h +++ b/shell/gpu/gpu_surface_gl_impeller.h @@ -35,7 +35,8 @@ class GPUSurfaceGLImpeller final : public Surface { fml::WeakPtrFactory weak_factory_; // |Surface| - std::unique_ptr AcquireFrame(const SkISize& size) override; + std::unique_ptr AcquireFrame(int64_t view_id, + const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; diff --git a/shell/gpu/gpu_surface_gl_skia.cc b/shell/gpu/gpu_surface_gl_skia.cc index d828efdced2d0..55997d03dfe89 100644 --- a/shell/gpu/gpu_surface_gl_skia.cc +++ b/shell/gpu/gpu_surface_gl_skia.cc @@ -165,6 +165,7 @@ SkMatrix GPUSurfaceGLSkia::GetRootTransformation() const { // |Surface| std::unique_ptr GPUSurfaceGLSkia::AcquireFrame( + int64_t view_id, const SkISize& size) { if (delegate_ == nullptr) { return nullptr; diff --git a/shell/gpu/gpu_surface_gl_skia.h b/shell/gpu/gpu_surface_gl_skia.h index 3d340ce4171c4..6367c8112584d 100644 --- a/shell/gpu/gpu_surface_gl_skia.h +++ b/shell/gpu/gpu_surface_gl_skia.h @@ -33,7 +33,8 @@ class GPUSurfaceGLSkia : public Surface { bool IsValid() override; // |Surface| - std::unique_ptr AcquireFrame(const SkISize& size) override; + std::unique_ptr AcquireFrame(int64_t view_id, + const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; diff --git a/shell/gpu/gpu_surface_metal_impeller.h b/shell/gpu/gpu_surface_metal_impeller.h index 41340873638bb..d5b18e68ef785 100644 --- a/shell/gpu/gpu_surface_metal_impeller.h +++ b/shell/gpu/gpu_surface_metal_impeller.h @@ -36,7 +36,8 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUSurfaceMetalImpeller : public Surface { fml::scoped_nsprotocol> last_drawable_; // |Surface| - std::unique_ptr AcquireFrame(const SkISize& size) override; + std::unique_ptr AcquireFrame(int64_t view_id, + const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; diff --git a/shell/gpu/gpu_surface_metal_impeller.mm b/shell/gpu/gpu_surface_metal_impeller.mm index 8e3056e390514..3c4206e55c9f4 100644 --- a/shell/gpu/gpu_surface_metal_impeller.mm +++ b/shell/gpu/gpu_surface_metal_impeller.mm @@ -43,7 +43,8 @@ } // |Surface| -std::unique_ptr GPUSurfaceMetalImpeller::AcquireFrame(const SkISize& frame_info) { +std::unique_ptr GPUSurfaceMetalImpeller::AcquireFrame(int64_t view_id, + const SkISize& frame_info) { TRACE_EVENT0("impeller", "GPUSurfaceMetalImpeller::AcquireFrame"); if (!IsValid()) { diff --git a/shell/gpu/gpu_surface_metal_skia.h b/shell/gpu/gpu_surface_metal_skia.h index da6cdc0efa24d..f828a4e9a9938 100644 --- a/shell/gpu/gpu_surface_metal_skia.h +++ b/shell/gpu/gpu_surface_metal_skia.h @@ -46,7 +46,8 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUSurfaceMetalSkia : public Surface { std::map damage_; // |Surface| - std::unique_ptr AcquireFrame(const SkISize& size) override; + std::unique_ptr AcquireFrame(int64_t view_id, + const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; @@ -59,6 +60,7 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUSurfaceMetalSkia : public Surface { const SkISize& frame_info); std::unique_ptr AcquireFrameFromMTLTexture( + int64_t view_id, const SkISize& frame_info); void PrecompileKnownSkSLsIfNecessary(); diff --git a/shell/gpu/gpu_surface_metal_skia.mm b/shell/gpu/gpu_surface_metal_skia.mm index ca8507c5546e8..14f23fbe532c5 100644 --- a/shell/gpu/gpu_surface_metal_skia.mm +++ b/shell/gpu/gpu_surface_metal_skia.mm @@ -77,7 +77,8 @@ } // |Surface| -std::unique_ptr GPUSurfaceMetalSkia::AcquireFrame(const SkISize& frame_size) { +std::unique_ptr GPUSurfaceMetalSkia::AcquireFrame(int64_t view_id, + const SkISize& frame_size) { if (!IsValid()) { FML_LOG(ERROR) << "Metal surface was invalid."; return nullptr; @@ -100,7 +101,7 @@ case MTLRenderTargetType::kCAMetalLayer: return AcquireFrameFromCAMetalLayer(frame_size); case MTLRenderTargetType::kMTLTexture: - return AcquireFrameFromMTLTexture(frame_size); + return AcquireFrameFromMTLTexture(view_id, frame_size); default: FML_CHECK(false) << "Unknown MTLRenderTargetType type."; } @@ -190,6 +191,7 @@ } std::unique_ptr GPUSurfaceMetalSkia::AcquireFrameFromMTLTexture( + int64_t view_id, const SkISize& frame_info) { GPUMTLTextureInfo texture = delegate_->GetMTLTexture(frame_info); id mtl_texture = (id)(texture.texture); diff --git a/shell/gpu/gpu_surface_software.cc b/shell/gpu/gpu_surface_software.cc index efb905ba24ecb..d61cf66dc93c3 100644 --- a/shell/gpu/gpu_surface_software.cc +++ b/shell/gpu/gpu_surface_software.cc @@ -27,6 +27,7 @@ bool GPUSurfaceSoftware::IsValid() { // |Surface| std::unique_ptr GPUSurfaceSoftware::AcquireFrame( + int64_t view_id, const SkISize& logical_size) { SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; diff --git a/shell/gpu/gpu_surface_software.h b/shell/gpu/gpu_surface_software.h index 18bc8d5b41446..2c9a2fc73c373 100644 --- a/shell/gpu/gpu_surface_software.h +++ b/shell/gpu/gpu_surface_software.h @@ -23,7 +23,8 @@ class GPUSurfaceSoftware : public Surface { bool IsValid() override; // |Surface| - std::unique_ptr AcquireFrame(const SkISize& size) override; + std::unique_ptr AcquireFrame(int64_t view_id, + const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; diff --git a/shell/gpu/gpu_surface_vulkan.cc b/shell/gpu/gpu_surface_vulkan.cc index ac5298a17b96b..9e373f746ec9a 100644 --- a/shell/gpu/gpu_surface_vulkan.cc +++ b/shell/gpu/gpu_surface_vulkan.cc @@ -29,6 +29,7 @@ bool GPUSurfaceVulkan::IsValid() { } std::unique_ptr GPUSurfaceVulkan::AcquireFrame( + int64_t view_id, const SkISize& frame_size) { if (!IsValid()) { FML_LOG(ERROR) << "Vulkan surface was invalid."; diff --git a/shell/gpu/gpu_surface_vulkan.h b/shell/gpu/gpu_surface_vulkan.h index c198f7c60e8f4..7b32f7da9bc40 100644 --- a/shell/gpu/gpu_surface_vulkan.h +++ b/shell/gpu/gpu_surface_vulkan.h @@ -40,7 +40,8 @@ class GPUSurfaceVulkan : public Surface { bool IsValid() override; // |Surface| - std::unique_ptr AcquireFrame(const SkISize& size) override; + std::unique_ptr AcquireFrame(int64_t view_id, + const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; diff --git a/shell/gpu/gpu_surface_vulkan_impeller.cc b/shell/gpu/gpu_surface_vulkan_impeller.cc index a2dc1607b77da..a18ffdbea59a1 100644 --- a/shell/gpu/gpu_surface_vulkan_impeller.cc +++ b/shell/gpu/gpu_surface_vulkan_impeller.cc @@ -44,6 +44,7 @@ bool GPUSurfaceVulkanImpeller::IsValid() { // |Surface| std::unique_ptr GPUSurfaceVulkanImpeller::AcquireFrame( + int64_t view_id, const SkISize& size) { if (!IsValid()) { FML_LOG(ERROR) << "Vulkan surface was invalid."; diff --git a/shell/gpu/gpu_surface_vulkan_impeller.h b/shell/gpu/gpu_surface_vulkan_impeller.h index 1514c1368c9fb..d3421fa1d834b 100644 --- a/shell/gpu/gpu_surface_vulkan_impeller.h +++ b/shell/gpu/gpu_surface_vulkan_impeller.h @@ -32,7 +32,8 @@ class GPUSurfaceVulkanImpeller final : public Surface { fml::WeakPtrFactory weak_factory_; // |Surface| - std::unique_ptr AcquireFrame(const SkISize& size) override; + std::unique_ptr AcquireFrame(int64_t view_id, + const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; 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 f1d8af9f6a710..f6f49320e062d 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder.cc @@ -8,6 +8,8 @@ namespace flutter { +constexpr int64_t kFlutterDefaultViewId = 0ll; + AndroidExternalViewEmbedder::AndroidExternalViewEmbedder( const AndroidContext& android_context, std::shared_ptr jni_facade, @@ -171,6 +173,8 @@ void AndroidExternalViewEmbedder::SubmitFrame( } std::unique_ptr frame = CreateSurfaceIfNeeded(context, // + // TODO(dkwingsmt) + kFlutterDefaultViewId, // view_id, // slices_.at(view_id).get(), // overlay->second // @@ -184,6 +188,7 @@ void AndroidExternalViewEmbedder::SubmitFrame( // |ExternalViewEmbedder| std::unique_ptr AndroidExternalViewEmbedder::CreateSurfaceIfNeeded(GrDirectContext* context, + int64_t render_view_id, int64_t view_id, EmbedderViewSlice* slice, const SkRect& rect) { @@ -191,7 +196,7 @@ AndroidExternalViewEmbedder::CreateSurfaceIfNeeded(GrDirectContext* context, context, android_context_, jni_facade_, surface_factory_); std::unique_ptr frame = - layer->surface->AcquireFrame(frame_size_); + layer->surface->AcquireFrame(render_view_id, frame_size_); // Display the overlay surface. If it's already displayed, then it's // just positioned and sized. jni_facade_->FlutterViewDisplayOverlaySurface(layer->id, // 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 03bae5ecb8e1a..8a9ae366d1de2 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.h +++ b/shell/platform/android/external_view_embedder/external_view_embedder.h @@ -138,6 +138,7 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder { // 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 render_view_id, int64_t view_id, EmbedderViewSlice* slice, const SkRect& rect); 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 af933e4251f2d..aacf5ed7aa4f0 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 @@ -24,6 +24,8 @@ namespace flutter { namespace testing { +constexpr int64_t kDefaultViewId = 0ll; + using ::testing::ByMove; using ::testing::Return; @@ -51,7 +53,7 @@ class SurfaceMock : public Surface { MOCK_METHOD(std::unique_ptr, AcquireFrame, - (const SkISize& size), + (int64_t view_id, const SkISize& size), (override)); MOCK_METHOD(SkMatrix, GetRootTransformation, (), (const, override)); @@ -274,7 +276,7 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame) { /*frame_size=*/SkISize::Make(800, 600)); auto surface_mock = std::make_unique(); - EXPECT_CALL(*surface_mock, AcquireFrame(frame_size)) + EXPECT_CALL(*surface_mock, AcquireFrame(kDefaultViewId, frame_size)) .Times(2 /* frames */) .WillOnce(Return(ByMove(std::move(surface_frame_1)))) .WillOnce(Return(ByMove(std::move(surface_frame_2)))); @@ -476,7 +478,7 @@ TEST(AndroidExternalViewEmbedder, OverlayCoverTwoPlatformViews) { /*frame_size=*/SkISize::Make(800, 600)); auto surface_mock = std::make_unique(); - EXPECT_CALL(*surface_mock, AcquireFrame(frame_size)) + EXPECT_CALL(*surface_mock, AcquireFrame(kDefaultViewId, frame_size)) .Times(1 /* frames */) .WillOnce(Return(ByMove(std::move(surface_frame_1)))); @@ -575,7 +577,7 @@ TEST(AndroidExternalViewEmbedder, SubmitFrameOverlayComposition) { /*frame_size=*/SkISize::Make(800, 600)); auto surface_mock = std::make_unique(); - EXPECT_CALL(*surface_mock, AcquireFrame(frame_size)) + EXPECT_CALL(*surface_mock, AcquireFrame(kDefaultViewId, frame_size)) .Times(1 /* frames */) .WillOnce(Return(ByMove(std::move(surface_frame_1)))); @@ -679,7 +681,7 @@ TEST(AndroidExternalViewEmbedder, SubmitFramePlatformViewWithoutAnyOverlay) { /*frame_size=*/SkISize::Make(800, 600)); auto surface_mock = std::make_unique(); - EXPECT_CALL(*surface_mock, AcquireFrame(frame_size)) + EXPECT_CALL(*surface_mock, AcquireFrame(kDefaultViewId, frame_size)) .Times(1 /* frames */) .WillOnce(Return(ByMove(std::move(surface_frame_1)))); @@ -768,7 +770,7 @@ TEST(AndroidExternalViewEmbedder, DestroyOverlayLayersOnSizeChange) { /*frame_size=*/SkISize::Make(800, 600)); auto surface_mock = std::make_unique(); - EXPECT_CALL(*surface_mock, AcquireFrame(frame_size)) + EXPECT_CALL(*surface_mock, AcquireFrame(kDefaultViewId, frame_size)) .WillOnce(Return(ByMove(std::move(surface_frame_1)))); auto android_surface_mock = @@ -857,7 +859,7 @@ TEST(AndroidExternalViewEmbedder, DoesNotDestroyOverlayLayersOnSizeChange) { /*frame_size=*/SkISize::Make(800, 600)); auto surface_mock = std::make_unique(); - EXPECT_CALL(*surface_mock, AcquireFrame(frame_size)) + EXPECT_CALL(*surface_mock, AcquireFrame(kDefaultViewId, frame_size)) .WillOnce(Return(ByMove(std::move(surface_frame_1)))); auto android_surface_mock = @@ -983,7 +985,7 @@ TEST(AndroidExternalViewEmbedder, Teardown) { /*frame_size=*/SkISize::Make(800, 600)); auto surface_mock = std::make_unique(); - EXPECT_CALL(*surface_mock, AcquireFrame(frame_size)) + EXPECT_CALL(*surface_mock, AcquireFrame(kDefaultViewId, frame_size)) .WillOnce(Return(ByMove(std::move(surface_frame_1)))); auto android_surface_mock = diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 5978c1137a4b0..a50ef22c32ab1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -653,6 +653,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, bool FlutterPlatformViewsController::SubmitFrame(GrDirectContext* gr_context, const std::shared_ptr& ios_context, + int64_t render_view_id, std::unique_ptr frame) { TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame"); @@ -727,6 +728,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Get a new host layer. std::shared_ptr layer = GetLayer(gr_context, // ios_context, // + render_view_id, // slice, // joined_rect, // current_platform_view_id, // @@ -798,6 +800,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, std::shared_ptr FlutterPlatformViewsController::GetLayer( GrDirectContext* gr_context, const std::shared_ptr& ios_context, + int64_t render_view_id, EmbedderViewSlice* slice, SkRect rect, int64_t view_id, @@ -824,7 +827,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, overlay_view.accessibilityIdentifier = [NSString stringWithFormat:@"platform_view[%lld].overlay_view[%lld]", view_id, overlay_id]; - std::unique_ptr frame = layer->surface->AcquireFrame(frame_size_); + std::unique_ptr frame = layer->surface->AcquireFrame(render_view_id, frame_size_); // If frame is null, AcquireFrame already printed out an error message. if (!frame) { return layer; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 56bc9ccf5bd32..e9f99672b1f92 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -15,6 +15,9 @@ #import "flutter/shell/platform/darwin/ios/platform_view_ios.h" FLUTTER_ASSERT_NOT_ARC + +constexpr int64_t kDefaultViewId = 0ll; + @class FlutterPlatformViewsTestMockPlatformView; static FlutterPlatformViewsTestMockPlatformView* gMockPlatformView = nil; const float kFloatCompareEpsilon = 0.001; @@ -2225,8 +2228,8 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin nullptr, framebuffer_info, [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return false; }, /*frame_size=*/SkISize::Make(800, 600)); - XCTAssertFalse( - flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + XCTAssertFalse(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, kDefaultViewId, + std::move(mock_surface))); auto embeddedViewParams_2 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); @@ -2236,7 +2239,7 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin nullptr, framebuffer_info, [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, + XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, kDefaultViewId, std::move(mock_surface_submit_true))); } @@ -2404,8 +2407,8 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - XCTAssertTrue( - flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, kDefaultViewId, + std::move(mock_surface))); // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view. UIView* clippingView1 = view1.superview.superview; UIView* clippingView2 = view2.superview.superview; @@ -2431,8 +2434,8 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { std::move(mock_sk_surface), framebuffer_info, [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - XCTAssertTrue( - flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, kDefaultViewId, + std::move(mock_surface))); XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] > [flutterView.subviews indexOfObject:clippingView2], @"The first clipping view should be added after the second clipping view."); @@ -2500,8 +2503,8 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - XCTAssertTrue( - flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, kDefaultViewId, + std::move(mock_surface))); // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view. UIView* clippingView1 = view1.superview.superview; UIView* clippingView2 = view2.superview.superview; @@ -2527,8 +2530,8 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { std::move(mock_sk_surface), framebuffer_info, [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - XCTAssertTrue( - flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, kDefaultViewId, + std::move(mock_surface))); XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] < [flutterView.subviews indexOfObject:clippingView2], @"The first clipping view should be added before the second clipping view."); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 8447d19ca2df9..7315f828f3038 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -253,6 +253,7 @@ class FlutterPlatformViewsController { bool SubmitFrame(GrDirectContext* gr_context, const std::shared_ptr& ios_context, + int64_t render_view_id, std::unique_ptr frame); void OnMethodCall(FlutterMethodCall* call, FlutterResult& result); @@ -318,6 +319,7 @@ class FlutterPlatformViewsController { // the picture on the layer's canvas. std::shared_ptr GetLayer(GrDirectContext* gr_context, const std::shared_ptr& ios_context, + int64_t render_view_id, EmbedderViewSlice* slice, SkRect rect, int64_t view_id, diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm index 4e28b6cfcca82..d93d8cb1ee76d 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.mm +++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm @@ -6,6 +6,8 @@ namespace flutter { +constexpr int64_t kFlutterDefaultViewId = 0ll; + IOSExternalViewEmbedder::IOSExternalViewEmbedder( const std::shared_ptr& platform_views_controller, std::shared_ptr context) @@ -70,7 +72,9 @@ std::unique_ptr frame) { TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::SubmitFrame"); FML_CHECK(platform_views_controller_); - platform_views_controller_->SubmitFrame(context, ios_context_, std::move(frame)); + // TODO(dkwingsmt) + platform_views_controller_->SubmitFrame(context, ios_context_, kFlutterDefaultViewId, + std::move(frame)); TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::DidSubmitFrame"); } From d5deac8fb03aab88ecd8b3548c68c22fbda4163d Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 29 Mar 2023 16:38:48 -0700 Subject: [PATCH 072/147] Remembered texture-view map --- shell/common/shell_test_platform_view_metal.h | 3 ++- .../common/shell_test_platform_view_metal.mm | 3 ++- shell/gpu/gpu_surface_metal_delegate.h | 3 ++- shell/gpu/gpu_surface_metal_skia.mm | 2 +- .../darwin/ios/ios_surface_metal_impeller.h | 2 +- .../darwin/ios/ios_surface_metal_impeller.mm | 3 ++- .../darwin/ios/ios_surface_metal_skia.h | 2 +- .../darwin/ios/ios_surface_metal_skia.mm | 3 ++- .../macos/framework/Source/FlutterRenderer.h | 2 +- .../macos/framework/Source/FlutterRenderer.mm | 27 ++++++++++--------- .../framework/Source/FlutterRendererTest.mm | 12 ++++++--- shell/platform/embedder/embedder.cc | 6 +++-- shell/platform/embedder/embedder.h | 1 + .../platform/embedder/embedder_studio_metal.h | 5 ++-- .../embedder/embedder_studio_metal.mm | 5 ++-- 15 files changed, 47 insertions(+), 32 deletions(-) diff --git a/shell/common/shell_test_platform_view_metal.h b/shell/common/shell_test_platform_view_metal.h index bffea1e20eeff..d29e6c1c04d78 100644 --- a/shell/common/shell_test_platform_view_metal.h +++ b/shell/common/shell_test_platform_view_metal.h @@ -61,7 +61,8 @@ class ShellTestPlatformViewMetal final : public ShellTestPlatformView, bool PresentDrawable(GrMTLHandle drawable) const override; // |GPUSurfaceMetalDelegate| - GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_info) const override; + GPUMTLTextureInfo GetMTLTexture(int64_t view_id, + const SkISize& frame_info) const override; // |GPUSurfaceMetalDelegate| bool PresentTexture(GPUMTLTextureInfo texture) const override; diff --git a/shell/common/shell_test_platform_view_metal.mm b/shell/common/shell_test_platform_view_metal.mm index f9c8deb131c99..8fcb895af71d7 100644 --- a/shell/common/shell_test_platform_view_metal.mm +++ b/shell/common/shell_test_platform_view_metal.mm @@ -120,7 +120,8 @@ GPUMTLTextureInfo offscreen_texture_info() const { } // |GPUSurfaceMetalDelegate| -GPUMTLTextureInfo ShellTestPlatformViewMetal::GetMTLTexture(const SkISize& frame_info) const { +GPUMTLTextureInfo ShellTestPlatformViewMetal::GetMTLTexture(int64_t view_id, + const SkISize& frame_info) const { return metal_context_->offscreen_texture_info(); } diff --git a/shell/gpu/gpu_surface_metal_delegate.h b/shell/gpu/gpu_surface_metal_delegate.h index 0136dc71d785b..aff58f14da308 100644 --- a/shell/gpu/gpu_surface_metal_delegate.h +++ b/shell/gpu/gpu_surface_metal_delegate.h @@ -91,7 +91,8 @@ class GPUSurfaceMetalDelegate { /// @brief Returns the handle to the MTLTexture to render to. This is only /// called when the specified render target type is `kMTLTexture`. /// - virtual GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_info) const = 0; + virtual GPUMTLTextureInfo GetMTLTexture(int64_t view_id, + const SkISize& frame_info) const = 0; //------------------------------------------------------------------------------ /// @brief Presents the texture with `texture_id` to the "screen". diff --git a/shell/gpu/gpu_surface_metal_skia.mm b/shell/gpu/gpu_surface_metal_skia.mm index 14f23fbe532c5..6191e4cf96ff9 100644 --- a/shell/gpu/gpu_surface_metal_skia.mm +++ b/shell/gpu/gpu_surface_metal_skia.mm @@ -193,7 +193,7 @@ std::unique_ptr GPUSurfaceMetalSkia::AcquireFrameFromMTLTexture( int64_t view_id, const SkISize& frame_info) { - GPUMTLTextureInfo texture = delegate_->GetMTLTexture(frame_info); + GPUMTLTextureInfo texture = delegate_->GetMTLTexture(view_id, frame_info); id mtl_texture = (id)(texture.texture); if (!mtl_texture) { diff --git a/shell/platform/darwin/ios/ios_surface_metal_impeller.h b/shell/platform/darwin/ios/ios_surface_metal_impeller.h index 9f29a9cc68dc3..88abe8fd82b24 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_impeller.h +++ b/shell/platform/darwin/ios/ios_surface_metal_impeller.h @@ -51,7 +51,7 @@ class SK_API_AVAILABLE_CA_METAL_LAYER IOSSurfaceMetalImpeller final bool PresentDrawable(GrMTLHandle drawable) const override; // |GPUSurfaceMetalDelegate| - GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_info) const override; + GPUMTLTextureInfo GetMTLTexture(int64_t view_id, const SkISize& frame_info) const override; // |GPUSurfaceMetalDelegate| bool PresentTexture(GPUMTLTextureInfo texture) const override; diff --git a/shell/platform/darwin/ios/ios_surface_metal_impeller.mm b/shell/platform/darwin/ios/ios_surface_metal_impeller.mm index e48623e943135..2b2c1284e1118 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_impeller.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_impeller.mm @@ -86,7 +86,8 @@ } // |GPUSurfaceMetalDelegate| -GPUMTLTextureInfo IOSSurfaceMetalImpeller::GetMTLTexture(const SkISize& frame_info) const { +GPUMTLTextureInfo IOSSurfaceMetalImpeller::GetMTLTexture(int64_t view_id, + const SkISize& frame_info) const { FML_CHECK(false); return GPUMTLTextureInfo{ .texture_id = -1, // diff --git a/shell/platform/darwin/ios/ios_surface_metal_skia.h b/shell/platform/darwin/ios/ios_surface_metal_skia.h index 43f6dd1268ae9..926ad1346b117 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_skia.h +++ b/shell/platform/darwin/ios/ios_surface_metal_skia.h @@ -49,7 +49,7 @@ class SK_API_AVAILABLE_CA_METAL_LAYER IOSSurfaceMetalSkia final : public IOSSurf bool PresentDrawable(GrMTLHandle drawable) const override; // |GPUSurfaceMetalDelegate| - GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_info) const override; + GPUMTLTextureInfo GetMTLTexture(int64_t view_id, const SkISize& frame_info) const override; // |GPUSurfaceMetalDelegate| bool PresentTexture(GPUMTLTextureInfo texture) const override; diff --git a/shell/platform/darwin/ios/ios_surface_metal_skia.mm b/shell/platform/darwin/ios/ios_surface_metal_skia.mm index 15b465528d221..bf80c80201295 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_skia.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_skia.mm @@ -98,7 +98,8 @@ } // |GPUSurfaceMetalDelegate| -GPUMTLTextureInfo IOSSurfaceMetalSkia::GetMTLTexture(const SkISize& frame_info) const { +GPUMTLTextureInfo IOSSurfaceMetalSkia::GetMTLTexture(int64_t view_id, + const SkISize& frame_info) const { FML_CHECK(false) << "render to texture not supported on ios"; return {.texture_id = -1, .texture = nullptr}; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h b/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h index 483a858032bc8..73c16855b7100 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h @@ -38,7 +38,7 @@ /** * Called by the engine when the given view's buffers should be swapped. */ -- (BOOL)present:(uint64_t)viewId texture:(nonnull const FlutterMetalTexture*)texture; +- (BOOL)present:(nonnull const FlutterMetalTexture*)texture; /** * Creates a Metal texture for the given view with the given size. diff --git a/shell/platform/darwin/macos/framework/Source/FlutterRenderer.mm b/shell/platform/darwin/macos/framework/Source/FlutterRenderer.mm index d7d2c2fc7d6ac..8b254f5534947 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterRenderer.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterRenderer.mm @@ -14,21 +14,13 @@ static FlutterMetalTexture OnGetNextDrawableForDefaultView(FlutterEngine* engine, const FlutterFrameInfo* frameInfo) { - // TODO(dkwingsmt): This callback only supports single-view, therefore it only - // operates on the default view. To support multi-view, we need a new callback - // that also receives a view ID, or pass the ID via FlutterFrameInfo. - uint64_t viewId = kFlutterDefaultViewId; CGSize size = CGSizeMake(frameInfo->size.width, frameInfo->size.height); - return [engine.renderer createTextureForView:viewId size:size]; + return [engine.renderer createTextureForView:frameInfo->view_id size:size]; } static bool OnPresentDrawableOfDefaultView(FlutterEngine* engine, const FlutterMetalTexture* texture) { - // TODO(dkwingsmt): This callback only supports single-view, therefore it only - // operates on the default view. To support multi-view, we need a new callback - // that also receives a view ID. - uint64_t viewId = kFlutterDefaultViewId; - return [engine.renderer present:viewId texture:texture]; + return [engine.renderer present:texture]; } static bool OnAcquireExternalTexture(FlutterEngine* engine, @@ -46,6 +38,8 @@ @implementation FlutterRenderer { FlutterViewEngineProvider* _viewProvider; FlutterDarwinContextMetalSkia* _darwinMetalContext; + + NSMutableDictionary* _texture_to_view; } - (instancetype)initWithFlutterEngine:(nonnull FlutterEngine*)flutterEngine { @@ -66,6 +60,7 @@ - (instancetype)initWithFlutterEngine:(nonnull FlutterEngine*)flutterEngine { _darwinMetalContext = [[FlutterDarwinContextMetalSkia alloc] initWithMTLDevice:_device commandQueue:_commandQueue]; + _texture_to_view = [NSMutableDictionary dictionary]; } return self; } @@ -95,11 +90,17 @@ - (FlutterMetalTexture)createTextureForView:(uint64_t)viewId size:(CGSize)size { // FlutterMetalTexture has texture `null`, therefore is discarded. return FlutterMetalTexture{}; } - return [view.surfaceManager surfaceForSize:size].asFlutterMetalTexture; + FlutterMetalTexture texture = [view.surfaceManager surfaceForSize:size].asFlutterMetalTexture; + _texture_to_view[@(texture.texture_id)] = @(viewId); + return texture; } -- (BOOL)present:(uint64_t)viewId texture:(const FlutterMetalTexture*)texture { - FlutterView* view = [_viewProvider viewForId:viewId]; +- (BOOL)present:(const FlutterMetalTexture*)texture { + NSNumber* viewId = _texture_to_view[@(texture->texture_id)]; + if (viewId == nil) { + return NO; + } + FlutterView* view = [_viewProvider viewForId:[viewId longLongValue]]; if (view == nil) { return NO; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterRendererTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterRendererTest.mm index 46da658efd8e6..f3c88be8806d7 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterRendererTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterRendererTest.mm @@ -62,18 +62,22 @@ - (nonnull FlutterView*)createFlutterViewWithMTLDevice:(id)device OCMStub([viewMock surfaceManager]).andReturn(surfaceManagerMock); id surfaceMock = OCMClassMock([FlutterSurface class]); - - FlutterMetalTexture texture = { + FlutterMetalTexture mockReturnTexture{ + .texture_id = 100ll, // Arbitrary .user_data = (__bridge void*)surfaceMock, }; - + OCMStub([surfaceMock asFlutterMetalTexture]).andReturn(mockReturnTexture); + OCMStub([surfaceManagerMock surfaceForSize:CGSize{}]) + .ignoringNonObjectArgs() + .andReturn(surfaceMock); [[surfaceManagerMock expect] present:[OCMArg checkWithBlock:^(id obj) { NSArray* array = (NSArray*)obj; return array.count == 1 ? YES : NO; }] notify:nil]; - [renderer present:kFlutterDefaultViewId texture:&texture]; + FlutterMetalTexture texture = [renderer createTextureForView:0ll size:CGSize{}]; + [renderer present:&texture]; [surfaceManagerMock verify]; } diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 17a754fb0f577..22ee1290dc439 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -477,12 +477,14 @@ InferMetalPlatformViewCreationCallback( return ptr(user_data, &embedder_texture); }; auto metal_get_texture = - [ptr = config->metal.get_next_drawable_callback, - user_data](const SkISize& frame_size) -> flutter::GPUMTLTextureInfo { + [ptr = config->metal.get_next_drawable_callback, user_data]( + int64_t view_id, + const SkISize& frame_size) -> flutter::GPUMTLTextureInfo { FlutterFrameInfo frame_info = {}; frame_info.struct_size = sizeof(FlutterFrameInfo); frame_info.size = {static_cast(frame_size.width()), static_cast(frame_size.height())}; + frame_info.view_id = view_id; flutter::GPUMTLTextureInfo texture_info; FlutterMetalTexture metal_texture = ptr(user_data, &frame_info); diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 06f8217a93f14..95e627a0bcc4d 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -464,6 +464,7 @@ typedef struct { size_t struct_size; /// The size of the surface that will be backed by the fbo. FlutterUIntSize size; + int64_t view_id; } FlutterFrameInfo; /// Callback for when a frame buffer object is requested. diff --git a/shell/platform/embedder/embedder_studio_metal.h b/shell/platform/embedder/embedder_studio_metal.h index 6c7d9e6e838fe..6c126370bed74 100644 --- a/shell/platform/embedder/embedder_studio_metal.h +++ b/shell/platform/embedder/embedder_studio_metal.h @@ -20,7 +20,7 @@ class EmbedderStudioMetal final : public EmbedderStudio, public: struct MetalDispatchTable { std::function present; // required - std::function + std::function get_texture; // required }; @@ -52,7 +52,8 @@ class EmbedderStudioMetal final : public EmbedderStudio, bool PresentDrawable(GrMTLHandle drawable) const override; // |GPUSurfaceMetalDelegate| - GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_size) const override; + GPUMTLTextureInfo GetMTLTexture(int64_t view_id, + const SkISize& frame_size) const override; // |GPUSurfaceMetalDelegate| bool PresentTexture(GPUMTLTextureInfo texture) const override; diff --git a/shell/platform/embedder/embedder_studio_metal.mm b/shell/platform/embedder/embedder_studio_metal.mm index 6a7f3fb267815..4f1cbae62c16a 100644 --- a/shell/platform/embedder/embedder_studio_metal.mm +++ b/shell/platform/embedder/embedder_studio_metal.mm @@ -84,8 +84,9 @@ return false; } -GPUMTLTextureInfo EmbedderStudioMetal::GetMTLTexture(const SkISize& frame_info) const { - return metal_dispatch_table_.get_texture(frame_info); +GPUMTLTextureInfo EmbedderStudioMetal::GetMTLTexture(int64_t view_id, + const SkISize& frame_info) const { + return metal_dispatch_table_.get_texture(view_id, frame_info); } bool EmbedderStudioMetal::PresentTexture(GPUMTLTextureInfo texture) const { From b73e7a8fcccadb8aa6b8d1892e1b3e8d76fe0623 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 29 Mar 2023 16:51:58 -0700 Subject: [PATCH 073/147] Put view ID in MetalTexture for now --- .../darwin/macos/framework/Source/FlutterRenderer.mm | 8 ++------ .../macos/framework/Source/FlutterRendererTest.mm | 11 ++++------- shell/platform/embedder/embedder.h | 1 + 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterRenderer.mm b/shell/platform/darwin/macos/framework/Source/FlutterRenderer.mm index 8b254f5534947..a9a8ea847d9f2 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterRenderer.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterRenderer.mm @@ -91,16 +91,12 @@ - (FlutterMetalTexture)createTextureForView:(uint64_t)viewId size:(CGSize)size { return FlutterMetalTexture{}; } FlutterMetalTexture texture = [view.surfaceManager surfaceForSize:size].asFlutterMetalTexture; - _texture_to_view[@(texture.texture_id)] = @(viewId); + texture.view_id = viewId; return texture; } - (BOOL)present:(const FlutterMetalTexture*)texture { - NSNumber* viewId = _texture_to_view[@(texture->texture_id)]; - if (viewId == nil) { - return NO; - } - FlutterView* view = [_viewProvider viewForId:[viewId longLongValue]]; + FlutterView* view = [_viewProvider viewForId:texture->view_id]; if (view == nil) { return NO; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterRendererTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterRendererTest.mm index f3c88be8806d7..9a705ea6c188a 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterRendererTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterRendererTest.mm @@ -62,21 +62,18 @@ - (nonnull FlutterView*)createFlutterViewWithMTLDevice:(id)device OCMStub([viewMock surfaceManager]).andReturn(surfaceManagerMock); id surfaceMock = OCMClassMock([FlutterSurface class]); - FlutterMetalTexture mockReturnTexture{ - .texture_id = 100ll, // Arbitrary + + FlutterMetalTexture texture = { .user_data = (__bridge void*)surfaceMock, + .view_id = 0ll, }; - OCMStub([surfaceMock asFlutterMetalTexture]).andReturn(mockReturnTexture); - OCMStub([surfaceManagerMock surfaceForSize:CGSize{}]) - .ignoringNonObjectArgs() - .andReturn(surfaceMock); + [[surfaceManagerMock expect] present:[OCMArg checkWithBlock:^(id obj) { NSArray* array = (NSArray*)obj; return array.count == 1 ? YES : NO; }] notify:nil]; - FlutterMetalTexture texture = [renderer createTextureForView:0ll size:CGSize{}]; [renderer present:&texture]; [surfaceManagerMock verify]; } diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 95e627a0bcc4d..9e165b1de0ac5 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -654,6 +654,7 @@ typedef struct { /// The callback invoked by the engine when it no longer needs this backing /// store. VoidCallback destruction_callback; + int64_t view_id; } FlutterMetalTexture; /// Callback for when a metal texture is requested. From eda122790e2dcd6f75419f5ea9a2578d1a7c4bb0 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 29 Mar 2023 16:53:15 -0700 Subject: [PATCH 074/147] Fix fuchsia compile --- shell/platform/fuchsia/flutter/surface.cc | 1 + shell/platform/fuchsia/flutter/surface.h | 1 + 2 files changed, 2 insertions(+) diff --git a/shell/platform/fuchsia/flutter/surface.cc b/shell/platform/fuchsia/flutter/surface.cc index 49d06f023cbf8..13ff5f7c8a110 100644 --- a/shell/platform/fuchsia/flutter/surface.cc +++ b/shell/platform/fuchsia/flutter/surface.cc @@ -29,6 +29,7 @@ bool Surface::IsValid() { // |flutter::Surface| std::unique_ptr Surface::AcquireFrame( + int64_t view_id, const SkISize& size) { flutter::SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; diff --git a/shell/platform/fuchsia/flutter/surface.h b/shell/platform/fuchsia/flutter/surface.h index 5d9098fbc6e07..b3e6621db5f09 100644 --- a/shell/platform/fuchsia/flutter/surface.h +++ b/shell/platform/fuchsia/flutter/surface.h @@ -31,6 +31,7 @@ class Surface final : public flutter::Surface { // |flutter::Surface| std::unique_ptr AcquireFrame( + int64_t view_id, const SkISize& size) override; // |flutter::Surface| From 30f816bc4e12f46dec3f5bee772eaaf3a05b256f Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 3 Apr 2023 01:51:56 -0700 Subject: [PATCH 075/147] CreateSurface add view_id --- shell/common/platform_view.cc | 8 +++----- shell/common/platform_view.h | 2 +- shell/common/shell.cc | 4 ++-- shell/platform/fuchsia/flutter/engine.h | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index fac06cee3b7b1..5600d41e32705 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -63,8 +63,6 @@ void PlatformView::NotifyDestroyed() { delegate_.OnPlatformViewDestroyed(); } -constexpr int64_t kFlutterDefaultViewId = 0; - std::unique_ptr PlatformView::CreateStudio() { std::unique_ptr studio; // Threading: We want to use the platform view on the non-platform thread. @@ -89,7 +87,7 @@ std::unique_ptr PlatformView::CreateStudio() { return studio; } -std::unique_ptr PlatformView::CreateSurface() { +std::unique_ptr PlatformView::CreateSurface(int64_t view_id) { std::unique_ptr surface; // Threading: We want to use the platform view on the non-platform thread. // Using the weak pointer is illegal. But, we are going to introduce a latch @@ -98,8 +96,8 @@ std::unique_ptr PlatformView::CreateSurface() { auto* platform_view = this; fml::ManualResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask( - task_runners_.GetRasterTaskRunner(), [platform_view, &surface, &latch]() { - surface = platform_view->CreateRenderingSurface(kFlutterDefaultViewId); + task_runners_.GetRasterTaskRunner(), [platform_view, &surface, &latch, view_id]() { + surface = platform_view->CreateRenderingSurface(view_id); if (!surface || !surface->IsValid()) { surface.reset(); } diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 985a25bcfe014..7d698ec7b9636 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -495,7 +495,7 @@ class PlatformView { virtual void NotifyDestroyed(); std::unique_ptr CreateStudio(); - std::unique_ptr CreateSurface(); + std::unique_ptr CreateSurface(int64_t view_id); //---------------------------------------------------------------------------- /// @brief Used by embedders to schedule a frame. In response to this diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 0f328503ba32a..3a2b85419ed5f 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -753,7 +753,7 @@ void Shell::OnPlatformViewCreated() { FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); std::unique_ptr studio = platform_view_->CreateStudio(); - std::unique_ptr surface = platform_view_->CreateSurface(); + std::unique_ptr surface = platform_view_->CreateSurface(kFlutterDefaultViewId); if (studio == nullptr || surface == nullptr) { // TODO(dkwingsmt): This case is observed in windows unit tests. Anyway, // we're probably not creating the surface in this callback eventually. @@ -1988,7 +1988,7 @@ void Shell::AddRenderSurface(int64_t view_id) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); - std::unique_ptr surface = platform_view_->CreateSurface(); + std::unique_ptr surface = platform_view_->CreateSurface(view_id); fml::AutoResetWaitableEvent latch; task_runners_.GetRasterTaskRunner()->PostTask( fml::MakeCopyable([&latch, // diff --git a/shell/platform/fuchsia/flutter/engine.h b/shell/platform/fuchsia/flutter/engine.h index 06097a9692f85..5cc519b6fd729 100644 --- a/shell/platform/fuchsia/flutter/engine.h +++ b/shell/platform/fuchsia/flutter/engine.h @@ -147,7 +147,7 @@ class Engine final : public fuchsia::memorypressure::Watcher { std::unique_ptr CreateStudio(); - std::unique_ptr CreateSurface(); + std::unique_ptr CreateSurface(int64_t view_id); Delegate& delegate_; From 72536530e629d6b5b79e98e8fb8249ecc47c3541 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 5 Apr 2023 23:02:02 -0700 Subject: [PATCH 076/147] Add and remove View --- lib/ui/hooks.dart | 7 +++ lib/ui/platform_dispatcher.dart | 6 +++ lib/ui/window.dart | 4 +- lib/ui/window/platform_configuration.cc | 19 ++++++++ lib/ui/window/platform_configuration.h | 3 ++ runtime/runtime_controller.cc | 9 ++++ runtime/runtime_controller.h | 2 + shell/common/engine.cc | 4 ++ shell/common/engine.h | 2 + shell/common/platform_view.cc | 18 +------- shell/common/rasterizer.cc | 4 ++ shell/common/rasterizer.h | 2 + shell/common/shell.cc | 58 ++++++++++++++++++++----- shell/common/shell.h | 1 + 14 files changed, 111 insertions(+), 28 deletions(-) diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index f92d287cf0f14..6a27e5d5af4c2 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -4,6 +4,13 @@ part of dart.ui; +@pragma('vm:entry-point') +void _addView( + Object id, +) { + PlatformDispatcher.instance._addView(id); +} + @pragma('vm:entry-point') void _updateWindowMetrics( Object id, diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index f955bb5f1882d..92afeb08c6dd3 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -215,6 +215,12 @@ class PlatformDispatcher { _onMetricsChangedZone = Zone.current; } + void _addView(Object id) { + assert(!_views.containsKey(id)); + _views[id] = FlutterView._(id, this); + _viewConfigurations[id] = const _ViewConfiguration(); + } + // Called from the engine, via hooks.dart // // Updates the metrics of the window with the given id. diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 44389d09f8f39..726998b0e2193 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -289,7 +289,9 @@ class FlutterView { /// scheduling of frames. /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. - void render(Scene scene) => _render(viewId == 0 ? 0 : viewId.hashCode, scene); + void render(Scene scene) { + _render(viewId as int, scene); + } @Native)>(symbol: 'PlatformConfigurationNativeApi::Render') external static void _render(int viewId, Scene scene); diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 25124cd9d3458..08882ea4249ad 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -43,6 +43,8 @@ void PlatformConfiguration::DidCreateIsolate() { on_error_.Set(tonic::DartState::Current(), Dart_GetField(library, tonic::ToDart("_onError"))); + add_view_.Set(tonic::DartState::Current(), + Dart_GetField(library, tonic::ToDart("_addView"))); update_locales_.Set(tonic::DartState::Current(), Dart_GetField(library, tonic::ToDart("_updateLocales"))); update_user_settings_data_.Set( @@ -78,6 +80,23 @@ void PlatformConfiguration::DidCreateIsolate() { kImplicitViewId, ViewportMetrics{1.0, 0.0, 0.0, -1})); } +constexpr int64_t kFlutterDefaultViewId = 0ll; + +void PlatformConfiguration::AddView(int64_t view_id) { + if (view_id == kFlutterDefaultViewId) { + return; + } + std::shared_ptr dart_state = add_view_.dart_state().lock(); + if (!dart_state) { + return; + } + tonic::DartState::Scope scope(dart_state); + tonic::CheckAndHandleError( + tonic::DartInvoke(add_view_.Get(), { + tonic::ToDart(view_id), + })); +} + void PlatformConfiguration::UpdateLocales( const std::vector& locales) { std::shared_ptr dart_state = diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index 57c939647715f..fa5e6174e4036 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -267,6 +267,8 @@ class PlatformConfiguration final { /// void DidCreateIsolate(); + void AddView(int64_t view_id); + //---------------------------------------------------------------------------- /// @brief Update the specified locale data in the framework. /// @@ -434,6 +436,7 @@ class PlatformConfiguration final { private: PlatformConfigurationClient* client_; tonic::DartPersistentValue on_error_; + tonic::DartPersistentValue add_view_; tonic::DartPersistentValue update_locales_; tonic::DartPersistentValue update_user_settings_data_; tonic::DartPersistentValue update_initial_lifecycle_state_; diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 0b934f635da1f..98aa7ef0d2472 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -122,6 +122,15 @@ bool RuntimeController::FlushRuntimeStateToIsolate() { SetInitialLifecycleState(platform_data_.lifecycle_state); } +bool RuntimeController::AddView(int64_t view_id) { + if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { + platform_configuration->AddView(view_id); + return true; + } + + return false; +} + bool RuntimeController::SetViewportMetrics(const ViewportMetrics& metrics) { platform_data_.viewport_metrics = metrics; diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 0312294f01732..c88f97e3a292a 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -163,6 +163,8 @@ class RuntimeController : public PlatformConfigurationClient { /// std::unique_ptr Clone() const; + bool AddView(int64_t view_id); + //---------------------------------------------------------------------------- /// @brief Forward the specified viewport metrics to the running isolate. /// If the isolate is not running, these metrics will be saved and diff --git a/shell/common/engine.cc b/shell/common/engine.cc index a141d09cd1fa7..95710dfcaf447 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -286,6 +286,10 @@ tonic::DartErrorHandleType Engine::GetUIIsolateLastError() { return runtime_controller_->GetLastError(); } +void Engine::AddView(int64_t view_id) { + runtime_controller_->AddView(view_id); +} + void Engine::SetViewportMetrics(const ViewportMetrics& metrics) { runtime_controller_->SetViewportMetrics(metrics); ScheduleFrame(); diff --git a/shell/common/engine.h b/shell/common/engine.h index 8c5a07379f86b..34774bf59dfbb 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -673,6 +673,8 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { /// std::optional GetUIIsolateReturnCode(); + void AddView(int64_t view_id); + //---------------------------------------------------------------------------- /// @brief Updates the viewport metrics for the currently running Flutter /// application. The viewport metrics detail the size of the diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index 5600d41e32705..87f3af8e6b3df 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -88,22 +88,8 @@ std::unique_ptr PlatformView::CreateStudio() { } std::unique_ptr PlatformView::CreateSurface(int64_t view_id) { - std::unique_ptr surface; - // Threading: We want to use the platform view on the non-platform thread. - // Using the weak pointer is illegal. But, we are going to introduce a latch - // so that the platform view is not collected till the studio and the surface - // are obtained. - auto* platform_view = this; - fml::ManualResetWaitableEvent latch; - fml::TaskRunner::RunNowOrPostTask( - task_runners_.GetRasterTaskRunner(), [platform_view, &surface, &latch, view_id]() { - surface = platform_view->CreateRenderingSurface(view_id); - if (!surface || !surface->IsValid()) { - surface.reset(); - } - latch.Signal(); - }); - latch.Wait(); + FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); + auto surface = CreateRenderingSurface(view_id); if (!surface) { FML_LOG(ERROR) << "Failed to create platform view rendering surface"; return nullptr; diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 5e6bcc53d7e01..8166a5ac70a71 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -154,6 +154,10 @@ void Rasterizer::AddSurface(int64_t view_id, std::unique_ptr surface) { } } +void Rasterizer::RemoveSurface(int64_t view_id) { + surfaces_.erase(view_id); +} + std::shared_ptr Rasterizer::GetTextureRegistry() { return compositor_context_->texture_registry(); } diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 5c4bb2639a48c..87fcbef522151 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -191,6 +191,8 @@ class Rasterizer final : public SnapshotDelegate, void AddSurface(int64_t view_id, std::unique_ptr surface); + void RemoveSurface(int64_t view_id); + //---------------------------------------------------------------------------- /// /// @bug https://github.com/flutter/flutter/issues/33939 diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 3a2b85419ed5f..07d27ed886a99 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -753,8 +753,7 @@ void Shell::OnPlatformViewCreated() { FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); std::unique_ptr studio = platform_view_->CreateStudio(); - std::unique_ptr surface = platform_view_->CreateSurface(kFlutterDefaultViewId); - if (studio == nullptr || surface == nullptr) { + if (studio == nullptr) { // TODO(dkwingsmt): This case is observed in windows unit tests. Anyway, // we're probably not creating the surface in this callback eventually. return; @@ -786,9 +785,13 @@ void Shell::OnPlatformViewCreated() { fml::AutoResetWaitableEvent latch; auto raster_task = fml::MakeCopyable( - [&waiting_for_first_frame = waiting_for_first_frame_, - rasterizer = rasterizer_->GetWeakPtr(), // - studio = std::move(studio), surface = std::move(surface)]() mutable { + [&waiting_for_first_frame = waiting_for_first_frame_, // + rasterizer = rasterizer_->GetWeakPtr(), // + platform_view = platform_view_.get(), // + studio = std::move(studio) // + ]() mutable { + std::unique_ptr surface = + platform_view->CreateSurface(kFlutterDefaultViewId); if (rasterizer) { // Enables the thread merger which may be used by the external view // embedder. @@ -1987,20 +1990,53 @@ void Shell::AddRenderSurface(int64_t view_id) { TRACE_EVENT0("flutter", "Shell::AddRenderSurface"); FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + if (!engine_) { + return; + } + if (view_id == kFlutterDefaultViewId) { + return; + } - std::unique_ptr surface = platform_view_->CreateSurface(view_id); - fml::AutoResetWaitableEvent latch; + auto ui_task = [engine = engine_->GetWeakPtr(), // + view_id // + ] { engine->AddView(view_id); }; + // TODO(dkwingsmt): platform_view_ is captured illegally here. + // We need some mechanism from it being collected. task_runners_.GetRasterTaskRunner()->PostTask( - fml::MakeCopyable([&latch, // + fml::MakeCopyable([&task_runners = task_runners_, // + ui_task, // + platform_view = platform_view_.get(), // rasterizer = rasterizer_->GetWeakPtr(), // - surface = std::move(surface), // view_id // + ]() mutable { + if (platform_view && rasterizer) { + std::unique_ptr surface = + platform_view->CreateSurface(view_id); + if (surface) { + rasterizer->AddSurface(view_id, std::move(surface)); + } + task_runners.GetUITaskRunner()->PostTask(ui_task); + } + })); +} + +void Shell::RemoveRenderSurface(int64_t view_id) { + TRACE_EVENT0("flutter", "Shell::RemoveRenderSurface"); + FML_DCHECK(is_setup_); + FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + fml::AutoResetWaitableEvent latch; + // platform_view_->RemoveSurface(view_id); // TODO + task_runners_.GetRasterTaskRunner()->PostTask( + [&latch, // + rasterizer = rasterizer_->GetWeakPtr(), // + view_id // ]() mutable { if (rasterizer) { - rasterizer->AddSurface(view_id, std::move(surface)); + rasterizer->RemoveSurface(view_id); } latch.Signal(); - })); + }); latch.Wait(); } diff --git a/shell/common/shell.h b/shell/common/shell.h index 9642a7e3b428c..5f7e62272757e 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -298,6 +298,7 @@ class Shell final : public PlatformView::Delegate, bool IsSetup() const; void AddRenderSurface(int64_t view_id); + void RemoveRenderSurface(int64_t view_id); //---------------------------------------------------------------------------- /// @brief Captures a screenshot and optionally Base64 encodes the data From a910a04bf62ded1026edeacbfe34350911e2b226 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 21 Apr 2023 13:23:24 -0700 Subject: [PATCH 077/147] Workable 2 (multiple initial window) --- flow/embedded_views.cc | 3 +- flow/embedded_views.h | 3 +- lib/ui/compositing.dart | 8 +-- lib/ui/compositing/scene.cc | 29 ++++---- lib/ui/compositing/scene.h | 4 +- lib/ui/compositing/scene_builder.cc | 9 +-- lib/ui/compositing/scene_builder.h | 7 +- lib/ui/dart_ui.cc | 2 +- lib/ui/hooks.dart | 12 ++++ lib/ui/platform_dispatcher.dart | 50 +++++++++++-- lib/ui/window/platform_configuration.cc | 60 ++++++++++++---- lib/ui/window/platform_configuration.h | 20 +++++- lib/ui/window/window.cc | 11 +-- lib/ui/window/window.h | 6 +- runtime/runtime_controller.cc | 25 +++++-- runtime/runtime_controller.h | 4 +- shell/common/engine.cc | 9 ++- shell/common/engine.h | 4 +- shell/common/engine_unittests.cc | 5 +- shell/common/platform_view.cc | 5 +- shell/common/platform_view.h | 3 +- shell/common/rasterizer.cc | 4 +- shell/common/rasterizer.h | 14 ++-- shell/common/rasterizer_unittests.cc | 35 ++++----- shell/common/shell.cc | 44 +++++++----- shell/common/shell.h | 5 +- shell/common/shell_test.cc | 4 +- .../shell_test_external_view_embedder.cc | 3 +- .../shell_test_external_view_embedder.h | 3 +- shell/common/shell_unittests.cc | 72 +++++++++++-------- .../external_view_embedder.cc | 3 +- .../external_view_embedder.h | 3 +- .../external_view_embedder_unittests.cc | 27 ++++--- .../darwin/ios/ios_external_view_embedder.h | 3 +- .../framework/Source/FlutterCompositor.mm | 5 +- .../macos/framework/Source/FlutterEngine.mm | 17 ++--- .../framework/Source/FlutterEngineTest.mm | 9 ++- .../Source/FlutterThreadSynchronizer.h | 12 +++- .../Source/FlutterThreadSynchronizer.mm | 48 ++++++++++--- .../macos/framework/Source/FlutterView.h | 4 +- .../macos/framework/Source/FlutterView.mm | 30 +++++--- .../framework/Source/FlutterViewController.mm | 7 +- .../macos/framework/Source/FlutterViewTest.mm | 5 +- shell/platform/embedder/embedder.cc | 9 +-- shell/platform/embedder/embedder.h | 4 ++ shell/platform/embedder/embedder_engine.cc | 3 +- shell/platform/embedder/embedder_engine.h | 3 +- .../embedder_external_view_embedder.cc | 11 +-- .../embedder_external_view_embedder.h | 6 +- shell/platform/embedder/embedder_layers.cc | 6 +- shell/platform/embedder/embedder_layers.h | 6 +- .../platform_view_embedder_unittests.cc | 4 +- .../embedder/tests/embedder_config_builder.cc | 1 + .../tests/embedder_metal_unittests.mm | 16 +++-- .../embedder/tests/embedder_unittests.cc | 55 ++++++++------ .../flatland_external_view_embedder.cc | 3 +- .../flutter/flatland_external_view_embedder.h | 3 +- .../flutter/gfx_external_view_embedder.cc | 3 +- .../flutter/gfx_external_view_embedder.h | 3 +- .../fuchsia/flutter/platform_view_unittest.cc | 3 +- ...atland_external_view_embedder_unittests.cc | 26 ++++--- .../tests/flatland_platform_view_unittest.cc | 3 +- .../gfx_external_view_embedder_unittests.cc | 26 ++++--- shell/platform/linux/fl_engine.cc | 1 + shell/testing/tester_main.cc | 4 +- 65 files changed, 565 insertions(+), 270 deletions(-) diff --git a/flow/embedded_views.cc b/flow/embedded_views.cc index b76c9730af903..b89ed606fcc14 100644 --- a/flow/embedded_views.cc +++ b/flow/embedded_views.cc @@ -43,7 +43,8 @@ bool DisplayListEmbedderViewSlice::recording_ended() { } void ExternalViewEmbedder::SubmitFrame(GrDirectContext* context, - std::unique_ptr frame) { + std::unique_ptr frame, + int64_t window_view_id) { frame->Submit(); }; diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 4162e79a15af6..c9ee05430d0ab 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -415,7 +415,8 @@ class ExternalViewEmbedder { // // It can also allocate frames for overlay surfaces to compose hybrid views. virtual void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame); + std::unique_ptr frame, + int64_t window_view_id); // This method provides the embedder a way to do additional tasks after // |SubmitFrame|. For example, merge task runners if `should_resubmit_frame` diff --git a/lib/ui/compositing.dart b/lib/ui/compositing.dart index f20ecff7349c6..cfb4e1908064b 100644 --- a/lib/ui/compositing.dart +++ b/lib/ui/compositing.dart @@ -231,12 +231,12 @@ class PhysicalShapeEngineLayer extends _EngineLayerWrapper { class SceneBuilder extends NativeFieldWrapperClass1 { /// Creates an empty [SceneBuilder] object. @pragma('vm:entry-point') - SceneBuilder() { - _constructor(); + SceneBuilder([Object viewId = 0]) { + _constructor(viewId as int); } - @Native(symbol: 'SceneBuilder::Create') - external void _constructor(); + @Native(symbol: 'SceneBuilder::Create') + external void _constructor(int viewId); // Layers used in this scene. // diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index c5070c6376091..91e9c45c9b4f5 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -26,30 +26,35 @@ namespace flutter { IMPLEMENT_WRAPPERTYPEINFO(ui, Scene); void Scene::create(Dart_Handle scene_handle, + int64_t view_id, std::shared_ptr rootLayer, uint32_t rasterizerTracingThreshold, bool checkerboardRasterCacheImages, bool checkerboardOffscreenLayers) { auto scene = fml::MakeRefCounted( - std::move(rootLayer), rasterizerTracingThreshold, + view_id, std::move(rootLayer), rasterizerTracingThreshold, checkerboardRasterCacheImages, checkerboardOffscreenLayers); scene->AssociateWithDartWrapper(scene_handle); } -Scene::Scene(std::shared_ptr rootLayer, +Scene::Scene(int64_t view_id, + std::shared_ptr rootLayer, uint32_t rasterizerTracingThreshold, bool checkerboardRasterCacheImages, bool checkerboardOffscreenLayers) { - // Currently only supports a single window. - auto viewport_metrics = UIDartState::Current() - ->platform_configuration() - ->get_window(0) - ->viewport_metrics(); - - layer_tree_ = std::make_shared( - SkISize::Make(viewport_metrics.physical_width, - viewport_metrics.physical_height), - static_cast(viewport_metrics.device_pixel_ratio)); + Window* window = + UIDartState::Current()->platform_configuration()->get_window(view_id); + if (window != nullptr) { + auto viewport_metrics = window->viewport_metrics(); + layer_tree_ = std::make_shared( + SkISize::Make(viewport_metrics.physical_width, + viewport_metrics.physical_height), + static_cast(viewport_metrics.device_pixel_ratio)); + } else { + // TODO(dkwingsmt) + layer_tree_ = std::make_shared(SkISize::Make(0, 0), 1); + } + layer_tree_->set_root_layer(std::move(rootLayer)); layer_tree_->set_rasterizer_tracing_threshold(rasterizerTracingThreshold); layer_tree_->set_checkerboard_raster_cache_images( diff --git a/lib/ui/compositing/scene.h b/lib/ui/compositing/scene.h index aca797836fcb1..7a143f0a42d37 100644 --- a/lib/ui/compositing/scene.h +++ b/lib/ui/compositing/scene.h @@ -21,6 +21,7 @@ class Scene : public RefCountedDartWrappable { public: ~Scene() override; static void create(Dart_Handle scene_handle, + int64_t view_id, std::shared_ptr rootLayer, uint32_t rasterizerTracingThreshold, bool checkerboardRasterCacheImages, @@ -39,7 +40,8 @@ class Scene : public RefCountedDartWrappable { void dispose(); private: - Scene(std::shared_ptr rootLayer, + Scene(int64_t view_id, + std::shared_ptr rootLayer, uint32_t rasterizerTracingThreshold, bool checkerboardRasterCacheImages, bool checkerboardOffscreenLayers); diff --git a/lib/ui/compositing/scene_builder.cc b/lib/ui/compositing/scene_builder.cc index 63ac03c92c872..e65fb5c741e52 100644 --- a/lib/ui/compositing/scene_builder.cc +++ b/lib/ui/compositing/scene_builder.cc @@ -34,7 +34,7 @@ namespace flutter { IMPLEMENT_WRAPPERTYPEINFO(ui, SceneBuilder); -SceneBuilder::SceneBuilder() { +SceneBuilder::SceneBuilder(int64_t view_id) : view_id_(view_id) { // Add a ContainerLayer as the root layer, so that AddLayer operations are // always valid. PushLayer(std::make_shared()); @@ -303,9 +303,10 @@ void SceneBuilder::setCheckerboardOffscreenLayers(bool checkerboard) { void SceneBuilder::build(Dart_Handle scene_handle) { FML_DCHECK(layer_stack_.size() >= 1); - Scene::create( - scene_handle, std::move(layer_stack_[0]), rasterizer_tracing_threshold_, - checkerboard_raster_cache_images_, checkerboard_offscreen_layers_); + Scene::create(scene_handle, view_id_, std::move(layer_stack_[0]), + rasterizer_tracing_threshold_, + checkerboard_raster_cache_images_, + checkerboard_offscreen_layers_); layer_stack_.clear(); ClearDartWrapper(); // may delete this object. } diff --git a/lib/ui/compositing/scene_builder.h b/lib/ui/compositing/scene_builder.h index f2de29c4802d2..7cb982b94f465 100644 --- a/lib/ui/compositing/scene_builder.h +++ b/lib/ui/compositing/scene_builder.h @@ -28,9 +28,9 @@ class SceneBuilder : public RefCountedDartWrappable { FML_FRIEND_MAKE_REF_COUNTED(SceneBuilder); public: - static void Create(Dart_Handle wrapper) { + static void Create(Dart_Handle wrapper, int64_t view_id) { UIDartState::ThrowIfUIOperationsProhibited(); - auto res = fml::MakeRefCounted(); + auto res = fml::MakeRefCounted(view_id); res->AssociateWithDartWrapper(wrapper); } @@ -135,12 +135,13 @@ class SceneBuilder : public RefCountedDartWrappable { } private: - SceneBuilder(); + SceneBuilder(int64_t view_id); void AddLayer(std::shared_ptr layer); void PushLayer(std::shared_ptr layer); void PopLayer(); + int64_t view_id_; std::vector> layer_stack_; int rasterizer_tracing_threshold_ = 0; bool checkerboard_raster_cache_images_ = false; diff --git a/lib/ui/dart_ui.cc b/lib/ui/dart_ui.cc index e26aeb307fd55..f9e4c29a378ec 100644 --- a/lib/ui/dart_ui.cc +++ b/lib/ui/dart_ui.cc @@ -81,7 +81,7 @@ typedef CanvasPath Path; V(PathMeasure::Create, 3) \ V(Path::Create, 1) \ V(PictureRecorder::Create, 1) \ - V(SceneBuilder::Create, 1) \ + V(SceneBuilder::Create, 2) \ V(SemanticsUpdateBuilder::Create, 1) \ /* Other */ \ V(FontCollection::LoadFontFromList, 3) \ diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index 6a27e5d5af4c2..95b9dbb83f7d9 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -11,6 +11,18 @@ void _addView( PlatformDispatcher.instance._addView(id); } +@pragma('vm:entry-point') +void _removeView( + Object id, +) { + PlatformDispatcher.instance._removeView(id); +} + +@pragma('vm:entry-point') +void _sendViewConfigurations(List viewIds) { + PlatformDispatcher.instance._onSentViewConfigurations(viewIds); +} + @pragma('vm:entry-point') void _updateWindowMetrics( Object id, diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index e771c9518c1fa..b6016d91a713f 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -61,6 +61,8 @@ typedef ErrorCallback = bool Function(Object exception, StackTrace stackTrace); // A gesture setting value that indicates it has not been set by the engine. const double _kUnsetGestureSetting = -1.0; +const int _kImplicitViewId = 0; + // A message channel to receive KeyData from the platform. // // See embedder.cc::kFlutterKeyDataChannel for more information. @@ -111,6 +113,12 @@ class PlatformDispatcher { /// these. Use [instance] to access the singleton. PlatformDispatcher._() { _setNeedsReportTimings = _nativeSetNeedsReportTimings; + // TODO(dkwingsmt): Can not call _implicitViewEnabled here because + // the Dart state has not finished initialization. + // if (_implicitViewEnabled()) { + // print('PlatformDispatcher ctor 2'); + // _implicitView = FlutterView._(_kImplicitViewId, this); + // } } /// The [PlatformDispatcher] singleton. @@ -153,6 +161,10 @@ class PlatformDispatcher { /// the application. /// /// If any of their configurations change, [onMetricsChanged] will be called. + /// + /// If implicit view is enabled but the implicit view is currently closed, + /// this list will not include the implicit view, but [implicitView] will + /// still be a non-null value. Iterable get views => _views.values; final Map _views = {}; @@ -179,12 +191,23 @@ class PlatformDispatcher { /// the application is shut down (although the engine may replace or remove /// the underlying backing surface of the view at its discretion). /// + /// If the implicit view is currently closed, [views] will not include this + /// value, but this property is still non-null, and single-view apps that + /// render into this property will still be able to do so, resulting in a + /// no-op. + /// /// See also: /// /// * [View.of], for accessing the current view. /// * [PlatformDispatcher.views] for a list of all [FlutterView]s provided /// by the platform. - FlutterView? get implicitView => _implicitViewEnabled() ? _views[0] : null; + FlutterView? get implicitView { + if (_implicitView == null && _implicitViewEnabled()) { + _implicitView = FlutterView._(_kImplicitViewId, this); + } + return _implicitView; + } + FlutterView? _implicitView; @Native(symbol: 'PlatformConfigurationNativeApi::ImplicitViewEnabled') external static bool _implicitViewEnabled(); @@ -215,12 +238,31 @@ class PlatformDispatcher { _onMetricsChangedZone = Zone.current; } + FlutterView _createView(Object id) { + if (id == _kImplicitViewId) { + assert(_implicitViewEnabled()); + return implicitView!; + } + return FlutterView._(id, this); + } + void _addView(Object id) { assert(!_views.containsKey(id)); - _views[id] = FlutterView._(id, this); + _views[id] = _createView(id); _viewConfigurations[id] = const _ViewConfiguration(); } + void _removeView(Object id) { + assert(!_views.containsKey(id)); + // TODO(dkwingsmt): Reset _implicitView? + _views.remove(id); + _viewConfigurations.remove(id); + } + + void _onSentViewConfigurations(List viewIds) { + viewIds.forEach(_addView); + } + // Called from the engine, via hooks.dart // // Updates the metrics of the window with the given id. @@ -248,9 +290,7 @@ class PlatformDispatcher { ) { final _ViewConfiguration previousConfiguration = _viewConfigurations[id] ?? const _ViewConfiguration(); - if (!_views.containsKey(id)) { - _views[id] = FlutterView._(id, this); - } + _views.putIfAbsent(id, () => _createView(id)); _viewConfigurations[id] = previousConfiguration.copyWith( view: _views[id], devicePixelRatio: devicePixelRatio, diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 3b97a597a25a5..bd9a7744daaa9 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -22,7 +22,7 @@ namespace flutter { namespace { -constexpr int kImplicitViewId = 0; +constexpr int64_t kFlutterDefaultViewId = 0ll; Dart_Handle ToByteData(const fml::Mapping& buffer) { return tonic::DartByteData::Create(buffer.GetMapping(), buffer.GetSize()); @@ -34,7 +34,11 @@ PlatformConfigurationClient::~PlatformConfigurationClient() {} PlatformConfiguration::PlatformConfiguration( PlatformConfigurationClient* client) - : client_(client) {} + : client_(client) { + if (client_->ImplicitViewEnabled()) { + AddView(kFlutterDefaultViewId); + } +} PlatformConfiguration::~PlatformConfiguration() {} @@ -45,6 +49,8 @@ void PlatformConfiguration::DidCreateIsolate() { Dart_GetField(library, tonic::ToDart("_onError"))); add_view_.Set(tonic::DartState::Current(), Dart_GetField(library, tonic::ToDart("_addView"))); + remove_view_.Set(tonic::DartState::Current(), + Dart_GetField(library, tonic::ToDart("_removeView"))); update_locales_.Set(tonic::DartState::Current(), Dart_GetField(library, tonic::ToDart("_updateLocales"))); update_user_settings_data_.Set( @@ -72,20 +78,33 @@ void PlatformConfiguration::DidCreateIsolate() { report_timings_.Set(tonic::DartState::Current(), Dart_GetField(library, tonic::ToDart("_reportTimings"))); - // TODO(loicsharma): This should only be created if the embedder enables the - // implicit view. - // See: https://github.com/flutter/flutter/issues/120306 - windows_.emplace(kImplicitViewId, - std::make_unique( - kImplicitViewId, ViewportMetrics{1.0, 0.0, 0.0, -1})); + library_.Set(tonic::DartState::Current(), + Dart_LookupLibrary(tonic::ToDart("dart:ui"))); + + SendViewConfigurations(); } -constexpr int64_t kFlutterDefaultViewId = 0ll; +void PlatformConfiguration::SendViewConfigurations() { + std::shared_ptr dart_state = library_.dart_state().lock(); + FML_DCHECK(dart_state); + tonic::DartState::Scope scope(dart_state); -void PlatformConfiguration::AddView(int64_t view_id) { - if (view_id == kFlutterDefaultViewId) { - return; + // TODO(dkwingsmt): send all of ViewportMetrics + std::vector view_ids; + for (const auto& [view_id, window] : windows_) { + view_ids.push_back(view_id); } + + tonic::CheckAndHandleError(tonic::DartInvokeField( + library_.value(), "_sendViewConfigurations", {tonic::ToDart(view_ids)})); +} + +void PlatformConfiguration::AddView(int64_t view_id) { + // TODO(dkwingsmt): How do I access the current dart state after + // DidCreateIsolate? + windows_.emplace( + view_id, std::make_unique(library_, view_id, + ViewportMetrics{1.0, 0.0, 0.0, -1})); std::shared_ptr dart_state = add_view_.dart_state().lock(); if (!dart_state) { return; @@ -93,8 +112,21 @@ void PlatformConfiguration::AddView(int64_t view_id) { tonic::DartState::Scope scope(dart_state); tonic::CheckAndHandleError( tonic::DartInvoke(add_view_.Get(), { - tonic::ToDart(view_id), - })); + tonic::ToDart(view_id), + })); +} + +void PlatformConfiguration::RemoveView(int64_t view_id) { + windows_.erase(view_id); + std::shared_ptr dart_state = remove_view_.dart_state().lock(); + if (!dart_state) { + return; + } + tonic::DartState::Scope scope(dart_state); + tonic::CheckAndHandleError( + tonic::DartInvoke(remove_view_.Get(), { + tonic::ToDart(view_id), + })); } void PlatformConfiguration::UpdateLocales( diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index 61245a343ea4c..e9b17c61f7157 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -269,6 +269,8 @@ class PlatformConfiguration final { void AddView(int64_t view_id); + void RemoveView(int64_t view_id); + //---------------------------------------------------------------------------- /// @brief Update the specified locale data in the framework. /// @@ -409,7 +411,14 @@ class PlatformConfiguration final { /// /// @return a pointer to the Window. /// - Window* get_window(int window_id) { return windows_[window_id].get(); } + Window* get_window(int window_id) { + auto found = windows_.find(window_id); + if (found != windows_.end()) { + return found->second.get(); + } else { + return nullptr; + } + } //---------------------------------------------------------------------------- /// @brief Responds to a previous platform message to the engine from the @@ -437,6 +446,7 @@ class PlatformConfiguration final { PlatformConfigurationClient* client_; tonic::DartPersistentValue on_error_; tonic::DartPersistentValue add_view_; + tonic::DartPersistentValue remove_view_; tonic::DartPersistentValue update_locales_; tonic::DartPersistentValue update_user_settings_data_; tonic::DartPersistentValue update_initial_lifecycle_state_; @@ -448,12 +458,20 @@ class PlatformConfiguration final { tonic::DartPersistentValue draw_frame_; tonic::DartPersistentValue report_timings_; + tonic::DartPersistentValue library_; + + // All *actual* views that the app has. + // + // This means that, if implicit view is enabled but the implicit view is + // currently closed, `windows_` will not have an entry for ID 0. std::unordered_map> windows_; // ID starts at 1 because an ID of 0 indicates that no response is expected. int next_response_id_ = 1; std::unordered_map> pending_responses_; + + void SendViewConfigurations(); }; //---------------------------------------------------------------------------- diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index b269e97f7bf87..0dd181d1c5e98 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -13,11 +13,12 @@ namespace flutter { -Window::Window(int64_t window_id, ViewportMetrics metrics) - : window_id_(window_id), viewport_metrics_(std::move(metrics)) { - library_.Set(tonic::DartState::Current(), - Dart_LookupLibrary(tonic::ToDart("dart:ui"))); -} +Window::Window(tonic::DartPersistentValue& library, + int64_t window_id, + ViewportMetrics metrics) + : library_(library), + window_id_(window_id), + viewport_metrics_(std::move(metrics)) {} Window::~Window() {} diff --git a/lib/ui/window/window.h b/lib/ui/window/window.h index aad79f9963f72..f4763174c43eb 100644 --- a/lib/ui/window/window.h +++ b/lib/ui/window/window.h @@ -20,7 +20,9 @@ namespace flutter { class Window final { public: - Window(int64_t window_id, ViewportMetrics metrics); + Window(tonic::DartPersistentValue& library, + int64_t window_id, + ViewportMetrics metrics); ~Window(); @@ -34,7 +36,7 @@ class Window final { void UpdateWindowMetrics(const ViewportMetrics& metrics); private: - tonic::DartPersistentValue library_; + tonic::DartPersistentValue& library_; int64_t window_id_; ViewportMetrics viewport_metrics_; }; diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index c4ccd8e153034..aa5575d1ec31a 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -112,8 +112,12 @@ std::unique_ptr RuntimeController::Clone() const { ); } +constexpr int64_t kFlutterDefaultViewId = 0ll; + bool RuntimeController::FlushRuntimeStateToIsolate() { - return SetViewportMetrics(platform_data_.viewport_metrics) && + // TODO(dkwingsmt) + return SetViewportMetrics(kFlutterDefaultViewId, + platform_data_.viewport_metrics) && SetLocales(platform_data_.locale_data) && SetSemanticsEnabled(platform_data_.semantics_enabled) && SetAccessibilityFeatures( @@ -131,13 +135,26 @@ bool RuntimeController::AddView(int64_t view_id) { return false; } -bool RuntimeController::SetViewportMetrics(const ViewportMetrics& metrics) { +bool RuntimeController::RemoveView(int64_t view_id) { + if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { + platform_configuration->RemoveView(view_id); + return true; + } + + return false; +} + +bool RuntimeController::SetViewportMetrics(int64_t view_id, + const ViewportMetrics& metrics) { TRACE_EVENT0("flutter", "SetViewportMetrics"); platform_data_.viewport_metrics = metrics; if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { - platform_configuration->get_window(0)->UpdateWindowMetrics(metrics); - return true; + Window* window = platform_configuration->get_window(view_id); + if (window) { + window->UpdateWindowMetrics(metrics); + return true; + } } return false; diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index c88f97e3a292a..d16fe3edaae92 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -165,6 +165,8 @@ class RuntimeController : public PlatformConfigurationClient { bool AddView(int64_t view_id); + bool RemoveView(int64_t view_id); + //---------------------------------------------------------------------------- /// @brief Forward the specified viewport metrics to the running isolate. /// If the isolate is not running, these metrics will be saved and @@ -174,7 +176,7 @@ class RuntimeController : public PlatformConfigurationClient { /// /// @return If the window metrics were forwarded to the running isolate. /// - bool SetViewportMetrics(const ViewportMetrics& metrics); + bool SetViewportMetrics(int64_t view_id, const ViewportMetrics& metrics); //---------------------------------------------------------------------------- /// @brief Forward the specified locale data to the running isolate. If diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 95710dfcaf447..04d730e4afa44 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -290,8 +290,13 @@ void Engine::AddView(int64_t view_id) { runtime_controller_->AddView(view_id); } -void Engine::SetViewportMetrics(const ViewportMetrics& metrics) { - runtime_controller_->SetViewportMetrics(metrics); +void Engine::RemoveView(int64_t view_id) { + runtime_controller_->RemoveView(view_id); +} + +void Engine::SetViewportMetrics(int64_t view_id, + const ViewportMetrics& metrics) { + runtime_controller_->SetViewportMetrics(view_id, metrics); ScheduleFrame(); } diff --git a/shell/common/engine.h b/shell/common/engine.h index 34774bf59dfbb..839fb35ce9df7 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -675,6 +675,8 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { void AddView(int64_t view_id); + void RemoveView(int64_t view_id); + //---------------------------------------------------------------------------- /// @brief Updates the viewport metrics for the currently running Flutter /// application. The viewport metrics detail the size of the @@ -685,7 +687,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { /// /// @param[in] metrics The metrics /// - void SetViewportMetrics(const ViewportMetrics& metrics); + void SetViewportMetrics(int64_t view_id, const ViewportMetrics& metrics); //---------------------------------------------------------------------------- /// @brief Notifies the engine that the embedder has sent it a message. diff --git a/shell/common/engine_unittests.cc b/shell/common/engine_unittests.cc index 85c4656506485..628810704dac9 100644 --- a/shell/common/engine_unittests.cc +++ b/shell/common/engine_unittests.cc @@ -21,6 +21,8 @@ namespace flutter { namespace { +constexpr int64_t kDefaultViewId = 0ll; + class MockDelegate : public Engine::Delegate { public: MOCK_METHOD2(OnEngineUpdateSemantics, @@ -322,7 +324,8 @@ TEST_F(EngineTest, SpawnResetsViewportMetrics) { const double kViewHeight = 1024; old_viewport_metrics.physical_width = kViewWidth; old_viewport_metrics.physical_height = kViewHeight; - mock_runtime_controller->SetViewportMetrics(old_viewport_metrics); + mock_runtime_controller->SetViewportMetrics(kDefaultViewId, + old_viewport_metrics); auto engine = std::make_unique( /*delegate=*/delegate_, /*dispatcher_maker=*/dispatcher_maker_, diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index 87f3af8e6b3df..cff0b1c797643 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -51,8 +51,9 @@ void PlatformView::SetAccessibilityFeatures(int32_t flags) { delegate_.OnPlatformViewSetAccessibilityFeatures(flags); } -void PlatformView::SetViewportMetrics(const ViewportMetrics& metrics) { - delegate_.OnPlatformViewSetViewportMetrics(metrics); +void PlatformView::SetViewportMetrics(int64_t view_id, + const ViewportMetrics& metrics) { + delegate_.OnPlatformViewSetViewportMetrics(view_id, metrics); } void PlatformView::NotifyCreated() { diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 7d698ec7b9636..3c12332c1d276 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -108,6 +108,7 @@ class PlatformView { /// @param[in] metrics The updated viewport metrics. /// virtual void OnPlatformViewSetViewportMetrics( + int64_t view_id, const ViewportMetrics& metrics) = 0; //-------------------------------------------------------------------------- @@ -470,7 +471,7 @@ class PlatformView { /// /// @param[in] metrics The updated viewport metrics. /// - void SetViewportMetrics(const ViewportMetrics& metrics); + void SetViewportMetrics(int64_t view_id, const ViewportMetrics& metrics); //---------------------------------------------------------------------------- /// @brief Used by embedders to notify the shell that a platform view diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 8fcb10f6393b6..78937353df8ce 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -675,8 +675,8 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( if (external_view_embedder_ && (!raster_thread_merger_ || raster_thread_merger_->IsMerged())) { FML_DCHECK(!frame->IsSubmitted()); - external_view_embedder_->SubmitFrame(studio_->GetContext(), - std::move(frame)); + external_view_embedder_->SubmitFrame( + studio_->GetContext(), std::move(frame), surface_record->view_id); } else { frame->Submit(); } diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 54384a81ce9bb..1de2050b0ab86 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -242,7 +242,8 @@ class Rasterizer final : public SnapshotDelegate, std::shared_ptr GetTextureRegistry() override; - using LayerTreeDiscardCallback = std::function; + using LayerTreeDiscardCallback = + std::function; //---------------------------------------------------------------------------- /// @brief Takes the next item from the layer tree pipeline and executes @@ -596,10 +597,9 @@ class Rasterizer final : public SnapshotDelegate, GrDirectContext* surface_context, bool compressed); - DoDrawResult DoDraw( - int64_t view_id, - std::unique_ptr frame_timings_recorder, - std::shared_ptr layer_tree); + DoDrawResult DoDraw(int64_t view_id, + FrameTimingsRecorder& frame_timings_recorder, + std::shared_ptr layer_tree); RasterStatus DrawToSurface(FrameTimingsRecorder& frame_timings_recorder, flutter::LayerTree* layer_tree, @@ -615,7 +615,9 @@ class Rasterizer final : public SnapshotDelegate, void FireNextFrameCallbackIfPresent(); - static bool NoDiscard(const flutter::LayerTree& layer_tree) { return false; } + static bool NoDiscard(int64_t view_id, const flutter::LayerTree& layer_tree) { + return false; + } static bool ShouldResubmitFrame(const RasterStatus& raster_status); Delegate& delegate_; diff --git a/shell/common/rasterizer_unittests.cc b/shell/common/rasterizer_unittests.cc index b6efdf9df19f3..7c07515366efc 100644 --- a/shell/common/rasterizer_unittests.cc +++ b/shell/common/rasterizer_unittests.cc @@ -82,9 +82,10 @@ class MockExternalViewEmbedder : public ExternalViewEmbedder { PostPrerollResult( fml::RefPtr raster_thread_merger)); MOCK_METHOD1(CompositeEmbeddedView, DlCanvas*(int64_t view_id)); - MOCK_METHOD2(SubmitFrame, + MOCK_METHOD3(SubmitFrame, void(GrDirectContext* context, - std::unique_ptr frame)); + std::unique_ptr frame, + int64_t window_view_id)); MOCK_METHOD2(EndFrame, void(bool should_resubmit_frame, fml::RefPtr raster_thread_merger)); @@ -208,7 +209,7 @@ TEST(RasterizerTest, PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); - auto no_discard = [](LayerTree&) { return false; }; + auto no_discard = [](int64_t, LayerTree&) { return false; }; rasterizer->Draw(pipeline, no_discard); latch.Signal(); }); @@ -275,7 +276,7 @@ TEST( PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); - auto no_discard = [](LayerTree&) { return false; }; + auto no_discard = [](int64_t, LayerTree&) { return false; }; rasterizer->Draw(pipeline, no_discard); latch.Signal(); }); @@ -348,7 +349,7 @@ TEST( PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); - auto no_discard = [](LayerTree&) { return false; }; + auto no_discard = [](int64_t, LayerTree&) { return false; }; rasterizer->Draw(pipeline, no_discard); } @@ -424,7 +425,7 @@ TEST(RasterizerTest, PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); - auto no_discard = [](LayerTree&) { return false; }; + auto no_discard = [](int64_t, LayerTree&) { return false; }; // The Draw() will respectively call BeginFrame(), SubmitFrame() and // EndFrame() one time. @@ -473,7 +474,7 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNoSurfaceIsSet) { PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); - auto no_discard = [](LayerTree&) { return false; }; + auto no_discard = [](int64_t, LayerTree&) { return false; }; rasterizer->Draw(pipeline, no_discard); latch.Signal(); }); @@ -531,7 +532,7 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNotUsedThisFrame) { pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); // Always discard the layer tree. - auto discard_callback = [](LayerTree&) { return true; }; + auto discard_callback = [](int64_t, LayerTree&) { return true; }; RasterStatus status = rasterizer->Draw(pipeline, discard_callback); EXPECT_EQ(status, RasterStatus::kDiscarded); latch.Signal(); @@ -577,7 +578,7 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenPipelineIsEmpty) { fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); - auto no_discard = [](LayerTree&) { return false; }; + auto no_discard = [](int64_t, LayerTree&) { return false; }; RasterStatus status = rasterizer->Draw(pipeline, no_discard); EXPECT_EQ(status, RasterStatus::kFailed); latch.Signal(); @@ -636,7 +637,7 @@ TEST(RasterizerTest, PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); - auto no_discard = [](LayerTree&) { return false; }; + auto no_discard = [](int64_t, LayerTree&) { return false; }; rasterizer->Draw(pipeline, no_discard); latch.Signal(); }); @@ -695,7 +696,7 @@ TEST( PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); - auto no_discard = [](LayerTree&) { return false; }; + auto no_discard = [](int64_t, LayerTree&) { return false; }; RasterStatus status = rasterizer->Draw(pipeline, no_discard); EXPECT_EQ(status, RasterStatus::kSuccess); latch.Signal(); @@ -754,7 +755,7 @@ TEST( PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); - auto no_discard = [](LayerTree&) { return false; }; + auto no_discard = [](int64_t, LayerTree&) { return false; }; RasterStatus status = rasterizer->Draw(pipeline, no_discard); EXPECT_EQ(status, RasterStatus::kSuccess); latch.Signal(); @@ -812,7 +813,7 @@ TEST( PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); - auto no_discard = [](LayerTree&) { return false; }; + auto no_discard = [](int64_t, LayerTree&) { return false; }; RasterStatus status = rasterizer->Draw(pipeline, no_discard); EXPECT_EQ(status, RasterStatus::kDiscarded); latch.Signal(); @@ -869,7 +870,7 @@ TEST( PipelineProduceResult result = pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); - auto no_discard = [](LayerTree&) { return false; }; + auto no_discard = [](int64_t, LayerTree&) { return false; }; RasterStatus status = rasterizer->Draw(pipeline, no_discard); EXPECT_EQ(status, RasterStatus::kFailed); latch.Signal(); @@ -953,7 +954,7 @@ TEST(RasterizerTest, EXPECT_TRUE(result.success); EXPECT_EQ(result.is_first_item, i == 0); } - auto no_discard = [](LayerTree&) { return false; }; + auto no_discard = [](int64_t, LayerTree&) { return false; }; // Although we only call 'Rasterizer::Draw' once, it will be called twice // finally because there are two items in the pipeline. rasterizer->Draw(pipeline, no_discard); @@ -1129,7 +1130,7 @@ TEST(RasterizerTest, presentationTimeSetWhenVsyncTargetInFuture) { EXPECT_TRUE(result.success); EXPECT_EQ(result.is_first_item, i == 0); } - auto no_discard = [](LayerTree&) { return false; }; + auto no_discard = [](int64_t, LayerTree&) { return false; }; // Although we only call 'Rasterizer::Draw' once, it will be called twice // finally because there are two items in the pipeline. rasterizer->Draw(pipeline, no_discard); @@ -1210,7 +1211,7 @@ TEST(RasterizerTest, presentationTimeNotSetWhenVsyncTargetInPast) { pipeline->Produce().Complete(std::move(layer_tree_item)); EXPECT_TRUE(result.success); EXPECT_EQ(result.is_first_item, true); - auto no_discard = [](LayerTree&) { return false; }; + auto no_discard = [](int64_t, LayerTree&) { return false; }; rasterizer->Draw(pipeline, no_discard); }); diff --git a/shell/common/shell.cc b/shell/common/shell.cc index f7738c196f503..c340c7dcdb932 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -940,7 +940,8 @@ void Shell::OnPlatformViewScheduleFrame() { } // |PlatformView::Delegate| -void Shell::OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) { +void Shell::OnPlatformViewSetViewportMetrics(int64_t view_id, + const ViewportMetrics& metrics) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); @@ -964,15 +965,15 @@ void Shell::OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) { }); task_runners_.GetUITaskRunner()->PostTask( - [engine = engine_->GetWeakPtr(), metrics]() { + [engine = engine_->GetWeakPtr(), metrics, view_id]() { if (engine) { - engine->SetViewportMetrics(metrics); + engine->SetViewportMetrics(view_id, metrics); } }); { std::scoped_lock lock(resize_mutex_); - expected_frame_size_ = + expected_frame_sizes_[view_id] = SkISize::Make(metrics.physical_width, metrics.physical_height); device_pixel_ratio_ = metrics.device_pixel_ratio; } @@ -1179,10 +1180,11 @@ void Shell::OnAnimatorUpdateLatestFrameTargetTime( void Shell::OnAnimatorDraw(std::shared_ptr pipeline) { FML_DCHECK(is_setup_); - auto discard_callback = [this](flutter::LayerTree& tree) { + auto discard_callback = [this](int64_t view_id, flutter::LayerTree& tree) { std::scoped_lock lock(resize_mutex_); - return !expected_frame_size_.isEmpty() && - tree.frame_size() != expected_frame_size_; + auto expected_frame_size = ExpectedFrameSize(view_id); + return !expected_frame_size.isEmpty() && + tree.frame_size() != expected_frame_size; }; task_runners_.GetRasterTaskRunner()->PostTask(fml::MakeCopyable( @@ -1933,7 +1935,8 @@ bool Shell::OnServiceProtocolRenderFrameWithRasterStats( response->AddMember("snapshots", snapshots, allocator); - const auto& frame_size = expected_frame_size_; + // TODO(dkwingsmt) + const auto& frame_size = ExpectedFrameSize(kFlutterDefaultViewId); response->AddMember("frame_width", frame_size.width(), allocator); response->AddMember("frame_height", frame_size.height(), allocator); @@ -2000,15 +2003,14 @@ void Shell::AddRenderSurface(int64_t view_id) { return; } - auto ui_task = [engine = engine_->GetWeakPtr(), // - view_id // - ] { engine->AddView(view_id); }; + task_runners_.GetUITaskRunner()->PostTask([engine = engine_->GetWeakPtr(), // + view_id // + ] { engine->AddView(view_id); }); + // TODO(dkwingsmt): platform_view_ is captured illegally here. // We need some mechanism from it being collected. task_runners_.GetRasterTaskRunner()->PostTask( - fml::MakeCopyable([&task_runners = task_runners_, // - ui_task, // - platform_view = platform_view_.get(), // + fml::MakeCopyable([platform_view = platform_view_.get(), // rasterizer = rasterizer_->GetWeakPtr(), // view_id // ]() mutable { @@ -2018,7 +2020,6 @@ void Shell::AddRenderSurface(int64_t view_id) { if (surface) { rasterizer->AddSurface(view_id, std::move(surface)); } - task_runners.GetUITaskRunner()->PostTask(ui_task); } })); } @@ -2029,7 +2030,10 @@ void Shell::RemoveRenderSurface(int64_t view_id) { FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); fml::AutoResetWaitableEvent latch; - // platform_view_->RemoveSurface(view_id); // TODO + // platform_view_->RemoveSurface(view_id); + task_runners_.GetUITaskRunner()->PostTask([engine = engine_->GetWeakPtr(), // + view_id // + ] { engine->RemoveView(view_id); }); task_runners_.GetRasterTaskRunner()->PostTask( [&latch, // rasterizer = rasterizer_->GetWeakPtr(), // @@ -2157,4 +2161,12 @@ const std::weak_ptr Shell::GetVsyncWaiter() const { return engine_->GetVsyncWaiter(); } +SkISize Shell::ExpectedFrameSize(int64_t view_id) { + auto found = expected_frame_sizes_.find(view_id); + if (found == expected_frame_sizes_.end()) { + return SkISize::MakeEmpty(); + } + return found->second; +} + } // namespace flutter diff --git a/shell/common/shell.h b/shell/common/shell.h index 5f7e62272757e..1382b659966ec 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -472,7 +472,7 @@ class Shell final : public PlatformView::Delegate, std::mutex resize_mutex_; // used to discard wrong size layer tree produced during interactive resizing - SkISize expected_frame_size_ = SkISize::MakeEmpty(); + std::unordered_map expected_frame_sizes_; // Used to communicate the right frame bounds via service protocol. double device_pixel_ratio_ = 0.0; @@ -537,6 +537,7 @@ class Shell final : public PlatformView::Delegate, // |PlatformView::Delegate| void OnPlatformViewSetViewportMetrics( + int64_t view_id, const ViewportMetrics& metrics) override; // |PlatformView::Delegate| @@ -733,6 +734,8 @@ class Shell final : public PlatformView::Delegate, // directory. std::unique_ptr RestoreOriginalAssetResolver(); + SkISize ExpectedFrameSize(int64_t view_id); + // For accessing the Shell via the raster thread, necessary for various // rasterizer callbacks. std::unique_ptr> weak_factory_gpu_; diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index d5fab1f5be7b9..bd05c8726e9e0 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -140,7 +140,7 @@ void ShellTest::SetViewportMetrics(Shell* shell, double width, double height) { shell->GetTaskRunners().GetUITaskRunner()->PostTask( [&latch, engine = shell->weak_engine_, viewport_metrics]() { if (engine) { - engine->SetViewportMetrics(viewport_metrics); + engine->SetViewportMetrics(kDefaultViewId, viewport_metrics); const auto frame_begin_time = fml::TimePoint::Now(); const auto frame_end_time = frame_begin_time + fml::TimeDelta::FromSecondsF(1.0 / 60.0); @@ -182,7 +182,7 @@ void ShellTest::PumpOneFrame(Shell* shell, fml::AutoResetWaitableEvent latch; shell->GetTaskRunners().GetUITaskRunner()->PostTask( [&latch, engine = shell->weak_engine_, viewport_metrics]() { - engine->SetViewportMetrics(viewport_metrics); + engine->SetViewportMetrics(kDefaultViewId, viewport_metrics); const auto frame_begin_time = fml::TimePoint::Now(); const auto frame_end_time = frame_begin_time + fml::TimeDelta::FromSecondsF(1.0 / 60.0); diff --git a/shell/common/shell_test_external_view_embedder.cc b/shell/common/shell_test_external_view_embedder.cc index 2536bb62ac6a2..475de0cd501da 100644 --- a/shell/common/shell_test_external_view_embedder.cc +++ b/shell/common/shell_test_external_view_embedder.cc @@ -91,7 +91,8 @@ DlCanvas* ShellTestExternalViewEmbedder::CompositeEmbeddedView( // |ExternalViewEmbedder| void ShellTestExternalViewEmbedder::SubmitFrame( GrDirectContext* context, - std::unique_ptr frame) { + std::unique_ptr frame, + int64_t view_id) { if (!frame) { return; } diff --git a/shell/common/shell_test_external_view_embedder.h b/shell/common/shell_test_external_view_embedder.h index 204472d5bc050..4aacfc6f7c401 100644 --- a/shell/common/shell_test_external_view_embedder.h +++ b/shell/common/shell_test_external_view_embedder.h @@ -75,7 +75,8 @@ class ShellTestExternalViewEmbedder final : public ExternalViewEmbedder { // |ExternalViewEmbedder| void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame) override; + std::unique_ptr frame, + int64_t view_id) override; // |ExternalViewEmbedder| void EndFrame( diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index c0b57188573b5..83b48e8b4fbd3 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -57,6 +57,8 @@ namespace flutter { namespace testing { +constexpr int64_t kDefaultViewId = 0ll; + using ::testing::_; using ::testing::Return; @@ -71,8 +73,8 @@ class MockPlatformViewDelegate : public PlatformView::Delegate { MOCK_METHOD1(OnPlatformViewSetNextFrameCallback, void(const fml::closure& closure)); - MOCK_METHOD1(OnPlatformViewSetViewportMetrics, - void(const ViewportMetrics& metrics)); + MOCK_METHOD2(OnPlatformViewSetViewportMetrics, + void(int64_t view_id, const ViewportMetrics& metrics)); MOCK_METHOD1(OnPlatformViewDispatchPlatformMessage, void(std::unique_ptr message)); @@ -1600,7 +1602,8 @@ TEST_F(ShellTest, MultipleFluttersSetResourceCacheBytes) { RunEngine(shell.get(), std::move(configuration)); PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { - shell->GetPlatformView()->SetViewportMetrics({1.0, 100, 100, 22}); + shell->GetPlatformView()->SetViewportMetrics(kDefaultViewId, + {1.0, 100, 100, 22}); }); // first cache bytes @@ -1629,7 +1632,7 @@ TEST_F(ShellTest, MultipleFluttersSetResourceCacheBytes) { PostSync(second_shell->GetTaskRunners().GetPlatformTaskRunner(), [&second_shell]() { second_shell->GetPlatformView()->SetViewportMetrics( - {1.0, 100, 100, 22}); + kDefaultViewId, {1.0, 100, 100, 22}); }); // first cache bytes + second cache bytes EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell), @@ -1638,7 +1641,7 @@ TEST_F(ShellTest, MultipleFluttersSetResourceCacheBytes) { PostSync(second_shell->GetTaskRunners().GetPlatformTaskRunner(), [&second_shell]() { second_shell->GetPlatformView()->SetViewportMetrics( - {1.0, 100, 300, 22}); + kDefaultViewId, {1.0, 100, 300, 22}); }); // first cache bytes + second cache bytes EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell), @@ -1646,18 +1649,20 @@ TEST_F(ShellTest, MultipleFluttersSetResourceCacheBytes) { std::unique_ptr third_shell = shell_spawn_callback(); PlatformViewNotifyCreated(third_shell.get()); - PostSync( - third_shell->GetTaskRunners().GetPlatformTaskRunner(), [&third_shell]() { - third_shell->GetPlatformView()->SetViewportMetrics({1.0, 400, 100, 22}); - }); + PostSync(third_shell->GetTaskRunners().GetPlatformTaskRunner(), + [&third_shell]() { + third_shell->GetPlatformView()->SetViewportMetrics( + kDefaultViewId, {1.0, 400, 100, 22}); + }); // first cache bytes + second cache bytes + third cache bytes EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell), static_cast(3840000U)); - PostSync( - third_shell->GetTaskRunners().GetPlatformTaskRunner(), [&third_shell]() { - third_shell->GetPlatformView()->SetViewportMetrics({1.0, 800, 100, 22}); - }); + PostSync(third_shell->GetTaskRunners().GetPlatformTaskRunner(), + [&third_shell]() { + third_shell->GetPlatformView()->SetViewportMetrics( + kDefaultViewId, {1.0, 800, 100, 22}); + }); // max bytes threshold EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell), static_cast(4000000U)); @@ -1669,7 +1674,7 @@ TEST_F(ShellTest, MultipleFluttersSetResourceCacheBytes) { PostSync(second_shell->GetTaskRunners().GetPlatformTaskRunner(), [&second_shell]() { second_shell->GetPlatformView()->SetViewportMetrics( - {1.0, 100, 100, 22}); + kDefaultViewId, {1.0, 100, 100, 22}); }); // first cache bytes + second cache bytes EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell), @@ -1713,7 +1718,8 @@ TEST_F(ShellTest, SetResourceCacheSize) { fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { - shell->GetPlatformView()->SetViewportMetrics({1.0, 400, 200, 22}); + shell->GetPlatformView()->SetViewportMetrics(kDefaultViewId, + {1.0, 400, 200, 22}); }); PumpOneFrame(shell.get()); @@ -1733,7 +1739,8 @@ TEST_F(ShellTest, SetResourceCacheSize) { fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { - shell->GetPlatformView()->SetViewportMetrics({1.0, 800, 400, 22}); + shell->GetPlatformView()->SetViewportMetrics(kDefaultViewId, + {1.0, 800, 400, 22}); }); PumpOneFrame(shell.get()); @@ -1750,7 +1757,8 @@ TEST_F(ShellTest, SetResourceCacheSizeEarly) { fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { - shell->GetPlatformView()->SetViewportMetrics({1.0, 400, 200, 22}); + shell->GetPlatformView()->SetViewportMetrics(kDefaultViewId, + {1.0, 400, 200, 22}); }); PumpOneFrame(shell.get()); @@ -1777,7 +1785,8 @@ TEST_F(ShellTest, SetResourceCacheSizeNotifiesDart) { fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { - shell->GetPlatformView()->SetViewportMetrics({1.0, 400, 200, 22}); + shell->GetPlatformView()->SetViewportMetrics(kDefaultViewId, + {1.0, 400, 200, 22}); }); PumpOneFrame(shell.get()); @@ -2642,8 +2651,8 @@ TEST_F(ShellTest, DISABLED_DiscardLayerTreeOnResize) { shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell, &expected_size]() { shell->GetPlatformView()->SetViewportMetrics( - {1.0, static_cast(expected_size.width()), - static_cast(expected_size.height()), 22}); + kDefaultViewId, {1.0, static_cast(expected_size.width()), + static_cast(expected_size.height()), 22}); }); auto configuration = RunConfiguration::InferFromSettings(settings); @@ -2715,8 +2724,8 @@ TEST_F(ShellTest, DISABLED_DiscardResubmittedLayerTreeOnResize) { shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell, &origin_size]() { shell->GetPlatformView()->SetViewportMetrics( - {1.0, static_cast(origin_size.width()), - static_cast(origin_size.height()), 22}); + kDefaultViewId, {1.0, static_cast(origin_size.width()), + static_cast(origin_size.height()), 22}); }); auto configuration = RunConfiguration::InferFromSettings(settings); @@ -2734,8 +2743,8 @@ TEST_F(ShellTest, DISABLED_DiscardResubmittedLayerTreeOnResize) { shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell, &new_size, &resize_latch]() { shell->GetPlatformView()->SetViewportMetrics( - {1.0, static_cast(new_size.width()), - static_cast(new_size.height()), 22}); + kDefaultViewId, {1.0, static_cast(new_size.width()), + static_cast(new_size.height()), 22}); resize_latch.Signal(); }); @@ -2798,13 +2807,17 @@ TEST_F(ShellTest, IgnoresInvalidMetrics) { RunEngine(shell.get(), std::move(configuration)); task_runner->PostTask([&]() { - shell->GetPlatformView()->SetViewportMetrics({0.0, 400, 200, 22}); + shell->GetPlatformView()->SetViewportMetrics(kDefaultViewId, + {0.0, 400, 200, 22}); task_runner->PostTask([&]() { - shell->GetPlatformView()->SetViewportMetrics({0.8, 0.0, 200, 22}); + shell->GetPlatformView()->SetViewportMetrics(kDefaultViewId, + {0.8, 0.0, 200, 22}); task_runner->PostTask([&]() { - shell->GetPlatformView()->SetViewportMetrics({0.8, 400, 0.0, 22}); + shell->GetPlatformView()->SetViewportMetrics(kDefaultViewId, + {0.8, 400, 0.0, 22}); task_runner->PostTask([&]() { - shell->GetPlatformView()->SetViewportMetrics({0.8, 400, 200.0, 22}); + shell->GetPlatformView()->SetViewportMetrics(kDefaultViewId, + {0.8, 400, 200.0, 22}); }); }); }); @@ -2816,7 +2829,8 @@ TEST_F(ShellTest, IgnoresInvalidMetrics) { latch.Reset(); task_runner->PostTask([&]() { - shell->GetPlatformView()->SetViewportMetrics({1.2, 600, 300, 22}); + shell->GetPlatformView()->SetViewportMetrics(kDefaultViewId, + {1.2, 600, 300, 22}); }); latch.Wait(); ASSERT_EQ(last_device_pixel_ratio, 1.2); 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 f6f49320e062d..5d2692e3d89aa 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder.cc @@ -66,7 +66,8 @@ SkRect AndroidExternalViewEmbedder::GetViewRect(int64_t view_id) const { // |ExternalViewEmbedder| void AndroidExternalViewEmbedder::SubmitFrame( GrDirectContext* context, - std::unique_ptr frame) { + std::unique_ptr frame, + int64_t window_view_id) { TRACE_EVENT0("flutter", "AndroidExternalViewEmbedder::SubmitFrame"); if (!FrameHasPlatformLayers()) { 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 8a9ae366d1de2..2038b4190478c 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.h +++ b/shell/platform/android/external_view_embedder/external_view_embedder.h @@ -46,7 +46,8 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder { // |ExternalViewEmbedder| void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame) override; + std::unique_ptr frame, + int64_t view_id) override; // |ExternalViewEmbedder| PostPrerollResult PostPrerollAction( 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 aacf5ed7aa4f0..fee260dcb7dbc 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 @@ -311,7 +311,8 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame) { }, /*frame_size=*/SkISize::Make(800, 600)); - embedder->SubmitFrame(gr_context.get(), std::move(surface_frame)); + embedder->SubmitFrame(gr_context.get(), std::move(surface_frame), + kDefaultViewId); // Submits frame if no Android view in the current frame. EXPECT_TRUE(did_submit_frame); // Doesn't resubmit frame. @@ -379,7 +380,8 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame) { }, /*frame_size=*/SkISize::Make(800, 600)); - embedder->SubmitFrame(gr_context.get(), std::move(surface_frame)); + embedder->SubmitFrame(gr_context.get(), std::move(surface_frame), + kDefaultViewId); // Doesn't submit frame if there aren't Android views in the previous frame. EXPECT_FALSE(did_submit_frame); // Resubmits frame. @@ -444,7 +446,8 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - embedder->SubmitFrame(gr_context.get(), std::move(surface_frame)); + embedder->SubmitFrame(gr_context.get(), std::move(surface_frame), + kDefaultViewId); // Submits frame if there are Android views in the previous frame. EXPECT_TRUE(did_submit_frame); // Doesn't resubmit frame. @@ -552,7 +555,8 @@ TEST(AndroidExternalViewEmbedder, OverlayCoverTwoPlatformViews) { }, /*frame_size=*/SkISize::Make(800, 600)); - embedder->SubmitFrame(gr_context.get(), std::move(surface_frame)); + embedder->SubmitFrame(gr_context.get(), std::move(surface_frame), + kDefaultViewId); EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); @@ -656,7 +660,8 @@ TEST(AndroidExternalViewEmbedder, SubmitFrameOverlayComposition) { }, /*frame_size=*/SkISize::Make(800, 600)); - embedder->SubmitFrame(gr_context.get(), std::move(surface_frame)); + embedder->SubmitFrame(gr_context.get(), std::move(surface_frame), + kDefaultViewId); EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); @@ -725,7 +730,8 @@ TEST(AndroidExternalViewEmbedder, SubmitFramePlatformViewWithoutAnyOverlay) { }, /*frame_size=*/SkISize::Make(800, 600)); - embedder->SubmitFrame(gr_context.get(), std::move(surface_frame)); + embedder->SubmitFrame(gr_context.get(), std::move(surface_frame), + kDefaultViewId); EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); @@ -827,7 +833,8 @@ TEST(AndroidExternalViewEmbedder, DestroyOverlayLayersOnSizeChange) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - embedder->SubmitFrame(gr_context.get(), std::move(surface_frame)); + embedder->SubmitFrame(gr_context.get(), std::move(surface_frame), + kDefaultViewId); EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); @@ -915,7 +922,8 @@ TEST(AndroidExternalViewEmbedder, DoesNotDestroyOverlayLayersOnSizeChange) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - embedder->SubmitFrame(gr_context.get(), std::move(surface_frame)); + embedder->SubmitFrame(gr_context.get(), std::move(surface_frame), + kDefaultViewId); EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); @@ -1027,7 +1035,8 @@ TEST(AndroidExternalViewEmbedder, Teardown) { SkSurface::MakeNull(1000, 1000), framebuffer_info, [](const SurfaceFrame& surface_frame, DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - embedder->SubmitFrame(gr_context.get(), std::move(surface_frame)); + embedder->SubmitFrame(gr_context.get(), std::move(surface_frame), + kDefaultViewId); embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.h b/shell/platform/darwin/ios/ios_external_view_embedder.h index 3893ccec0c1ab..3b94df3888f2e 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.h +++ b/shell/platform/darwin/ios/ios_external_view_embedder.h @@ -51,7 +51,8 @@ class IOSExternalViewEmbedder : public ExternalViewEmbedder { // |ExternalViewEmbedder| void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame) override; + std::unique_ptr frame, + int64_t window_view_id) override; // |ExternalViewEmbedder| void EndFrame( diff --git a/shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm b/shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm index 96acf4bb2a189..8ec78d3b229a9 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm @@ -19,10 +19,7 @@ bool FlutterCompositor::CreateBackingStore(const FlutterBackingStoreConfig* config, FlutterBackingStore* backing_store_out) { - // TODO(dkwingsmt): This class only supports single-view for now. As more - // classes are gradually converted to multi-view, it should get the view ID - // from somewhere. - FlutterView* view = [view_provider_ viewForId:kFlutterDefaultViewId]; + FlutterView* view = [view_provider_ viewForId:config->view_id]; if (!view) { return false; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 913b9a64f3ae5..f4730dbc9db6a 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -660,13 +660,11 @@ - (FlutterCompositor*)createFlutterCompositor { _compositor.present_layers_callback = [](const FlutterLayer** layers, // size_t layers_count, // + int64_t view_id, // void* user_data // ) { - // TODO(dkwingsmt): This callback only supports single-view, therefore it - // only operates on the default view. To support multi-view, we need a new - // callback that also receives a view ID. - return reinterpret_cast(user_data)->Present(kFlutterDefaultViewId, - layers, layers_count); + return reinterpret_cast(user_data)->Present(view_id, layers, + layers_count); }; _compositor.avoid_backing_store_cache = true; @@ -753,12 +751,6 @@ - (nonnull NSString*)executableName { } - (void)updateWindowMetricsForViewController:(FlutterViewController*)viewController { - if (viewController.viewId != kFlutterDefaultViewId) { - // TODO(dkwingsmt): The embedder API only supports single-view for now. As - // embedder APIs are converted to multi-view, this method should support any - // views. - return; - } if (!_engine || !viewController || !viewController.viewLoaded) { return; } @@ -777,7 +769,7 @@ - (void)updateWindowMetricsForViewController:(FlutterViewController*)viewControl .left = static_cast(scaledBounds.origin.x), .top = static_cast(scaledBounds.origin.y), }; - _embedderAPI.SendWindowMetricsEvent(_engine, &windowMetricsEvent); + _embedderAPI.SendWindowMetricsEvent(_engine, viewController.viewId, &windowMetricsEvent); } - (void)sendPointerEvent:(const FlutterPointerEvent&)event { @@ -897,6 +889,7 @@ - (void)shutDownEngine { } NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator]; + [[FlutterView sharedThreadSynchronizer] shutdown]; FlutterViewController* nextViewController; while ((nextViewController = [viewControllerEnumerator nextObject])) { [nextViewController.flutterView shutdown]; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm index af2cc140f909f..ccb3d06834cd4 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm @@ -24,6 +24,8 @@ // CREATE_NATIVE_ENTRY and MOCK_ENGINE_PROC are leaky by design // NOLINTBEGIN(clang-analyzer-core.StackAddressEscape) +constexpr int64_t kDefaultViewId = 0ll; + @interface FlutterEngine (Test) /** * The FlutterCompositor object currently in use by the FlutterEngine. @@ -626,9 +628,10 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable [threadSynchronizer shutdown]; std::thread rasterThread([&threadSynchronizer] { - [threadSynchronizer performCommit:CGSizeMake(100, 100) - notify:^{ - }]; + [threadSynchronizer performCommitForView:kDefaultViewId + size:CGSizeMake(100, 100) + notify:^{ + }]; }); rasterThread.join(); diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h index 8eca9611f0f48..5cc399cb33279 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h @@ -15,7 +15,9 @@ * Called from platform thread. Blocks until commit with given size (or empty) * is requested. */ -- (void)beginResize:(CGSize)size notify:(nonnull dispatch_block_t)notify; +- (void)beginResizeForView:(int64_t)viewId + size:(CGSize)size + notify:(nonnull dispatch_block_t)notify; /** * Called from raster thread. Schedules the given block on platform thread @@ -26,7 +28,13 @@ * * The notify block is guaranteed to be called within a core animation transaction. */ -- (void)performCommit:(CGSize)size notify:(nonnull dispatch_block_t)notify; +- (void)performCommitForView:(int64_t)viewId + size:(CGSize)size + notify:(nonnull dispatch_block_t)notify; + +- (void)registerView:(int64_t)viewId; + +- (void)deregisterView:(int64_t)viewId; /** * Called when shutting down. Unblocks everything and prevents any further synchronization. diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm index cb442ea768ffb..bf4506994e3cd 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm @@ -3,6 +3,7 @@ #import #include +#include #include #import "flutter/fml/logging.h" @@ -11,7 +12,7 @@ @interface FlutterThreadSynchronizer () { std::mutex _mutex; BOOL _shuttingDown; - CGSize _contentSize; + std::unordered_map _contentSizes; std::vector _scheduledBlocks; BOOL _beginResizeWaiting; @@ -20,10 +21,21 @@ @interface FlutterThreadSynchronizer () { std::condition_variable _condBlockBeginResize; } +- (BOOL)allViewsHaveFrame; + @end @implementation FlutterThreadSynchronizer +- (BOOL)allViewsHaveFrame { + for (auto const& [viewId, contentSize] : _contentSizes) { + if (CGSizeEqualToSize(contentSize, CGSizeZero)) { + return NO; + } + } + return YES; +} + - (void)drain { FML_DCHECK([NSThread isMainThread]); @@ -41,7 +53,7 @@ - (void)blockUntilFrameAvailable { _beginResizeWaiting = YES; - while (CGSizeEqualToSize(_contentSize, CGSizeZero) && !_shuttingDown) { + while (![self allViewsHaveFrame] && !_shuttingDown) { _condBlockBeginResize.wait(lock); [self drain]; } @@ -49,10 +61,12 @@ - (void)blockUntilFrameAvailable { _beginResizeWaiting = NO; } -- (void)beginResize:(CGSize)size notify:(nonnull dispatch_block_t)notify { +- (void)beginResizeForView:(int64_t)viewId + size:(CGSize)size + notify:(nonnull dispatch_block_t)notify { std::unique_lock lock(_mutex); - if (CGSizeEqualToSize(_contentSize, CGSizeZero) || _shuttingDown) { + if (![self allViewsHaveFrame] || _shuttingDown) { // No blocking until framework produces at least one frame notify(); return; @@ -62,12 +76,18 @@ - (void)beginResize:(CGSize)size notify:(nonnull dispatch_block_t)notify { notify(); - _contentSize = CGSizeMake(-1, -1); + _contentSizes[viewId] = CGSizeMake(-1, -1); _beginResizeWaiting = YES; - while (!CGSizeEqualToSize(_contentSize, size) && // - !CGSizeEqualToSize(_contentSize, CGSizeZero) && !_shuttingDown) { + while (true) { + if (_shuttingDown) { + break; + } + const CGSize& contentSize = _contentSizes[viewId]; + if (CGSizeEqualToSize(contentSize, size) || CGSizeEqualToSize(contentSize, CGSizeZero)) { + break; + } _condBlockBeginResize.wait(lock); [self drain]; } @@ -75,7 +95,9 @@ - (void)beginResize:(CGSize)size notify:(nonnull dispatch_block_t)notify { _beginResizeWaiting = NO; } -- (void)performCommit:(CGSize)size notify:(nonnull dispatch_block_t)notify { +- (void)performCommitForView:(int64_t)viewId + size:(CGSize)size + notify:(nonnull dispatch_block_t)notify { fml::AutoResetWaitableEvent event; { std::unique_lock lock(_mutex); @@ -87,7 +109,7 @@ - (void)performCommit:(CGSize)size notify:(nonnull dispatch_block_t)notify { fml::AutoResetWaitableEvent& e = event; _scheduledBlocks.push_back(^{ notify(); - _contentSize = size; + _contentSizes[viewId] = size; e.Signal(); }); if (_beginResizeWaiting) { @@ -102,6 +124,14 @@ - (void)performCommit:(CGSize)size notify:(nonnull dispatch_block_t)notify { event.Wait(); } +- (void)registerView:(int64_t)viewId { + _contentSizes[viewId] = CGSizeZero; +} + +- (void)deregisterView:(int64_t)viewId { + _contentSizes.erase(viewId); +} + - (void)shutdown { std::unique_lock lock(_mutex); _shuttingDown = YES; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.h b/shell/platform/darwin/macos/framework/Source/FlutterView.h index 73425737fbd3a..721f6f497f917 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.h @@ -31,7 +31,7 @@ - (nullable instancetype)initWithMTLDevice:(nonnull id)device commandQueue:(nonnull id)commandQueue reshapeListener:(nonnull id)reshapeListener - NS_DESIGNATED_INITIALIZER; + viewId:(int64_t)viewId NS_DESIGNATED_INITIALIZER; - (nullable instancetype)initWithFrame:(NSRect)frameRect pixelFormat:(nullable NSOpenGLPixelFormat*)format NS_UNAVAILABLE; @@ -60,6 +60,8 @@ */ - (void)setBackgroundColor:(nonnull NSColor*)color; ++ (nonnull FlutterThreadSynchronizer*)sharedThreadSynchronizer; + @end @interface FlutterView (FlutterViewPrivate) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.mm b/shell/platform/darwin/macos/framework/Source/FlutterView.mm index c3f802d43ed9e..a5be0767e783a 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.mm @@ -10,8 +10,8 @@ #import @interface FlutterView () { + int64_t _viewId; __weak id _reshapeListener; - FlutterThreadSynchronizer* _threadSynchronizer; FlutterSurfaceManager* _surfaceManager; } @@ -21,24 +21,26 @@ @implementation FlutterView - (instancetype)initWithMTLDevice:(id)device commandQueue:(id)commandQueue - reshapeListener:(id)reshapeListener { + reshapeListener:(id)reshapeListener + viewId:(int64_t)viewId { self = [super initWithFrame:NSZeroRect]; if (self) { [self setWantsLayer:YES]; [self setBackgroundColor:[NSColor blackColor]]; [self setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawDuringViewResize]; + _viewId = viewId; _reshapeListener = reshapeListener; - _threadSynchronizer = [[FlutterThreadSynchronizer alloc] init]; _surfaceManager = [[FlutterSurfaceManager alloc] initWithDevice:device commandQueue:commandQueue layer:self.layer delegate:self]; + [[FlutterView sharedThreadSynchronizer] registerView:viewId]; } return self; } - (void)onPresent:(CGSize)frameSize withBlock:(dispatch_block_t)block { - [_threadSynchronizer performCommit:frameSize notify:block]; + [[FlutterView sharedThreadSynchronizer] performCommitForView:_viewId size:frameSize notify:block]; } - (FlutterSurfaceManager*)surfaceManager { @@ -46,15 +48,16 @@ - (FlutterSurfaceManager*)surfaceManager { } - (FlutterThreadSynchronizer*)threadSynchronizer { - return _threadSynchronizer; + return [FlutterView sharedThreadSynchronizer]; } - (void)reshaped { CGSize scaledSize = [self convertSizeToBacking:self.bounds.size]; - [_threadSynchronizer beginResize:scaledSize - notify:^{ - [_reshapeListener viewDidReshape:self]; - }]; + [[FlutterView sharedThreadSynchronizer] beginResizeForView:_viewId + size:scaledSize + notify:^{ + [_reshapeListener viewDidReshape:self]; + }]; } - (void)setBackgroundColor:(NSColor*)color { @@ -113,7 +116,6 @@ - (BOOL)layer:(CALayer*)layer } - (void)shutdown { - [_threadSynchronizer shutdown]; } #pragma mark - NSAccessibility overrides @@ -137,4 +139,12 @@ - (NSString*)accessibilityLabel { return applicationName; } +static FlutterThreadSynchronizer* _synchronizer; ++ (FlutterThreadSynchronizer*)sharedThreadSynchronizer { + if (_synchronizer == nil) { + _synchronizer = [[FlutterThreadSynchronizer alloc] init]; + } + return _synchronizer; +} + @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index acd07a827c569..d414c376a8dea 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -480,6 +480,10 @@ - (void)viewWillAppear { [self listenForMetaModifiedKeyUpEvents]; } +- (void)viewDidAppear { + [_engine updateWindowMetricsForViewController:self]; +} + - (void)viewWillDisappear { // Per Apple's documentation, it is discouraged to call removeMonitor: in dealloc, and it's // recommended to be called earlier in the lifecycle. @@ -856,7 +860,8 @@ - (nonnull FlutterView*)createFlutterViewWithMTLDevice:(id)device commandQueue:(id)commandQueue { return [[FlutterView alloc] initWithMTLDevice:device commandQueue:commandQueue - reshapeListener:self]; + reshapeListener:self + viewId:_viewId]; } - (void)onKeyboardLayoutChanged { diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewTest.mm index 15ca078042fd8..40faed5b13efd 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewTest.mm @@ -8,6 +8,8 @@ #import "flutter/testing/testing.h" +constexpr int64_t kDefaultViewId = 0ll; + @interface TestReshapeListener : NSObject @end @@ -25,6 +27,7 @@ - (void)viewDidReshape:(nonnull NSView*)view { TestReshapeListener* listener = [[TestReshapeListener alloc] init]; FlutterView* view = [[FlutterView alloc] initWithMTLDevice:device commandQueue:queue - reshapeListener:listener]; + reshapeListener:listener + viewId:kDefaultViewId]; EXPECT_EQ([view layer:view.layer shouldInheritContentsScale:3.0 fromWindow:view.window], YES); } diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 8fa0ee09e8dcb..0db62b3edc5c9 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1069,12 +1069,12 @@ InferExternalViewEmbedderFromArgs(const FlutterCompositor* compositor) { }; flutter::EmbedderExternalViewEmbedder::PresentCallback present_callback = - [c_present_callback, - user_data = compositor->user_data](const auto& layers) { + [c_present_callback, user_data = compositor->user_data]( + const auto& layers, int64_t window_view_id) { TRACE_EVENT0("flutter", "FlutterCompositorPresentLayers"); return c_present_callback( const_cast(layers.data()), layers.size(), - user_data); + window_view_id, user_data); }; return {std::make_unique( @@ -2065,6 +2065,7 @@ FlutterEngineResult FlutterEngineAddRenderSurface( FlutterEngineResult FlutterEngineSendWindowMetricsEvent( FLUTTER_API_SYMBOL(FlutterEngine) engine, + int64_t view_id, const FlutterWindowMetricsEvent* flutter_metrics) { if (engine == nullptr || flutter_metrics == nullptr) { return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); @@ -2109,7 +2110,7 @@ FlutterEngineResult FlutterEngineSendWindowMetricsEvent( } return reinterpret_cast(engine)->SetViewportMetrics( - metrics) + view_id, metrics) ? kSuccess : LOG_EMBEDDER_ERROR(kInvalidArguments, "Viewport metrics were invalid."); diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 6b0ff0baf5a12..70b85ccf132cf 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -1549,6 +1549,7 @@ typedef struct { size_t struct_size; /// The size of the render target the engine expects to render into. FlutterSize size; + int64_t view_id; } FlutterBackingStoreConfig; typedef enum { @@ -1591,6 +1592,7 @@ typedef bool (*FlutterBackingStoreCollectCallback)( typedef bool (*FlutterLayersPresentCallback)(const FlutterLayer** layers, size_t layers_count, + int64_t view_id, void* user_data); typedef struct { @@ -2253,6 +2255,7 @@ FlutterEngineResult FlutterEngineAddRenderSurface( FLUTTER_EXPORT FlutterEngineResult FlutterEngineSendWindowMetricsEvent( FLUTTER_API_SYMBOL(FlutterEngine) engine, + int64_t view_id, const FlutterWindowMetricsEvent* event); FLUTTER_EXPORT @@ -2830,6 +2833,7 @@ typedef FlutterEngineResult (*FlutterEngineAddRenderSurfaceFnPtr)( int64_t view_id); typedef FlutterEngineResult (*FlutterEngineSendWindowMetricsEventFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine, + int64_t view_id, const FlutterWindowMetricsEvent* event); typedef FlutterEngineResult (*FlutterEngineSendPointerEventFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine, diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc index cc4da738f72fc..b34da8a287f4e 100644 --- a/shell/platform/embedder/embedder_engine.cc +++ b/shell/platform/embedder/embedder_engine.cc @@ -100,6 +100,7 @@ bool EmbedderEngine::NotifyDestroyed() { } bool EmbedderEngine::SetViewportMetrics( + int64_t view_id, const flutter::ViewportMetrics& metrics) { if (!IsValid()) { return false; @@ -109,7 +110,7 @@ bool EmbedderEngine::SetViewportMetrics( if (!platform_view) { return false; } - platform_view->SetViewportMetrics(metrics); + platform_view->SetViewportMetrics(view_id, metrics); return true; } diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index 60d33ba76e652..a587eeb3eaa74 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -48,7 +48,8 @@ class EmbedderEngine { bool IsValid() const; - bool SetViewportMetrics(const flutter::ViewportMetrics& metrics); + bool SetViewportMetrics(int64_t view_id, + const flutter::ViewportMetrics& metrics); bool DispatchPointerDataPacket( std::unique_ptr packet); diff --git a/shell/platform/embedder/embedder_external_view_embedder.cc b/shell/platform/embedder/embedder_external_view_embedder.cc index 380b404a13046..d9ae64df14887 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.cc +++ b/shell/platform/embedder/embedder_external_view_embedder.cc @@ -111,13 +111,15 @@ DlCanvas* EmbedderExternalViewEmbedder::CompositeEmbeddedView(int64_t view_id) { } static FlutterBackingStoreConfig MakeBackingStoreConfig( - const SkISize& backing_store_size) { + const SkISize& backing_store_size, + int64_t window_view_id) { FlutterBackingStoreConfig config = {}; config.struct_size = sizeof(config); config.size.width = backing_store_size.width(); config.size.height = backing_store_size.height(); + config.view_id = window_view_id; return config; } @@ -125,7 +127,8 @@ static FlutterBackingStoreConfig MakeBackingStoreConfig( // |ExternalViewEmbedder| void EmbedderExternalViewEmbedder::SubmitFrame( GrDirectContext* context, - std::unique_ptr frame) { + std::unique_ptr frame, + int64_t window_view_id) { auto [matched_render_targets, pending_keys] = render_target_cache_.GetExistingTargetsInCache(pending_views_); @@ -164,7 +167,7 @@ void EmbedderExternalViewEmbedder::SubmitFrame( const auto render_surface_size = external_view->GetRenderSurfaceSize(); const auto backing_store_config = - MakeBackingStoreConfig(render_surface_size); + MakeBackingStoreConfig(render_surface_size, window_view_id); // This is where the embedder will create render targets for us. Control // flow to the embedder makes the engine susceptible to having the embedder @@ -243,7 +246,7 @@ void EmbedderExternalViewEmbedder::SubmitFrame( // Flush the layer description down to the embedder for presentation. // // @warning: Embedder may trample on our OpenGL context here. - presented_layers.InvokePresentCallback(present_callback_); + presented_layers.InvokePresentCallback(present_callback_, window_view_id); } // See why this is necessary in the comment where this collection in realized. diff --git a/shell/platform/embedder/embedder_external_view_embedder.h b/shell/platform/embedder/embedder_external_view_embedder.h index 55a08f0627a72..2118db5e25758 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.h +++ b/shell/platform/embedder/embedder_external_view_embedder.h @@ -33,7 +33,8 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder { GrDirectContext* context, const FlutterBackingStoreConfig& config)>; using PresentCallback = - std::function& layers)>; + std::function& layers, + int64_t window_view_id)>; using SurfaceTransformationCallback = std::function; //---------------------------------------------------------------------------- @@ -95,7 +96,8 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder { // |ExternalViewEmbedder| void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame) override; + std::unique_ptr frame, + int64_t window_view_id) override; // |ExternalViewEmbedder| DlCanvas* GetRootCanvas() override; diff --git a/shell/platform/embedder/embedder_layers.cc b/shell/platform/embedder/embedder_layers.cc index 6465293748f9f..f5a62b26c4bea 100644 --- a/shell/platform/embedder/embedder_layers.cc +++ b/shell/platform/embedder/embedder_layers.cc @@ -199,14 +199,14 @@ void EmbedderLayers::PushPlatformViewLayer( presented_layers_.push_back(layer); } -void EmbedderLayers::InvokePresentCallback( - const PresentCallback& callback) const { +void EmbedderLayers::InvokePresentCallback(const PresentCallback& callback, + int64_t window_view_id) const { std::vector presented_layers_pointers; presented_layers_pointers.reserve(presented_layers_.size()); for (const auto& layer : presented_layers_) { presented_layers_pointers.push_back(&layer); } - callback(presented_layers_pointers); + callback(presented_layers_pointers, window_view_id); } } // namespace flutter diff --git a/shell/platform/embedder/embedder_layers.h b/shell/platform/embedder/embedder_layers.h index c1cb2907588c9..46137f459df99 100644 --- a/shell/platform/embedder/embedder_layers.h +++ b/shell/platform/embedder/embedder_layers.h @@ -30,8 +30,10 @@ class EmbedderLayers { const EmbeddedViewParams& params); using PresentCallback = - std::function& layers)>; - void InvokePresentCallback(const PresentCallback& callback) const; + std::function& layers, + int64_t view_id)>; + void InvokePresentCallback(const PresentCallback& callback, + int64_t window_view_id) const; private: const SkISize frame_size_; diff --git a/shell/platform/embedder/platform_view_embedder_unittests.cc b/shell/platform/embedder/platform_view_embedder_unittests.cc index f0a07cc8aa004..b04365c0d6967 100644 --- a/shell/platform/embedder/platform_view_embedder_unittests.cc +++ b/shell/platform/embedder/platform_view_embedder_unittests.cc @@ -22,8 +22,8 @@ class MockDelegate : public PlatformView::Delegate { MOCK_METHOD0(OnPlatformViewScheduleFrame, void()); MOCK_METHOD1(OnPlatformViewSetNextFrameCallback, void(const fml::closure& closure)); - MOCK_METHOD1(OnPlatformViewSetViewportMetrics, - void(const ViewportMetrics& metrics)); + MOCK_METHOD2(OnPlatformViewSetViewportMetrics, + void(int64_t view_id, const ViewportMetrics& metrics)); MOCK_METHOD1(OnPlatformViewDispatchPlatformMessage, void(std::unique_ptr message)); MOCK_METHOD1(OnPlatformViewDispatchPointerDataPacket, diff --git a/shell/platform/embedder/tests/embedder_config_builder.cc b/shell/platform/embedder/tests/embedder_config_builder.cc index f12a9e58b130a..dae2c4dc12110 100644 --- a/shell/platform/embedder/tests/embedder_config_builder.cc +++ b/shell/platform/embedder/tests/embedder_config_builder.cc @@ -362,6 +362,7 @@ void EmbedderConfigBuilder::SetCompositor(bool avoid_backing_store_cache) { }; compositor_.present_layers_callback = [](const FlutterLayer** layers, // size_t layers_count, // + int64_t view_id, // void* user_data // ) { return reinterpret_cast(user_data)->Present( diff --git a/shell/platform/embedder/tests/embedder_metal_unittests.mm b/shell/platform/embedder/tests/embedder_metal_unittests.mm index 7974dc32cf5ea..db452abd4701d 100644 --- a/shell/platform/embedder/tests/embedder_metal_unittests.mm +++ b/shell/platform/embedder/tests/embedder_metal_unittests.mm @@ -28,6 +28,8 @@ namespace flutter { namespace testing { +constexpr int64_t kDefaultViewId = 0ll; + using EmbedderTest = testing::EmbedderTest; TEST_F(EmbedderTest, CanRenderGradientWithMetal) { @@ -49,7 +51,7 @@ event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), kSuccess); // TODO (https://github.com/flutter/flutter/issues/73590): re-enable once // we are able to figure out why this fails on the bots. @@ -118,7 +120,7 @@ GrBackendTexture backend_texture(texture_size.width(), texture_size.height(), Gr event.width = texture_size.width(); event.height = texture_size.height(); event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), kSuccess); ASSERT_TRUE(ImageMatchesFixture("external_texture_metal.png", rendered_scene)); } @@ -200,7 +202,7 @@ GrBackendTexture backend_texture(texture_size.width(), texture_size.height(), Gr event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -225,7 +227,7 @@ GrBackendTexture backend_texture(texture_size.width(), texture_size.height(), Gr event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), kSuccess); ASSERT_TRUE(ImageMatchesFixture("scene_without_custom_compositor.png", rendered_scene)); } @@ -273,7 +275,7 @@ GrBackendTexture backend_texture(texture_size.width(), texture_size.height(), Gr event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), kSuccess); ASSERT_TRUE(engine.is_valid()); collect_context->latch.Wait(); @@ -427,7 +429,7 @@ GrBackendTexture backend_texture(texture_size.width(), texture_size.height(), Gr event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -485,7 +487,7 @@ void Collect() { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), kSuccess); + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); } diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 8b03077fb5878..1ca6fbe798205 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -52,6 +52,8 @@ static uint64_t NanosFromEpoch(int millis_from_now) { namespace flutter { namespace testing { +static constexpr int64_t kDefaultViewId = 0ll; + using EmbedderTest = testing::EmbedderTest; TEST(EmbedderTestNoFixture, MustNotRunWithInvalidArgs) { @@ -805,8 +807,9 @@ TEST_F(EmbedderTest, event.physical_view_inset_right = 0.0; event.physical_view_inset_bottom = 0.0; event.physical_view_inset_left = 0.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -921,8 +924,9 @@ TEST_F(EmbedderTest, NoLayerCreatedForTransparentOverlayOnTopOfPlatformLayer) { event.physical_view_inset_right = 0.0; event.physical_view_inset_bottom = 0.0; event.physical_view_inset_left = 0.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -1043,8 +1047,9 @@ TEST_F(EmbedderTest, NoLayerCreatedForNoOverlayOnTopOfPlatformLayer) { event.physical_view_inset_right = 0.0; event.physical_view_inset_bottom = 0.0; event.physical_view_inset_left = 0.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -1114,8 +1119,9 @@ TEST_F(EmbedderTest, CanDeinitializeAnEngine) { event.physical_view_inset_right = 0.0; event.physical_view_inset_bottom = 0.0; event.physical_view_inset_left = 0.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kInvalidArguments); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kInvalidArguments); engine.reset(); } @@ -1319,8 +1325,9 @@ TEST_F(EmbedderTest, VerifyB143464703WithSoftwareBackend) { event.physical_view_inset_right = 0.0; event.physical_view_inset_bottom = 0.0; event.physical_view_inset_left = 0.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); // wait for scene to be rendered. @@ -1811,8 +1818,9 @@ TEST_F(EmbedderTest, InvalidFlutterWindowMetricsEvent) { event.physical_view_inset_left = 0.0; // Pixel ratio must be positive. - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kInvalidArguments); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kInvalidArguments); event.pixel_ratio = 1.0; event.physical_view_inset_top = -1.0; @@ -1821,8 +1829,9 @@ TEST_F(EmbedderTest, InvalidFlutterWindowMetricsEvent) { event.physical_view_inset_left = -1.0; // Physical view insets must be non-negative. - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kInvalidArguments); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kInvalidArguments); event.physical_view_inset_top = 700; event.physical_view_inset_right = 900; @@ -1831,8 +1840,9 @@ TEST_F(EmbedderTest, InvalidFlutterWindowMetricsEvent) { // Top/bottom insets cannot be greater than height. // Left/right insets cannot be greater than width. - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kInvalidArguments); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kInvalidArguments); } static void expectSoftwareRenderingOutputMatches( @@ -1876,8 +1886,9 @@ static void expectSoftwareRenderingOutputMatches( event.width = 1; event.height = 1; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); latch.Wait(); ASSERT_TRUE(matches); @@ -2389,7 +2400,8 @@ TEST_F(EmbedderTest, VsyncCallbackPostedIntoFuture) { event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, + &event), kSuccess); }); @@ -2464,8 +2476,9 @@ TEST_F(EmbedderTest, CanSetNextFrameCallback) { event.physical_view_inset_right = 0.0; event.physical_view_inset_bottom = 0.0; event.physical_view_inset_left = 0.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); callback_latch.Wait(); } diff --git a/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc b/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc index 901bd8c078e97..9d970c6489865 100644 --- a/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc +++ b/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc @@ -132,7 +132,8 @@ void FlatlandExternalViewEmbedder::EndFrame( void FlatlandExternalViewEmbedder::SubmitFrame( GrDirectContext* context, - std::unique_ptr frame) { + std::unique_ptr frame, + int64_t window_view_id) { TRACE_EVENT0("flutter", "FlatlandExternalViewEmbedder::SubmitFrame"); std::vector> frame_surfaces; std::unordered_map frame_surface_indices; diff --git a/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h b/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h index 00a845cde2e85..394de12c7887a 100644 --- a/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h +++ b/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h @@ -87,7 +87,8 @@ class FlatlandExternalViewEmbedder final // |ExternalViewEmbedder| void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame) override; + std::unique_ptr frame, + int64_t window_view_id) override; // |ExternalViewEmbedder| void CancelFrame() override { Reset(); } diff --git a/shell/platform/fuchsia/flutter/gfx_external_view_embedder.cc b/shell/platform/fuchsia/flutter/gfx_external_view_embedder.cc index 21c6810c4e11b..e34dfc5e64bc3 100644 --- a/shell/platform/fuchsia/flutter/gfx_external_view_embedder.cc +++ b/shell/platform/fuchsia/flutter/gfx_external_view_embedder.cc @@ -231,7 +231,8 @@ void GfxExternalViewEmbedder::EndFrame( void GfxExternalViewEmbedder::SubmitFrame( GrDirectContext* context, - std::unique_ptr frame) { + std::unique_ptr frame, + int64_t window_view_id) { TRACE_EVENT0("flutter", "GfxExternalViewEmbedder::SubmitFrame"); std::vector> frame_surfaces; std::unordered_map frame_surface_indices; diff --git a/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h b/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h index eb37d2e7e2d1f..7d2f545cf238e 100644 --- a/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h +++ b/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h @@ -110,7 +110,8 @@ class GfxExternalViewEmbedder final : public flutter::ExternalViewEmbedder { // |ExternalViewEmbedder| void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame) override; + std::unique_ptr frame, + int64_t window_view_id) override; // |ExternalViewEmbedder| void CancelFrame() override { Reset(); } diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index be9bac1005a73..6f30c64e79e3a 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -55,7 +55,8 @@ class MockExternalViewEmbedder : public flutter::ExternalViewEmbedder { fml::RefPtr raster_thread_merger) override {} void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame) override {} + std::unique_ptr frame, + int64_t window_view_id) override {} void PrerollCompositeEmbeddedView( int64_t view_id, diff --git a/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc b/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc index 22ce7acda3a7c..e01337c409eef 100644 --- a/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc +++ b/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc @@ -53,6 +53,8 @@ using ::testing::VariantWith; namespace flutter_runner::testing { namespace { +constexpr int64_t kDefaultViewId = 0ll; + constexpr static fuchsia::ui::composition::BlendMode kFirstLayerBlendMode{ fuchsia::ui::composition::BlendMode::SRC}; constexpr static fuchsia::ui::composition::BlendMode kUpperLayerBlendMode{ @@ -335,11 +337,13 @@ void DrawSimpleFrame(FlatlandExternalViewEmbedder& external_view_embedder, flutter::SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; external_view_embedder.SubmitFrame( - nullptr, std::make_unique( - nullptr, std::move(framebuffer_info), - [](const flutter::SurfaceFrame& surface_frame, - flutter::DlCanvas* canvas) { return true; }, - frame_size)); + nullptr, + std::make_unique( + nullptr, std::move(framebuffer_info), + [](const flutter::SurfaceFrame& surface_frame, + flutter::DlCanvas* canvas) { return true; }, + frame_size), + kDefaultViewId); } void DrawFrameWithView( @@ -365,11 +369,13 @@ void DrawFrameWithView( flutter::SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; external_view_embedder.SubmitFrame( - nullptr, std::make_unique( - nullptr, std::move(framebuffer_info), - [](const flutter::SurfaceFrame& surface_frame, - flutter::DlCanvas* canvas) { return true; }, - frame_size)); + nullptr, + std::make_unique( + nullptr, std::move(framebuffer_info), + [](const flutter::SurfaceFrame& surface_frame, + flutter::DlCanvas* canvas) { return true; }, + frame_size), + kDefaultViewId); } }; // namespace diff --git a/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc b/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc index 9b4a8524ae038..01326fc07a4ac 100644 --- a/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc @@ -54,7 +54,8 @@ class MockExternalViewEmbedder : public flutter::ExternalViewEmbedder { fml::RefPtr raster_thread_merger) override {} void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame) override {} + std::unique_ptr frame, + int64_t window_view_id) override {} void PrerollCompositeEmbeddedView( int64_t view_id, diff --git a/shell/platform/fuchsia/flutter/tests/gfx_external_view_embedder_unittests.cc b/shell/platform/fuchsia/flutter/tests/gfx_external_view_embedder_unittests.cc index 45f570075ca3a..14e5b58fb9274 100644 --- a/shell/platform/fuchsia/flutter/tests/gfx_external_view_embedder_unittests.cc +++ b/shell/platform/fuchsia/flutter/tests/gfx_external_view_embedder_unittests.cc @@ -54,6 +54,8 @@ using ::testing::VariantWith; namespace flutter_runner::testing { namespace { +constexpr int64_t kDefaultViewId = 0ll; + class FakeSurfaceProducerSurface : public SurfaceProducerSurface { public: explicit FakeSurfaceProducerSurface(scenic::Session* session, @@ -458,11 +460,13 @@ void DrawSimpleFrame(GfxExternalViewEmbedder& external_view_embedder, external_view_embedder.EndFrame(false, nullptr); flutter::SurfaceFrame::FramebufferInfo framebuffer_info; external_view_embedder.SubmitFrame( - nullptr, std::make_unique( - nullptr, framebuffer_info, - [](const flutter::SurfaceFrame& surface_frame, - flutter::DlCanvas* canvas) { return true; }, - frame_size)); + nullptr, + std::make_unique( + nullptr, framebuffer_info, + [](const flutter::SurfaceFrame& surface_frame, + flutter::DlCanvas* canvas) { return true; }, + frame_size), + kDefaultViewId); } void DrawFrameWithView( @@ -487,11 +491,13 @@ void DrawFrameWithView( external_view_embedder.EndFrame(false, nullptr); flutter::SurfaceFrame::FramebufferInfo framebuffer_info; external_view_embedder.SubmitFrame( - nullptr, std::make_unique( - nullptr, framebuffer_info, - [](const flutter::SurfaceFrame& surface_frame, - flutter::DlCanvas* canvas) { return true; }, - frame_size)); + nullptr, + std::make_unique( + nullptr, framebuffer_info, + [](const flutter::SurfaceFrame& surface_frame, + flutter::DlCanvas* canvas) { return true; }, + frame_size), + kDefaultViewId); } FramePresentedInfo MakeFramePresentedInfoForOnePresent( diff --git a/shell/platform/linux/fl_engine.cc b/shell/platform/linux/fl_engine.cc index 0ae331b328375..b8a6a1942dcc7 100644 --- a/shell/platform/linux/fl_engine.cc +++ b/shell/platform/linux/fl_engine.cc @@ -178,6 +178,7 @@ static bool compositor_collect_backing_store_callback( // Called when embedder should composite contents of each layer onto the screen. static bool compositor_present_layers_callback(const FlutterLayer** layers, size_t layers_count, + int64_t view_id, void* user_data) { g_return_val_if_fail(FL_IS_RENDERER(user_data), false); return fl_renderer_present_layers(FL_RENDERER(user_data), layers, diff --git a/shell/testing/tester_main.cc b/shell/testing/tester_main.cc index d0a3711505b44..c99aeb1c1005a 100644 --- a/shell/testing/tester_main.cc +++ b/shell/testing/tester_main.cc @@ -40,6 +40,8 @@ namespace flutter { +static constexpr int64_t kDefaultViewId = 0ll; + class TesterExternalViewEmbedder : public ExternalViewEmbedder { // |ExternalViewEmbedder| DlCanvas* GetRootCanvas() override { return nullptr; } @@ -357,7 +359,7 @@ int RunTester(const flutter::Settings& settings, metrics.device_pixel_ratio = 3.0; metrics.physical_width = 2400.0; // 800 at 3x resolution. metrics.physical_height = 1800.0; // 600 at 3x resolution. - shell->GetPlatformView()->SetViewportMetrics(metrics); + shell->GetPlatformView()->SetViewportMetrics(kDefaultViewId, metrics); // Run the message loop and wait for the script to do its thing. fml::MessageLoop::GetCurrent().Run(); From 6701a63914a3a291bef1f3316d415950f0d9f399 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 21 Apr 2023 13:23:42 -0700 Subject: [PATCH 078/147] Format --- lib/ui/window/platform_configuration.cc | 13 +++++++------ shell/common/shell_test_platform_view_metal.mm | 2 +- shell/gpu/gpu_studio_gl_impeller.cc | 3 ++- shell/gpu/gpu_studio_vulkan_impeller.cc | 3 ++- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index bd9a7744daaa9..fdf7a0b2f3337 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -50,7 +50,7 @@ void PlatformConfiguration::DidCreateIsolate() { add_view_.Set(tonic::DartState::Current(), Dart_GetField(library, tonic::ToDart("_addView"))); remove_view_.Set(tonic::DartState::Current(), - Dart_GetField(library, tonic::ToDart("_removeView"))); + Dart_GetField(library, tonic::ToDart("_removeView"))); update_locales_.Set(tonic::DartState::Current(), Dart_GetField(library, tonic::ToDart("_updateLocales"))); update_user_settings_data_.Set( @@ -112,21 +112,22 @@ void PlatformConfiguration::AddView(int64_t view_id) { tonic::DartState::Scope scope(dart_state); tonic::CheckAndHandleError( tonic::DartInvoke(add_view_.Get(), { - tonic::ToDart(view_id), - })); + tonic::ToDart(view_id), + })); } void PlatformConfiguration::RemoveView(int64_t view_id) { windows_.erase(view_id); - std::shared_ptr dart_state = remove_view_.dart_state().lock(); + std::shared_ptr dart_state = + remove_view_.dart_state().lock(); if (!dart_state) { return; } tonic::DartState::Scope scope(dart_state); tonic::CheckAndHandleError( tonic::DartInvoke(remove_view_.Get(), { - tonic::ToDart(view_id), - })); + tonic::ToDart(view_id), + })); } void PlatformConfiguration::UpdateLocales( diff --git a/shell/common/shell_test_platform_view_metal.mm b/shell/common/shell_test_platform_view_metal.mm index 5d44f04d3ab72..6ef2bbc5a1d81 100644 --- a/shell/common/shell_test_platform_view_metal.mm +++ b/shell/common/shell_test_platform_view_metal.mm @@ -111,7 +111,7 @@ GPUMTLTextureInfo offscreen_texture_info() const { std::unique_ptr ShellTestPlatformViewMetal::CreateRenderingStudio() { if (GetSettings().enable_impeller) { return std::make_unique(this, - [metal_context_->impeller_context() context]); + [metal_context_->impeller_context() context]); } return std::make_unique(this, [metal_context_->context() mainContext], sksl_precompiler_); diff --git a/shell/gpu/gpu_studio_gl_impeller.cc b/shell/gpu/gpu_studio_gl_impeller.cc index a64eefb170853..cae6e498f1e99 100644 --- a/shell/gpu/gpu_studio_gl_impeller.cc +++ b/shell/gpu/gpu_studio_gl_impeller.cc @@ -73,7 +73,8 @@ bool GPUStudioGLImpeller::EnableRasterCache() const { } // |Studio| -std::shared_ptr GPUStudioGLImpeller::GetAiksContext() const { +std::shared_ptr GPUStudioGLImpeller::GetAiksContext() + const { return aiks_context_; } diff --git a/shell/gpu/gpu_studio_vulkan_impeller.cc b/shell/gpu/gpu_studio_vulkan_impeller.cc index 0b5418405ec42..bc94f8e4a8bf4 100644 --- a/shell/gpu/gpu_studio_vulkan_impeller.cc +++ b/shell/gpu/gpu_studio_vulkan_impeller.cc @@ -54,7 +54,8 @@ bool GPUStudioVulkanImpeller::EnableRasterCache() const { } // |Studio| -std::shared_ptr GPUStudioVulkanImpeller::GetAiksContext() const { +std::shared_ptr GPUStudioVulkanImpeller::GetAiksContext() + const { return aiks_context_; } From 2391ce35c0876c2e7b19ee14470a062468d95652 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 21 Apr 2023 13:31:51 -0700 Subject: [PATCH 079/147] Compile --- shell/common/rasterizer.cc | 2 +- shell/common/rasterizer.h | 7 ++++--- shell/gpu/gpu_studio_metal_impeller.mm | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 78937353df8ce..a3ed3a9c64b7b 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -241,7 +241,7 @@ RasterStatus Rasterizer::Draw( std::unique_ptr frame_timings_recorder = std::move(item->frame_timings_recorder); int64_t view_id = item->view_id; - if (discard_callback(*layer_tree.get())) { + if (discard_callback(view_id, *layer_tree.get())) { draw_result.raster_status = RasterStatus::kDiscarded; } else { draw_result = DoDraw(view_id, std::move(frame_timings_recorder), diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 1de2050b0ab86..a982ada38712b 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -597,9 +597,10 @@ class Rasterizer final : public SnapshotDelegate, GrDirectContext* surface_context, bool compressed); - DoDrawResult DoDraw(int64_t view_id, - FrameTimingsRecorder& frame_timings_recorder, - std::shared_ptr layer_tree); + DoDrawResult DoDraw( + int64_t view_id, + std::unique_ptr frame_timings_recorder, + std::shared_ptr layer_tree); RasterStatus DrawToSurface(FrameTimingsRecorder& frame_timings_recorder, flutter::LayerTree* layer_tree, diff --git a/shell/gpu/gpu_studio_metal_impeller.mm b/shell/gpu/gpu_studio_metal_impeller.mm index b5b184bd98f21..4a9c140c0ca48 100644 --- a/shell/gpu/gpu_studio_metal_impeller.mm +++ b/shell/gpu/gpu_studio_metal_impeller.mm @@ -11,7 +11,7 @@ #include "flutter/fml/make_copyable.h" #include "flutter/fml/mapping.h" #include "flutter/fml/trace_event.h" -#include "flutter/impeller/display_list/display_list_dispatcher.h" +#include "flutter/impeller/display_list/dl_dispatcher.h" #include "flutter/impeller/renderer/backend/metal/surface_mtl.h" static_assert(!__has_feature(objc_arc), "ARC must be disabled."); From d934818d22754f38ff95317b1682f5aed5b3cc90 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 27 Apr 2023 14:58:44 -0700 Subject: [PATCH 080/147] Create layer tree on taking --- flow/layers/layer_tree.cc | 14 ++-- flow/layers/layer_tree.h | 32 ++++----- flow/layers/layer_tree_unittests.cc | 55 +++++++++------ lib/ui/compositing/scene.cc | 55 +++++++++------ lib/ui/compositing/scene.h | 17 +++-- ...isplay_list_deferred_image_gpu_impeller.cc | 14 ++-- ...display_list_deferred_image_gpu_impeller.h | 6 +- .../display_list_deferred_image_gpu_skia.cc | 12 ++-- .../display_list_deferred_image_gpu_skia.h | 6 +- lib/ui/painting/picture.cc | 27 ++++---- lib/ui/painting/picture.h | 12 ++-- runtime/runtime_delegate.h | 2 +- shell/common/animator.cc | 2 +- shell/common/animator.h | 2 +- shell/common/animator_unittests.cc | 8 +-- shell/common/engine.cc | 2 +- shell/common/engine.h | 2 +- shell/common/engine_unittests.cc | 2 +- shell/common/pipeline.h | 4 +- shell/common/rasterizer.cc | 4 +- shell/common/rasterizer.h | 6 +- shell/common/rasterizer_unittests.cc | 69 +++++++++++-------- shell/common/shell_test.cc | 10 +-- 23 files changed, 202 insertions(+), 161 deletions(-) diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index d01b243ab3eb8..8a0cb890acef8 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -20,12 +20,16 @@ namespace flutter { -LayerTree::LayerTree(const SkISize& frame_size, float device_pixel_ratio) - : frame_size_(frame_size), +LayerTree::LayerTree(const Config& config, + const SkISize& frame_size, + float device_pixel_ratio) + : root_layer_(config.root_layer), + frame_size_(frame_size), device_pixel_ratio_(device_pixel_ratio), - rasterizer_tracing_threshold_(0), - checkerboard_raster_cache_images_(false), - checkerboard_offscreen_layers_(false) { + rasterizer_tracing_threshold_(config.rasterizer_tracing_threshold), + checkerboard_raster_cache_images_( + config.checkerboard_raster_cache_images), + checkerboard_offscreen_layers_(config.checkerboard_offscreen_layers) { FML_CHECK(device_pixel_ratio_ != 0.0f); } diff --git a/flow/layers/layer_tree.h b/flow/layers/layer_tree.h index a39cc2360076f..f01c1c80044ad 100644 --- a/flow/layers/layer_tree.h +++ b/flow/layers/layer_tree.h @@ -23,7 +23,16 @@ namespace flutter { class LayerTree { public: - LayerTree(const SkISize& frame_size, float device_pixel_ratio); + struct Config { + std::shared_ptr root_layer; + uint32_t rasterizer_tracing_threshold = 0; + bool checkerboard_raster_cache_images = false; + bool checkerboard_offscreen_layers = false; + }; + + LayerTree(const Config& config, + const SkISize& frame_size, + float device_pixel_ratio); // Perform a preroll pass on the tree and return information about // the tree that affects rendering this frame. @@ -50,11 +59,6 @@ class LayerTree { GrDirectContext* gr_context = nullptr); Layer* root_layer() const { return root_layer_.get(); } - - void set_root_layer(std::shared_ptr root_layer) { - root_layer_ = std::move(root_layer); - } - const SkISize& frame_size() const { return frame_size_; } float device_pixel_ratio() const { return device_pixel_ratio_; } @@ -62,24 +66,12 @@ class LayerTree { PaintRegionMap& paint_region_map() { return paint_region_map_; } // The number of frame intervals missed after which the compositor must - // trace the rasterized picture to a trace file. Specify 0 to disable all - // tracing - void set_rasterizer_tracing_threshold(uint32_t interval) { - rasterizer_tracing_threshold_ = interval; - } - + // trace the rasterized picture to a trace file. 0 stands for disabling all + // tracing. uint32_t rasterizer_tracing_threshold() const { return rasterizer_tracing_threshold_; } - void set_checkerboard_raster_cache_images(bool checkerboard) { - checkerboard_raster_cache_images_ = checkerboard; - } - - void set_checkerboard_offscreen_layers(bool checkerboard) { - checkerboard_offscreen_layers_ = checkerboard; - } - /// When `Paint` is called, if leaf layer tracing is enabled, additional /// metadata around raterization of leaf layers is collected. /// diff --git a/flow/layers/layer_tree_unittests.cc b/flow/layers/layer_tree_unittests.cc index ea655b5b44ae8..47673b8eb764b 100644 --- a/flow/layers/layer_tree_unittests.cc +++ b/flow/layers/layer_tree_unittests.cc @@ -19,8 +19,7 @@ namespace testing { class LayerTreeTest : public CanvasTest { public: LayerTreeTest() - : layer_tree_(SkISize::Make(64, 64), 1.0f), - root_transform_(SkMatrix::Translate(1.0f, 1.0f)), + : root_transform_(SkMatrix::Translate(1.0f, 1.0f)), scoped_frame_(compositor_context_.AcquireFrame(nullptr, &mock_canvas(), nullptr, @@ -31,12 +30,14 @@ class LayerTreeTest : public CanvasTest { nullptr, nullptr)) {} - LayerTree& layer_tree() { return layer_tree_; } CompositorContext::ScopedFrame& frame() { return *scoped_frame_.get(); } const SkMatrix& root_transform() { return root_transform_; } + std::unique_ptr BuildLayerTree(LayerTree::Config config) { + return std::make_unique(config, SkISize::Make(64, 64), 1.0f); + } + private: - LayerTree layer_tree_; CompositorContext compositor_context_; SkMatrix root_transform_; std::unique_ptr scoped_frame_; @@ -45,12 +46,14 @@ class LayerTreeTest : public CanvasTest { TEST_F(LayerTreeTest, PaintingEmptyLayerDies) { auto layer = std::make_shared(); - layer_tree().set_root_layer(layer); - layer_tree().Preroll(frame()); + auto layer_tree = BuildLayerTree(LayerTree::Config{ + .root_layer = layer, + }); + layer_tree->Preroll(frame()); EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); EXPECT_TRUE(layer->is_empty()); - layer_tree().Paint(frame()); + layer_tree->Paint(frame()); } TEST_F(LayerTreeTest, PaintBeforePrerollDies) { @@ -61,13 +64,15 @@ TEST_F(LayerTreeTest, PaintBeforePrerollDies) { auto layer = std::make_shared(); layer->Add(mock_layer); - layer_tree().set_root_layer(layer); + auto layer_tree = BuildLayerTree(LayerTree::Config{ + .root_layer = layer, + }); EXPECT_EQ(mock_layer->paint_bounds(), kEmptyRect); EXPECT_EQ(layer->paint_bounds(), kEmptyRect); EXPECT_TRUE(mock_layer->is_empty()); EXPECT_TRUE(layer->is_empty()); - layer_tree().Paint(frame()); + layer_tree->Paint(frame()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector()); } @@ -79,15 +84,17 @@ TEST_F(LayerTreeTest, Simple) { auto layer = std::make_shared(); layer->Add(mock_layer); - layer_tree().set_root_layer(layer); - layer_tree().Preroll(frame()); + auto layer_tree = BuildLayerTree(LayerTree::Config{ + .root_layer = layer, + }); + layer_tree->Preroll(frame()); EXPECT_EQ(mock_layer->paint_bounds(), child_bounds); EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds()); EXPECT_FALSE(mock_layer->is_empty()); EXPECT_FALSE(layer->is_empty()); EXPECT_EQ(mock_layer->parent_matrix(), root_transform()); - layer_tree().Paint(frame()); + layer_tree->Paint(frame()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector({MockCanvas::DrawCall{ 0, MockCanvas::DrawPathData{child_path, child_paint}}})); @@ -107,8 +114,10 @@ TEST_F(LayerTreeTest, Multiple) { SkRect expected_total_bounds = child_path1.getBounds(); expected_total_bounds.join(child_path2.getBounds()); - layer_tree().set_root_layer(layer); - layer_tree().Preroll(frame()); + auto layer_tree = BuildLayerTree(LayerTree::Config{ + .root_layer = layer, + }); + layer_tree->Preroll(frame()); EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); @@ -121,7 +130,7 @@ TEST_F(LayerTreeTest, Multiple) { EXPECT_EQ(mock_layer2->parent_cull_rect(), kGiantRect); // Siblings are independent - layer_tree().Paint(frame()); + layer_tree->Paint(frame()); EXPECT_EQ( mock_canvas().draw_calls(), std::vector({MockCanvas::DrawCall{ @@ -140,8 +149,10 @@ TEST_F(LayerTreeTest, MultipleWithEmpty) { layer->Add(mock_layer1); layer->Add(mock_layer2); - layer_tree().set_root_layer(layer); - layer_tree().Preroll(frame()); + auto layer_tree = BuildLayerTree(LayerTree::Config{ + .root_layer = layer, + }); + layer_tree->Preroll(frame()); EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), SkPath().getBounds()); EXPECT_EQ(layer->paint_bounds(), child_path1.getBounds()); @@ -153,7 +164,7 @@ TEST_F(LayerTreeTest, MultipleWithEmpty) { EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); EXPECT_EQ(mock_layer2->parent_cull_rect(), kGiantRect); - layer_tree().Paint(frame()); + layer_tree->Paint(frame()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector({MockCanvas::DrawCall{ 0, MockCanvas::DrawPathData{child_path1, child_paint1}}})); @@ -172,8 +183,10 @@ TEST_F(LayerTreeTest, NeedsSystemComposite) { SkRect expected_total_bounds = child_path1.getBounds(); expected_total_bounds.join(child_path2.getBounds()); - layer_tree().set_root_layer(layer); - layer_tree().Preroll(frame()); + auto layer_tree = BuildLayerTree(LayerTree::Config{ + .root_layer = layer, + }); + layer_tree->Preroll(frame()); EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); @@ -185,7 +198,7 @@ TEST_F(LayerTreeTest, NeedsSystemComposite) { EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); EXPECT_EQ(mock_layer2->parent_cull_rect(), kGiantRect); - layer_tree().Paint(frame()); + layer_tree->Paint(frame()); EXPECT_EQ( mock_canvas().draw_calls(), std::vector({MockCanvas::DrawCall{ diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index c5070c6376091..f85397ac881b5 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -45,22 +45,23 @@ Scene::Scene(std::shared_ptr rootLayer, ->platform_configuration() ->get_window(0) ->viewport_metrics(); - - layer_tree_ = std::make_shared( - SkISize::Make(viewport_metrics.physical_width, - viewport_metrics.physical_height), - static_cast(viewport_metrics.device_pixel_ratio)); - layer_tree_->set_root_layer(std::move(rootLayer)); - layer_tree_->set_rasterizer_tracing_threshold(rasterizerTracingThreshold); - layer_tree_->set_checkerboard_raster_cache_images( - checkerboardRasterCacheImages); - layer_tree_->set_checkerboard_offscreen_layers(checkerboardOffscreenLayers); + device_width_ = viewport_metrics.physical_width; + device_height_ = viewport_metrics.physical_height; + device_pixel_ratio_ = static_cast(viewport_metrics.device_pixel_ratio); + + layer_tree_config_ = std::make_unique(); + layer_tree_config_->root_layer = std::move(rootLayer); + layer_tree_config_->rasterizer_tracing_threshold = rasterizerTracingThreshold; + layer_tree_config_->checkerboard_raster_cache_images = + checkerboardRasterCacheImages; + layer_tree_config_->checkerboard_offscreen_layers = + checkerboardOffscreenLayers; } Scene::~Scene() {} void Scene::dispose() { - layer_tree_.reset(); + layer_tree_config_.reset(); ClearDartWrapper(); } @@ -69,8 +70,8 @@ Dart_Handle Scene::toImageSync(uint32_t width, Dart_Handle raw_image_handle) { TRACE_EVENT0("flutter", "Scene::toImageSync"); - if (!layer_tree_) { - return tonic::ToDart("Scene did not contain a layer tree."); + if (!layer_tree_config_) { + return tonic::ToDart("Scene's layer tree has been taken away."); } Scene::RasterizeToImage(width, height, raw_image_handle); @@ -82,17 +83,18 @@ Dart_Handle Scene::toImage(uint32_t width, Dart_Handle raw_image_callback) { TRACE_EVENT0("flutter", "Scene::toImage"); - if (!layer_tree_) { - return tonic::ToDart("Scene did not contain a layer tree."); + if (!layer_tree_config_) { + return tonic::ToDart("Scene's layer tree has been taken away."); } - return Picture::RasterizeLayerTreeToImage(std::move(layer_tree_), width, - height, raw_image_callback); + return Picture::RasterizeLayerTreeToImage( + BuildLayerTree(device_width_, device_height_, device_pixel_ratio_), width, + height, raw_image_callback); } static sk_sp CreateDeferredImage( bool impeller, - std::shared_ptr layer_tree, + std::unique_ptr layer_tree, uint32_t width, uint32_t height, fml::TaskRunnerAffineWeakPtr snapshot_delegate, @@ -126,15 +128,24 @@ void Scene::RasterizeToImage(uint32_t width, auto image = CanvasImage::Create(); auto dl_image = CreateDeferredImage( - dart_state->IsImpellerEnabled(), layer_tree_, width, height, - std::move(snapshot_delegate), std::move(raster_task_runner), + dart_state->IsImpellerEnabled(), + BuildLayerTree(device_width_, device_height_, device_pixel_ratio_), width, + height, std::move(snapshot_delegate), std::move(raster_task_runner), std::move(unref_queue)); image->set_image(dl_image); image->AssociateWithDartWrapper(raw_image_handle); } -std::shared_ptr Scene::takeLayerTree() { - return std::move(layer_tree_); +std::unique_ptr Scene::takeLayerTree() { + return BuildLayerTree(device_width_, device_height_, device_pixel_ratio_); +} + +std::unique_ptr Scene::BuildLayerTree(uint32_t width, + uint32_t height, + float pixel_ratio) { + FML_CHECK(layer_tree_config_ != nullptr); + return std::make_unique(*layer_tree_config_, + SkISize::Make(width, height), pixel_ratio); } } // namespace flutter diff --git a/lib/ui/compositing/scene.h b/lib/ui/compositing/scene.h index aca797836fcb1..e8b8c0cf0d2ff 100644 --- a/lib/ui/compositing/scene.h +++ b/lib/ui/compositing/scene.h @@ -26,7 +26,7 @@ class Scene : public RefCountedDartWrappable { bool checkerboardRasterCacheImages, bool checkerboardOffscreenLayers); - std::shared_ptr takeLayerTree(); + std::unique_ptr takeLayerTree(); Dart_Handle toImageSync(uint32_t width, uint32_t height, @@ -48,12 +48,15 @@ class Scene : public RefCountedDartWrappable { uint32_t height, Dart_Handle raw_image_handle); - // This is a shared_ptr to support flattening the layer tree from the UI - // thread onto the raster thread - allowing access to the texture registry - // required to render TextureLayers. - // - // No longer valid after calling `takeLayerTree`. - std::shared_ptr layer_tree_; + std::unique_ptr BuildLayerTree(uint32_t width, + uint32_t height, + float pixel_ratio); + + // No longer valid after calling `takeLayerTreeConfig`. + std::unique_ptr layer_tree_config_; + float device_pixel_ratio_; + uint32_t device_width_; + uint32_t device_height_; }; } // namespace flutter diff --git a/lib/ui/painting/display_list_deferred_image_gpu_impeller.cc b/lib/ui/painting/display_list_deferred_image_gpu_impeller.cc index 40d09b8ff00c6..ffe33c079ebaa 100644 --- a/lib/ui/painting/display_list_deferred_image_gpu_impeller.cc +++ b/lib/ui/painting/display_list_deferred_image_gpu_impeller.cc @@ -3,14 +3,15 @@ // found in the LICENSE file. #include "flutter/lib/ui/painting/display_list_deferred_image_gpu_impeller.h" -#include "display_list_deferred_image_gpu_impeller.h" #include +#include "flutter/fml/make_copyable.h" + namespace flutter { sk_sp DlDeferredImageGPUImpeller::Make( - std::shared_ptr layer_tree, + std::unique_ptr layer_tree, const SkISize& size, fml::TaskRunnerAffineWeakPtr snapshot_delegate, fml::RefPtr raster_task_runner) { @@ -106,7 +107,7 @@ DlDeferredImageGPUImpeller::ImageWrapper::Make( std::shared_ptr DlDeferredImageGPUImpeller::ImageWrapper::Make( - std::shared_ptr layer_tree, + std::unique_ptr layer_tree, const SkISize& size, fml::TaskRunnerAffineWeakPtr snapshot_delegate, fml::RefPtr raster_task_runner) { @@ -152,10 +153,11 @@ bool DlDeferredImageGPUImpeller::ImageWrapper::isTextureBacked() const { } void DlDeferredImageGPUImpeller::ImageWrapper::SnapshotDisplayList( - std::shared_ptr layer_tree) { + std::unique_ptr layer_tree) { fml::TaskRunner::RunNowOrPostTask( raster_task_runner_, - [weak_this = weak_from_this(), layer_tree = std::move(layer_tree)] { + fml::MakeCopyable([weak_this = weak_from_this(), + layer_tree = std::move(layer_tree)]() { TRACE_EVENT0("flutter", "SnapshotDisplayList (impeller)"); auto wrapper = weak_this.lock(); if (!wrapper) { @@ -183,7 +185,7 @@ void DlDeferredImageGPUImpeller::ImageWrapper::SnapshotDisplayList( return; } wrapper->texture_ = snapshot->impeller_texture(); - }); + })); } std::optional diff --git a/lib/ui/painting/display_list_deferred_image_gpu_impeller.h b/lib/ui/painting/display_list_deferred_image_gpu_impeller.h index 664818da8a0fc..bad93600cd395 100644 --- a/lib/ui/painting/display_list_deferred_image_gpu_impeller.h +++ b/lib/ui/painting/display_list_deferred_image_gpu_impeller.h @@ -18,7 +18,7 @@ namespace flutter { class DlDeferredImageGPUImpeller final : public DlImage { public: static sk_sp Make( - std::shared_ptr layer_tree, + std::unique_ptr layer_tree, const SkISize& size, fml::TaskRunnerAffineWeakPtr snapshot_delegate, fml::RefPtr raster_task_runner); @@ -71,7 +71,7 @@ class DlDeferredImageGPUImpeller final : public DlImage { fml::RefPtr raster_task_runner); static std::shared_ptr Make( - std::shared_ptr layer_tree, + std::unique_ptr layer_tree, const SkISize& size, fml::TaskRunnerAffineWeakPtr snapshot_delegate, fml::RefPtr raster_task_runner); @@ -107,7 +107,7 @@ class DlDeferredImageGPUImpeller final : public DlImage { // thread task spwaned by this method. After being flattened into a display // list, the image wrapper will be updated to hold this display list and the // layer tree can be dropped. - void SnapshotDisplayList(std::shared_ptr layer_tree = nullptr); + void SnapshotDisplayList(std::unique_ptr layer_tree = nullptr); // |ContextListener| void OnGrContextCreated() override; diff --git a/lib/ui/painting/display_list_deferred_image_gpu_skia.cc b/lib/ui/painting/display_list_deferred_image_gpu_skia.cc index a2bb4c0b94cda..0cec39c737a5d 100644 --- a/lib/ui/painting/display_list_deferred_image_gpu_skia.cc +++ b/lib/ui/painting/display_list_deferred_image_gpu_skia.cc @@ -4,6 +4,7 @@ #include "flutter/lib/ui/painting/display_list_deferred_image_gpu_skia.h" +#include "flutter/fml/make_copyable.h" #include "third_party/skia/include/core/SkColorSpace.h" #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h" @@ -25,7 +26,7 @@ sk_sp DlDeferredImageGPUSkia::Make( sk_sp DlDeferredImageGPUSkia::MakeFromLayerTree( const SkImageInfo& image_info, - std::shared_ptr layer_tree, + std::unique_ptr layer_tree, fml::TaskRunnerAffineWeakPtr snapshot_delegate, const fml::RefPtr& raster_task_runner, fml::RefPtr unref_queue) { @@ -114,7 +115,7 @@ DlDeferredImageGPUSkia::ImageWrapper::Make( std::shared_ptr DlDeferredImageGPUSkia::ImageWrapper::MakeFromLayerTree( const SkImageInfo& image_info, - std::shared_ptr layer_tree, + std::unique_ptr layer_tree, fml::TaskRunnerAffineWeakPtr snapshot_delegate, fml::RefPtr raster_task_runner, fml::RefPtr unref_queue) { @@ -164,10 +165,11 @@ bool DlDeferredImageGPUSkia::ImageWrapper::isTextureBacked() const { } void DlDeferredImageGPUSkia::ImageWrapper::SnapshotDisplayList( - std::shared_ptr layer_tree) { + std::unique_ptr layer_tree) { fml::TaskRunner::RunNowOrPostTask( raster_task_runner_, - [weak_this = weak_from_this(), layer_tree = std::move(layer_tree)]() { + fml::MakeCopyable([weak_this = weak_from_this(), + layer_tree = std::move(layer_tree)]() mutable { auto wrapper = weak_this.lock(); if (!wrapper) { return; @@ -199,7 +201,7 @@ void DlDeferredImageGPUSkia::ImageWrapper::SnapshotDisplayList( std::scoped_lock lock(wrapper->error_mutex_); wrapper->error_ = result->error; } - }); + })); } std::optional DlDeferredImageGPUSkia::ImageWrapper::get_error() { diff --git a/lib/ui/painting/display_list_deferred_image_gpu_skia.h b/lib/ui/painting/display_list_deferred_image_gpu_skia.h index 2d6c9b53c5cf9..4e3558fbe9348 100644 --- a/lib/ui/painting/display_list_deferred_image_gpu_skia.h +++ b/lib/ui/painting/display_list_deferred_image_gpu_skia.h @@ -30,7 +30,7 @@ class DlDeferredImageGPUSkia final : public DlImage { static sk_sp MakeFromLayerTree( const SkImageInfo& image_info, - std::shared_ptr layer_tree, + std::unique_ptr layer_tree, fml::TaskRunnerAffineWeakPtr snapshot_delegate, const fml::RefPtr& raster_task_runner, fml::RefPtr unref_queue); @@ -85,7 +85,7 @@ class DlDeferredImageGPUSkia final : public DlImage { static std::shared_ptr MakeFromLayerTree( const SkImageInfo& image_info, - std::shared_ptr layer_tree, + std::unique_ptr layer_tree, fml::TaskRunnerAffineWeakPtr snapshot_delegate, fml::RefPtr raster_task_runner, fml::RefPtr unref_queue); @@ -125,7 +125,7 @@ class DlDeferredImageGPUSkia final : public DlImage { // thread task spwaned by this method. After being flattened into a display // list, the image wrapper will be updated to hold this display list and the // layer tree can be dropped. - void SnapshotDisplayList(std::shared_ptr layer_tree = nullptr); + void SnapshotDisplayList(std::unique_ptr layer_tree = nullptr); // |ContextListener| void OnGrContextCreated() override; diff --git a/lib/ui/painting/picture.cc b/lib/ui/painting/picture.cc index 53a9371277555..47318f7bb74f8 100644 --- a/lib/ui/painting/picture.cc +++ b/lib/ui/painting/picture.cc @@ -118,24 +118,24 @@ Dart_Handle Picture::RasterizeToImage(const sk_sp& display_list, uint32_t width, uint32_t height, Dart_Handle raw_image_callback) { - return RasterizeToImage(display_list, nullptr, width, height, - raw_image_callback); + return DoRasterizeToImage(display_list, nullptr, width, height, + raw_image_callback); } Dart_Handle Picture::RasterizeLayerTreeToImage( - std::shared_ptr layer_tree, + std::unique_ptr layer_tree, uint32_t width, uint32_t height, Dart_Handle raw_image_callback) { - return RasterizeToImage(nullptr, std::move(layer_tree), width, height, - raw_image_callback); + return DoRasterizeToImage(nullptr, std::move(layer_tree), width, height, + raw_image_callback); } -Dart_Handle Picture::RasterizeToImage(const sk_sp& display_list, - std::shared_ptr layer_tree, - uint32_t width, - uint32_t height, - Dart_Handle raw_image_callback) { +Dart_Handle Picture::DoRasterizeToImage(const sk_sp& display_list, + std::unique_ptr layer_tree, + uint32_t width, + uint32_t height, + Dart_Handle raw_image_callback) { if (Dart_IsNull(raw_image_callback) || !Dart_IsClosure(raw_image_callback)) { return tonic::ToDart("Image callback was invalid"); } @@ -198,8 +198,9 @@ Dart_Handle Picture::RasterizeToImage(const sk_sp& display_list, // Kick things off on the raster rask runner. fml::TaskRunner::RunNowOrPostTask( raster_task_runner, - [ui_task_runner, snapshot_delegate, display_list, picture_bounds, ui_task, - layer_tree = std::move(layer_tree)] { + fml::MakeCopyable([ui_task_runner, snapshot_delegate, display_list, + picture_bounds, ui_task, + layer_tree = std::move(layer_tree)]() mutable { sk_sp image; if (layer_tree) { auto display_list = layer_tree->Flatten( @@ -216,7 +217,7 @@ Dart_Handle Picture::RasterizeToImage(const sk_sp& display_list, fml::TaskRunner::RunNowOrPostTask( ui_task_runner, [ui_task, image]() { ui_task(image); }); - }); + })); return Dart_Null(); } diff --git a/lib/ui/painting/picture.h b/lib/ui/painting/picture.h index d0c2a0123fa1d..df8c17e6113f1 100644 --- a/lib/ui/painting/picture.h +++ b/lib/ui/painting/picture.h @@ -49,7 +49,7 @@ class Picture : public RefCountedDartWrappable { Dart_Handle raw_image_callback); static Dart_Handle RasterizeLayerTreeToImage( - std::shared_ptr layer_tree, + std::unique_ptr layer_tree, uint32_t width, uint32_t height, Dart_Handle raw_image_callback); @@ -57,11 +57,11 @@ class Picture : public RefCountedDartWrappable { // Callers may provide either a display list or a layer tree. If a layer tree // is provided, it will be flattened on the raster thread. In this case the // display list will be ignored. - static Dart_Handle RasterizeToImage(const sk_sp& display_list, - std::shared_ptr layer_tree, - uint32_t width, - uint32_t height, - Dart_Handle raw_image_callback); + static Dart_Handle DoRasterizeToImage(const sk_sp& display_list, + std::unique_ptr layer_tree, + uint32_t width, + uint32_t height, + Dart_Handle raw_image_callback); private: explicit Picture(sk_sp display_list); diff --git a/runtime/runtime_delegate.h b/runtime/runtime_delegate.h index 0fbb43391e113..6724085515abd 100644 --- a/runtime/runtime_delegate.h +++ b/runtime/runtime_delegate.h @@ -27,7 +27,7 @@ class RuntimeDelegate { virtual void ScheduleFrame(bool regenerate_layer_tree = true) = 0; - virtual void Render(std::shared_ptr layer_tree) = 0; + virtual void Render(std::unique_ptr layer_tree) = 0; virtual void UpdateSemantics(SemanticsNodeUpdates update, CustomAccessibilityActionUpdates actions) = 0; diff --git a/shell/common/animator.cc b/shell/common/animator.cc index 0b969c2ccbc0b..83498bc1535ba 100644 --- a/shell/common/animator.cc +++ b/shell/common/animator.cc @@ -128,7 +128,7 @@ void Animator::BeginFrame( } } -void Animator::Render(std::shared_ptr layer_tree) { +void Animator::Render(std::unique_ptr layer_tree) { has_rendered_ = true; last_layer_tree_size_ = layer_tree->frame_size(); diff --git a/shell/common/animator.h b/shell/common/animator.h index dfacb4a786bf3..c96a0f5536abb 100644 --- a/shell/common/animator.h +++ b/shell/common/animator.h @@ -54,7 +54,7 @@ class Animator final { void RequestFrame(bool regenerate_layer_tree = true); - void Render(std::shared_ptr layer_tree); + void Render(std::unique_ptr layer_tree); const std::weak_ptr GetVsyncWaiter() const; diff --git a/shell/common/animator_unittests.cc b/shell/common/animator_unittests.cc index 05c728c57ae16..b37de4fa0ccd5 100644 --- a/shell/common/animator_unittests.cc +++ b/shell/common/animator_unittests.cc @@ -155,8 +155,8 @@ TEST_F(ShellTest, AnimatorDoesNotNotifyIdleBeforeRender) { task_runners.GetUITaskRunner()->PostDelayedTask( [&] { ASSERT_FALSE(delegate.notify_idle_called_); - auto layer_tree = - std::make_shared(SkISize::Make(600, 800), 1.0); + auto layer_tree = std::make_unique( + LayerTree::Config(), SkISize::Make(600, 800), 1.0); animator->Render(std::move(layer_tree)); task_runners.GetPlatformTaskRunner()->PostTask(flush_vsync_task); }, @@ -238,8 +238,8 @@ TEST_F(ShellTest, AnimatorDoesNotNotifyDelegateIfPipelineIsNotEmpty) { begin_frame_latch.Wait(); PostTaskSync(task_runners.GetUITaskRunner(), [&] { - auto layer_tree = - std::make_shared(SkISize::Make(600, 800), 1.0); + auto layer_tree = std::make_unique( + LayerTree::Config(), SkISize::Make(600, 800), 1.0); animator->Render(std::move(layer_tree)); }); } diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 522ee89a52a7b..3e3613cef02e5 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -450,7 +450,7 @@ void Engine::ScheduleFrame(bool regenerate_layer_tree) { animator_->RequestFrame(regenerate_layer_tree); } -void Engine::Render(std::shared_ptr layer_tree) { +void Engine::Render(std::unique_ptr layer_tree) { if (!layer_tree) { return; } diff --git a/shell/common/engine.h b/shell/common/engine.h index c6ff37eae37a5..2b43b11932286 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -895,7 +895,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { std::string DefaultRouteName() override; // |RuntimeDelegate| - void Render(std::shared_ptr layer_tree) override; + void Render(std::unique_ptr layer_tree) override; // |RuntimeDelegate| void UpdateSemantics(SemanticsNodeUpdates update, diff --git a/shell/common/engine_unittests.cc b/shell/common/engine_unittests.cc index 3bfdd000c37eb..c2b2dccb9948b 100644 --- a/shell/common/engine_unittests.cc +++ b/shell/common/engine_unittests.cc @@ -51,7 +51,7 @@ class MockRuntimeDelegate : public RuntimeDelegate { MOCK_METHOD0(ImplicitViewEnabled, bool()); MOCK_METHOD0(DefaultRouteName, std::string()); MOCK_METHOD1(ScheduleFrame, void(bool)); - MOCK_METHOD1(Render, void(std::shared_ptr)); + MOCK_METHOD1(Render, void(std::unique_ptr)); MOCK_METHOD2(UpdateSemantics, void(SemanticsNodeUpdates, CustomAccessibilityActionUpdates)); MOCK_METHOD1(HandlePlatformMessage, void(std::unique_ptr)); diff --git a/shell/common/pipeline.h b/shell/common/pipeline.h index 1de4498e7f08c..df0acc6479a33 100644 --- a/shell/common/pipeline.h +++ b/shell/common/pipeline.h @@ -252,11 +252,11 @@ class Pipeline { }; struct LayerTreeItem { - LayerTreeItem(std::shared_ptr layer_tree, + LayerTreeItem(std::unique_ptr layer_tree, std::unique_ptr frame_timings_recorder) : layer_tree(std::move(layer_tree)), frame_timings_recorder(std::move(frame_timings_recorder)) {} - std::shared_ptr layer_tree; + std::unique_ptr layer_tree; std::unique_ptr frame_timings_recorder; }; diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 3b01c5c9bb0a9..3326a7dc6e547 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -195,7 +195,7 @@ RasterStatus Rasterizer::Draw( RasterStatus raster_status = RasterStatus::kFailed; LayerTreePipeline::Consumer consumer = [&](std::unique_ptr item) { - std::shared_ptr layer_tree = std::move(item->layer_tree); + std::unique_ptr layer_tree = std::move(item->layer_tree); std::unique_ptr frame_timings_recorder = std::move(item->frame_timings_recorder); if (discard_callback(*layer_tree.get())) { @@ -375,7 +375,7 @@ fml::Milliseconds Rasterizer::GetFrameBudget() const { RasterStatus Rasterizer::DoDraw( std::unique_ptr frame_timings_recorder, - std::shared_ptr layer_tree) { + std::unique_ptr layer_tree) { TRACE_EVENT_WITH_FRAME_NUMBER(frame_timings_recorder, "flutter", "Rasterizer::DoDraw"); FML_DCHECK(delegate_.GetTaskRunners() diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 23227f5d67618..fbb9386b19c3a 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -556,7 +556,7 @@ class Rasterizer final : public SnapshotDelegate, RasterStatus DoDraw( std::unique_ptr frame_timings_recorder, - std::shared_ptr layer_tree); + std::unique_ptr layer_tree); RasterStatus DrawToSurface(FrameTimingsRecorder& frame_timings_recorder, flutter::LayerTree& layer_tree); @@ -576,11 +576,11 @@ class Rasterizer final : public SnapshotDelegate, std::unique_ptr snapshot_surface_producer_; std::unique_ptr compositor_context_; // This is the last successfully rasterized layer tree. - std::shared_ptr last_layer_tree_; + std::unique_ptr last_layer_tree_; // Set when we need attempt to rasterize the layer tree again. This layer_tree // has not successfully rasterized. This can happen due to the change in the // thread configuration. This will be inserted to the front of the pipeline. - std::shared_ptr resubmitted_layer_tree_; + std::unique_ptr resubmitted_layer_tree_; std::unique_ptr resubmitted_recorder_; fml::closure next_frame_callback_; bool user_override_resource_cache_bytes_; diff --git a/shell/common/rasterizer_unittests.cc b/shell/common/rasterizer_unittests.cc index 6ab77b53401ba..c9d9edc6bb707 100644 --- a/shell/common/rasterizer_unittests.cc +++ b/shell/common/rasterizer_unittests.cc @@ -185,8 +185,10 @@ TEST(RasterizerTest, fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); - auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), - /*device_pixel_ratio=*/2.0f); + auto layer_tree = + std::make_unique(/*config=*/LayerTree::Config(), + /*frame_size=*/SkISize(), + /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = @@ -250,8 +252,9 @@ TEST( fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); - auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), - /*device_pixel_ratio=*/2.0f); + auto layer_tree = std::make_unique( + /*config=*/LayerTree::Config(), /*frame_size=*/SkISize(), + /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = @@ -321,7 +324,8 @@ TEST( rasterizer->Setup(std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); - auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), + auto layer_tree = std::make_unique(/*config=*/LayerTree::Config(), + /*frame_size=*/SkISize(), /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( std::move(layer_tree), CreateFinishedBuildRecorder()); @@ -395,7 +399,8 @@ TEST(RasterizerTest, rasterizer->Setup(std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); - auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), + auto layer_tree = std::make_unique(/*config=*/LayerTree::Config(), + /*frame_size=*/SkISize(), /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( std::move(layer_tree), CreateFinishedBuildRecorder()); @@ -444,8 +449,9 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNoSurfaceIsSet) { fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); - auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), - /*device_pixel_ratio=*/2.0f); + auto layer_tree = std::make_unique( + /*config=*/LayerTree::Config(), /*frame_size=*/SkISize(), + /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = @@ -499,8 +505,9 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNotUsedThisFrame) { fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); - auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), - /*device_pixel_ratio=*/2.0f); + auto layer_tree = std::make_unique( + /*config=*/LayerTree::Config(), /*frame_size=*/SkISize(), + /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = @@ -601,8 +608,9 @@ TEST(RasterizerTest, fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); - auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), - /*device_pixel_ratio=*/2.0f); + auto layer_tree = std::make_unique( + /*config=*/LayerTree::Config(), /*frame_size=*/SkISize(), + /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = @@ -658,8 +666,9 @@ TEST( fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); - auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), - /*device_pixel_ratio=*/2.0f); + auto layer_tree = std::make_unique( + /*config=*/LayerTree::Config(), /*frame_size=*/SkISize(), + /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = @@ -715,8 +724,9 @@ TEST( fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); - auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), - /*device_pixel_ratio=*/2.0f); + auto layer_tree = std::make_unique( + /*config=*/LayerTree::Config(), /*frame_size=*/SkISize(), + /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = @@ -771,8 +781,9 @@ TEST( fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); - auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), - /*device_pixel_ratio=*/2.0f); + auto layer_tree = std::make_unique( + /*config=*/LayerTree::Config(), /*frame_size=*/SkISize(), + /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = @@ -826,8 +837,9 @@ TEST( fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); - auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), - /*device_pixel_ratio=*/2.0f); + auto layer_tree = std::make_unique( + /*config=*/LayerTree::Config(), /*frame_size=*/SkISize(), + /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( std::move(layer_tree), CreateFinishedBuildRecorder()); PipelineProduceResult result = @@ -904,9 +916,9 @@ TEST(RasterizerTest, rasterizer->Setup(std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); for (int i = 0; i < 2; i++) { - auto layer_tree = - std::make_shared(/*frame_size=*/SkISize(), - /*device_pixel_ratio=*/2.0f); + auto layer_tree = std::make_unique( + /*config=*/LayerTree::Config(), /*frame_size=*/SkISize(), + /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( std::move(layer_tree), CreateFinishedBuildRecorder(timestamps[i])); PipelineProduceResult result = @@ -1075,9 +1087,9 @@ TEST(RasterizerTest, presentationTimeSetWhenVsyncTargetInFuture) { rasterizer->Setup(std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); for (int i = 0; i < 2; i++) { - auto layer_tree = - std::make_shared(/*frame_size=*/SkISize(), - /*device_pixel_ratio=*/2.0f); + auto layer_tree = std::make_unique( + /*config=*/LayerTree::Config(), /*frame_size=*/SkISize(), + /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( std::move(layer_tree), CreateFinishedBuildRecorder(timestamps[i])); PipelineProduceResult result = @@ -1155,8 +1167,9 @@ TEST(RasterizerTest, presentationTimeNotSetWhenVsyncTargetInPast) { thread_host.raster_thread->GetTaskRunner()->PostTask([&] { rasterizer->Setup(std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); - auto layer_tree = std::make_shared(/*frame_size=*/SkISize(), - /*device_pixel_ratio=*/2.0f); + auto layer_tree = std::make_unique( + /*config=*/LayerTree::Config(), /*frame_size=*/SkISize(), + /*device_pixel_ratio=*/2.0f); auto layer_tree_item = std::make_unique( std::move(layer_tree), CreateFinishedBuildRecorder(first_timestamp)); PipelineProduceResult result = diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index a799e6bcb4811..215919ef82ae7 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -197,14 +197,14 @@ void ShellTest::PumpOneFrame(Shell* shell, fml::WeakPtr runtime_delegate = shell->weak_engine_; shell->GetTaskRunners().GetUITaskRunner()->PostTask( [&latch, runtime_delegate, &builder, viewport_metrics]() { - auto layer_tree = std::make_shared( - SkISize::Make(viewport_metrics.physical_width, - viewport_metrics.physical_height), - static_cast(viewport_metrics.device_pixel_ratio)); SkMatrix identity; identity.setIdentity(); auto root_layer = std::make_shared(identity); - layer_tree->set_root_layer(root_layer); + auto layer_tree = std::make_unique( + LayerTree::Config{.root_layer = root_layer}, + SkISize::Make(viewport_metrics.physical_width, + viewport_metrics.physical_height), + static_cast(viewport_metrics.device_pixel_ratio)); if (builder) { builder(root_layer); } From 474b357f8c45b9611e31969ddcc4f6f49d2c78e6 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 27 Apr 2023 16:23:27 -0700 Subject: [PATCH 081/147] Let takeLayerTree remove config for now --- lib/ui/compositing/scene.cc | 11 +++++++++-- lib/ui/compositing/scene.h | 1 - 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index f85397ac881b5..77f4dc4a0f1b1 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -137,13 +137,20 @@ void Scene::RasterizeToImage(uint32_t width, } std::unique_ptr Scene::takeLayerTree() { - return BuildLayerTree(device_width_, device_height_, device_pixel_ratio_); + if (layer_tree_config_ != nullptr) { + auto layer_tree = BuildLayerTree(device_width_, device_height_, device_pixel_ratio_); + // TODO(dkwingsmt): We don't need to reset here. But certain unit tests test it. + // Let's keep it this way for now. + layer_tree_config_.reset(); + return layer_tree; + } else { + return nullptr; + } } std::unique_ptr Scene::BuildLayerTree(uint32_t width, uint32_t height, float pixel_ratio) { - FML_CHECK(layer_tree_config_ != nullptr); return std::make_unique(*layer_tree_config_, SkISize::Make(width, height), pixel_ratio); } diff --git a/lib/ui/compositing/scene.h b/lib/ui/compositing/scene.h index e8b8c0cf0d2ff..4f8c240d6c447 100644 --- a/lib/ui/compositing/scene.h +++ b/lib/ui/compositing/scene.h @@ -52,7 +52,6 @@ class Scene : public RefCountedDartWrappable { uint32_t height, float pixel_ratio); - // No longer valid after calling `takeLayerTreeConfig`. std::unique_ptr layer_tree_config_; float device_pixel_ratio_; uint32_t device_width_; From 39a3a0927ea9a38692c99f9e30d3e1cec948d1f3 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 27 Apr 2023 16:23:55 -0700 Subject: [PATCH 082/147] Format --- lib/ui/compositing/scene.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index 77f4dc4a0f1b1..084b4ea5f03f5 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -138,9 +138,10 @@ void Scene::RasterizeToImage(uint32_t width, std::unique_ptr Scene::takeLayerTree() { if (layer_tree_config_ != nullptr) { - auto layer_tree = BuildLayerTree(device_width_, device_height_, device_pixel_ratio_); - // TODO(dkwingsmt): We don't need to reset here. But certain unit tests test it. - // Let's keep it this way for now. + auto layer_tree = + BuildLayerTree(device_width_, device_height_, device_pixel_ratio_); + // TODO(dkwingsmt): We don't need to reset here. But certain unit tests test + // it. Let's keep it this way for now. layer_tree_config_.reset(); return layer_tree; } else { From ba4b01adf913dfa32273038e1e1863261b5807e2 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 27 Apr 2023 16:46:55 -0700 Subject: [PATCH 083/147] lint --- flow/layers/layer_tree_unittests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/layers/layer_tree_unittests.cc b/flow/layers/layer_tree_unittests.cc index 47673b8eb764b..6d45d39bdefbb 100644 --- a/flow/layers/layer_tree_unittests.cc +++ b/flow/layers/layer_tree_unittests.cc @@ -33,7 +33,7 @@ class LayerTreeTest : public CanvasTest { CompositorContext::ScopedFrame& frame() { return *scoped_frame_.get(); } const SkMatrix& root_transform() { return root_transform_; } - std::unique_ptr BuildLayerTree(LayerTree::Config config) { + std::unique_ptr BuildLayerTree(const LayerTree::Config& config) { return std::make_unique(config, SkISize::Make(64, 64), 1.0f); } From c8bd69b1be2a9f25866cc377fccaf2498f681e14 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 27 Apr 2023 17:15:58 -0700 Subject: [PATCH 084/147] Compute layer tree size in RuntimeConfiguration --- lib/ui/compositing/scene.cc | 16 +++++++--------- lib/ui/compositing/scene.h | 5 ++--- lib/ui/compositing/scene_builder_unittests.cc | 2 +- runtime/runtime_controller.cc | 11 ++++++++++- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index 084b4ea5f03f5..b7e71fe66ebc2 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -45,8 +45,6 @@ Scene::Scene(std::shared_ptr rootLayer, ->platform_configuration() ->get_window(0) ->viewport_metrics(); - device_width_ = viewport_metrics.physical_width; - device_height_ = viewport_metrics.physical_height; device_pixel_ratio_ = static_cast(viewport_metrics.device_pixel_ratio); layer_tree_config_ = std::make_unique(); @@ -88,8 +86,8 @@ Dart_Handle Scene::toImage(uint32_t width, } return Picture::RasterizeLayerTreeToImage( - BuildLayerTree(device_width_, device_height_, device_pixel_ratio_), width, - height, raw_image_callback); + BuildLayerTree(width, height, device_pixel_ratio_), width, height, + raw_image_callback); } static sk_sp CreateDeferredImage( @@ -129,17 +127,17 @@ void Scene::RasterizeToImage(uint32_t width, auto image = CanvasImage::Create(); auto dl_image = CreateDeferredImage( dart_state->IsImpellerEnabled(), - BuildLayerTree(device_width_, device_height_, device_pixel_ratio_), width, - height, std::move(snapshot_delegate), std::move(raster_task_runner), + BuildLayerTree(width, height, device_pixel_ratio_), width, height, + std::move(snapshot_delegate), std::move(raster_task_runner), std::move(unref_queue)); image->set_image(dl_image); image->AssociateWithDartWrapper(raw_image_handle); } -std::unique_ptr Scene::takeLayerTree() { +std::unique_ptr Scene::takeLayerTree(uint64_t width, + uint64_t height) { if (layer_tree_config_ != nullptr) { - auto layer_tree = - BuildLayerTree(device_width_, device_height_, device_pixel_ratio_); + auto layer_tree = BuildLayerTree(width, height, device_pixel_ratio_); // TODO(dkwingsmt): We don't need to reset here. But certain unit tests test // it. Let's keep it this way for now. layer_tree_config_.reset(); diff --git a/lib/ui/compositing/scene.h b/lib/ui/compositing/scene.h index 4f8c240d6c447..5c9bf791135f8 100644 --- a/lib/ui/compositing/scene.h +++ b/lib/ui/compositing/scene.h @@ -26,7 +26,8 @@ class Scene : public RefCountedDartWrappable { bool checkerboardRasterCacheImages, bool checkerboardOffscreenLayers); - std::unique_ptr takeLayerTree(); + std::unique_ptr takeLayerTree(uint64_t width, + uint64_t height); Dart_Handle toImageSync(uint32_t width, uint32_t height, @@ -54,8 +55,6 @@ class Scene : public RefCountedDartWrappable { std::unique_ptr layer_tree_config_; float device_pixel_ratio_; - uint32_t device_width_; - uint32_t device_height_; }; } // namespace flutter diff --git a/lib/ui/compositing/scene_builder_unittests.cc b/lib/ui/compositing/scene_builder_unittests.cc index 41efcdc64efd5..702d83bf61417 100644 --- a/lib/ui/compositing/scene_builder_unittests.cc +++ b/lib/ui/compositing/scene_builder_unittests.cc @@ -59,7 +59,7 @@ TEST_F(ShellTest, SceneBuilderBuildAndSceneDisposeReleasesLayerStack) { auto validate_scene_has_no_layers = [message_latch, &retained_scene](Dart_NativeArguments args) { - EXPECT_FALSE(retained_scene->takeLayerTree()); + EXPECT_FALSE(retained_scene->takeLayerTree(100, 100)); retained_scene->Release(); retained_scene = nullptr; message_latch->Signal(); diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 97664325fe4a0..50417a52ce797 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -316,7 +316,16 @@ void RuntimeController::ScheduleFrame() { // |PlatformConfigurationClient| void RuntimeController::Render(Scene* scene) { - client_.Render(scene->takeLayerTree()); + // TODO(dkwingsmt): Currently only supports a single window. + int64_t view_id = 0ll; + auto window = + UIDartState::Current()->platform_configuration()->get_window(view_id); + if (window == nullptr) { + return; + } + const auto& viewport_metrics = window->viewport_metrics(); + client_.Render(scene->takeLayerTree(viewport_metrics.physical_width, + viewport_metrics.physical_height)); } // |PlatformConfigurationClient| From 6a03556ddfa86305381f3b5b70ad061a4a63ea77 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 27 Apr 2023 17:58:11 -0700 Subject: [PATCH 085/147] Resolve toImageSync --- lib/ui/compositing/scene.cc | 16 ++++++++-------- .../display_list_deferred_image_gpu_impeller.cc | 10 ++++------ .../display_list_deferred_image_gpu_impeller.h | 2 -- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index b7e71fe66ebc2..7d7bc4dd5acba 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -93,21 +93,21 @@ Dart_Handle Scene::toImage(uint32_t width, static sk_sp CreateDeferredImage( bool impeller, std::unique_ptr layer_tree, - uint32_t width, - uint32_t height, fml::TaskRunnerAffineWeakPtr snapshot_delegate, fml::RefPtr raster_task_runner, fml::RefPtr unref_queue) { #if IMPELLER_SUPPORTS_RENDERING if (impeller) { - return DlDeferredImageGPUImpeller::Make( - std::move(layer_tree), SkISize::Make(width, height), - std::move(snapshot_delegate), std::move(raster_task_runner)); + return DlDeferredImageGPUImpeller::Make(std::move(layer_tree), + std::move(snapshot_delegate), + std::move(raster_task_runner)); } #endif // IMPELLER_SUPPORTS_RENDERING - const SkImageInfo image_info = SkImageInfo::Make( - width, height, kRGBA_8888_SkColorType, kPremul_SkAlphaType); + const auto& frame_size = layer_tree->frame_size(); + const SkImageInfo image_info = + SkImageInfo::Make(frame_size.width(), frame_size.height(), + kRGBA_8888_SkColorType, kPremul_SkAlphaType); return DlDeferredImageGPUSkia::MakeFromLayerTree( image_info, std::move(layer_tree), std::move(snapshot_delegate), raster_task_runner, std::move(unref_queue)); @@ -127,7 +127,7 @@ void Scene::RasterizeToImage(uint32_t width, auto image = CanvasImage::Create(); auto dl_image = CreateDeferredImage( dart_state->IsImpellerEnabled(), - BuildLayerTree(width, height, device_pixel_ratio_), width, height, + BuildLayerTree(width, height, device_pixel_ratio_), std::move(snapshot_delegate), std::move(raster_task_runner), std::move(unref_queue)); image->set_image(dl_image); diff --git a/lib/ui/painting/display_list_deferred_image_gpu_impeller.cc b/lib/ui/painting/display_list_deferred_image_gpu_impeller.cc index ffe33c079ebaa..0c2a7de57cca4 100644 --- a/lib/ui/painting/display_list_deferred_image_gpu_impeller.cc +++ b/lib/ui/painting/display_list_deferred_image_gpu_impeller.cc @@ -12,12 +12,11 @@ namespace flutter { sk_sp DlDeferredImageGPUImpeller::Make( std::unique_ptr layer_tree, - const SkISize& size, fml::TaskRunnerAffineWeakPtr snapshot_delegate, fml::RefPtr raster_task_runner) { return sk_sp(new DlDeferredImageGPUImpeller( DlDeferredImageGPUImpeller::ImageWrapper::Make( - std::move(layer_tree), size, std::move(snapshot_delegate), + std::move(layer_tree), std::move(snapshot_delegate), std::move(raster_task_runner)))); } @@ -108,12 +107,11 @@ DlDeferredImageGPUImpeller::ImageWrapper::Make( std::shared_ptr DlDeferredImageGPUImpeller::ImageWrapper::Make( std::unique_ptr layer_tree, - const SkISize& size, fml::TaskRunnerAffineWeakPtr snapshot_delegate, fml::RefPtr raster_task_runner) { - auto wrapper = std::shared_ptr( - new ImageWrapper(nullptr, size, std::move(snapshot_delegate), - std::move(raster_task_runner))); + auto wrapper = std::shared_ptr(new ImageWrapper( + nullptr, layer_tree->frame_size(), std::move(snapshot_delegate), + std::move(raster_task_runner))); wrapper->SnapshotDisplayList(std::move(layer_tree)); return wrapper; } diff --git a/lib/ui/painting/display_list_deferred_image_gpu_impeller.h b/lib/ui/painting/display_list_deferred_image_gpu_impeller.h index bad93600cd395..7bcb27922423e 100644 --- a/lib/ui/painting/display_list_deferred_image_gpu_impeller.h +++ b/lib/ui/painting/display_list_deferred_image_gpu_impeller.h @@ -19,7 +19,6 @@ class DlDeferredImageGPUImpeller final : public DlImage { public: static sk_sp Make( std::unique_ptr layer_tree, - const SkISize& size, fml::TaskRunnerAffineWeakPtr snapshot_delegate, fml::RefPtr raster_task_runner); @@ -72,7 +71,6 @@ class DlDeferredImageGPUImpeller final : public DlImage { static std::shared_ptr Make( std::unique_ptr layer_tree, - const SkISize& size, fml::TaskRunnerAffineWeakPtr snapshot_delegate, fml::RefPtr raster_task_runner); From 30b95c67683d07d3f53bcf4489a7dd0fab2b244f Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 27 Apr 2023 18:55:51 -0700 Subject: [PATCH 086/147] Convert toImage --- lib/ui/compositing/scene.cc | 3 +-- lib/ui/painting/picture.cc | 7 +++---- lib/ui/painting/picture.h | 2 -- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index 7d7bc4dd5acba..3dfffc0e6d0dc 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -86,8 +86,7 @@ Dart_Handle Scene::toImage(uint32_t width, } return Picture::RasterizeLayerTreeToImage( - BuildLayerTree(width, height, device_pixel_ratio_), width, height, - raw_image_callback); + BuildLayerTree(width, height, device_pixel_ratio_), raw_image_callback); } static sk_sp CreateDeferredImage( diff --git a/lib/ui/painting/picture.cc b/lib/ui/painting/picture.cc index 47318f7bb74f8..c8cf17c9c511e 100644 --- a/lib/ui/painting/picture.cc +++ b/lib/ui/painting/picture.cc @@ -124,11 +124,10 @@ Dart_Handle Picture::RasterizeToImage(const sk_sp& display_list, Dart_Handle Picture::RasterizeLayerTreeToImage( std::unique_ptr layer_tree, - uint32_t width, - uint32_t height, Dart_Handle raw_image_callback) { - return DoRasterizeToImage(nullptr, std::move(layer_tree), width, height, - raw_image_callback); + auto frame_size = layer_tree->frame_size(); + return DoRasterizeToImage(nullptr, std::move(layer_tree), frame_size.width(), + frame_size.height(), raw_image_callback); } Dart_Handle Picture::DoRasterizeToImage(const sk_sp& display_list, diff --git a/lib/ui/painting/picture.h b/lib/ui/painting/picture.h index df8c17e6113f1..14934eecded6a 100644 --- a/lib/ui/painting/picture.h +++ b/lib/ui/painting/picture.h @@ -50,8 +50,6 @@ class Picture : public RefCountedDartWrappable { static Dart_Handle RasterizeLayerTreeToImage( std::unique_ptr layer_tree, - uint32_t width, - uint32_t height, Dart_Handle raw_image_callback); // Callers may provide either a display list or a layer tree. If a layer tree From ae2c065b2266be09b43937ae55689a51c1dc827a Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 27 Apr 2023 20:51:14 -0700 Subject: [PATCH 087/147] Right before dart_ui.cc --- lib/ui/compositing/scene.cc | 27 +++++++++---------- lib/ui/compositing/scene.h | 6 +++-- lib/ui/compositing/scene_builder_unittests.cc | 2 +- lib/ui/painting/picture.cc | 7 ++++- lib/ui/painting/picture.h | 7 ++--- runtime/runtime_controller.cc | 3 ++- 6 files changed, 30 insertions(+), 22 deletions(-) diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index 3dfffc0e6d0dc..6424e1b1eb237 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -40,13 +40,6 @@ Scene::Scene(std::shared_ptr rootLayer, uint32_t rasterizerTracingThreshold, bool checkerboardRasterCacheImages, bool checkerboardOffscreenLayers) { - // Currently only supports a single window. - auto viewport_metrics = UIDartState::Current() - ->platform_configuration() - ->get_window(0) - ->viewport_metrics(); - device_pixel_ratio_ = static_cast(viewport_metrics.device_pixel_ratio); - layer_tree_config_ = std::make_unique(); layer_tree_config_->root_layer = std::move(rootLayer); layer_tree_config_->rasterizer_tracing_threshold = rasterizerTracingThreshold; @@ -67,12 +60,14 @@ Dart_Handle Scene::toImageSync(uint32_t width, uint32_t height, Dart_Handle raw_image_handle) { TRACE_EVENT0("flutter", "Scene::toImageSync"); + double pixel_ratio = 1.0f; // TODO(dkwingsmt) if (!layer_tree_config_) { return tonic::ToDart("Scene's layer tree has been taken away."); } - Scene::RasterizeToImage(width, height, raw_image_handle); + Scene::RasterizeToImage(width, height, static_cast(pixel_ratio), + raw_image_handle); return Dart_Null(); } @@ -80,13 +75,15 @@ Dart_Handle Scene::toImage(uint32_t width, uint32_t height, Dart_Handle raw_image_callback) { TRACE_EVENT0("flutter", "Scene::toImage"); + double pixel_ratio = 1.0f; // TODO(dkwingsmt) if (!layer_tree_config_) { return tonic::ToDart("Scene's layer tree has been taken away."); } return Picture::RasterizeLayerTreeToImage( - BuildLayerTree(width, height, device_pixel_ratio_), raw_image_callback); + BuildLayerTree(width, height, static_cast(pixel_ratio)), + raw_image_callback); } static sk_sp CreateDeferredImage( @@ -114,6 +111,7 @@ static sk_sp CreateDeferredImage( void Scene::RasterizeToImage(uint32_t width, uint32_t height, + float pixel_ratio, Dart_Handle raw_image_handle) { auto* dart_state = UIDartState::Current(); if (!dart_state) { @@ -126,17 +124,17 @@ void Scene::RasterizeToImage(uint32_t width, auto image = CanvasImage::Create(); auto dl_image = CreateDeferredImage( dart_state->IsImpellerEnabled(), - BuildLayerTree(width, height, device_pixel_ratio_), - std::move(snapshot_delegate), std::move(raster_task_runner), - std::move(unref_queue)); + BuildLayerTree(width, height, pixel_ratio), std::move(snapshot_delegate), + std::move(raster_task_runner), std::move(unref_queue)); image->set_image(dl_image); image->AssociateWithDartWrapper(raw_image_handle); } std::unique_ptr Scene::takeLayerTree(uint64_t width, - uint64_t height) { + uint64_t height, + float pixel_ratio) { if (layer_tree_config_ != nullptr) { - auto layer_tree = BuildLayerTree(width, height, device_pixel_ratio_); + auto layer_tree = BuildLayerTree(width, height, pixel_ratio); // TODO(dkwingsmt): We don't need to reset here. But certain unit tests test // it. Let's keep it this way for now. layer_tree_config_.reset(); @@ -149,6 +147,7 @@ std::unique_ptr Scene::takeLayerTree(uint64_t width, std::unique_ptr Scene::BuildLayerTree(uint32_t width, uint32_t height, float pixel_ratio) { + FML_CHECK(layer_tree_config_ != nullptr); return std::make_unique(*layer_tree_config_, SkISize::Make(width, height), pixel_ratio); } diff --git a/lib/ui/compositing/scene.h b/lib/ui/compositing/scene.h index 5c9bf791135f8..14fecc9a5ec2d 100644 --- a/lib/ui/compositing/scene.h +++ b/lib/ui/compositing/scene.h @@ -27,7 +27,8 @@ class Scene : public RefCountedDartWrappable { bool checkerboardOffscreenLayers); std::unique_ptr takeLayerTree(uint64_t width, - uint64_t height); + uint64_t height, + float pixel_ratio); Dart_Handle toImageSync(uint32_t width, uint32_t height, @@ -47,14 +48,15 @@ class Scene : public RefCountedDartWrappable { void RasterizeToImage(uint32_t width, uint32_t height, + float pixel_ratio, Dart_Handle raw_image_handle); std::unique_ptr BuildLayerTree(uint32_t width, uint32_t height, float pixel_ratio); + // No longer valid after calling `takeLayerTreeConfig`. std::unique_ptr layer_tree_config_; - float device_pixel_ratio_; }; } // namespace flutter diff --git a/lib/ui/compositing/scene_builder_unittests.cc b/lib/ui/compositing/scene_builder_unittests.cc index 702d83bf61417..93060ea6025c8 100644 --- a/lib/ui/compositing/scene_builder_unittests.cc +++ b/lib/ui/compositing/scene_builder_unittests.cc @@ -59,7 +59,7 @@ TEST_F(ShellTest, SceneBuilderBuildAndSceneDisposeReleasesLayerStack) { auto validate_scene_has_no_layers = [message_latch, &retained_scene](Dart_NativeArguments args) { - EXPECT_FALSE(retained_scene->takeLayerTree(100, 100)); + EXPECT_FALSE(retained_scene->takeLayerTree(100, 100, 1.0f)); retained_scene->Release(); retained_scene = nullptr; message_latch->Signal(); diff --git a/lib/ui/painting/picture.cc b/lib/ui/painting/picture.cc index c8cf17c9c511e..21104e2e41328 100644 --- a/lib/ui/painting/picture.cc +++ b/lib/ui/painting/picture.cc @@ -125,6 +125,7 @@ Dart_Handle Picture::RasterizeToImage(const sk_sp& display_list, Dart_Handle Picture::RasterizeLayerTreeToImage( std::unique_ptr layer_tree, Dart_Handle raw_image_callback) { + FML_DCHECK(layer_tree != nullptr); auto frame_size = layer_tree->frame_size(); return DoRasterizeToImage(nullptr, std::move(layer_tree), frame_size.width(), frame_size.height(), raw_image_callback); @@ -135,6 +136,9 @@ Dart_Handle Picture::DoRasterizeToImage(const sk_sp& display_list, uint32_t width, uint32_t height, Dart_Handle raw_image_callback) { + // Either display_list or layer_tree should be provided. + FML_DCHECK((display_list == nullptr) != (layer_tree == nullptr)); + if (Dart_IsNull(raw_image_callback) || !Dart_IsClosure(raw_image_callback)) { return tonic::ToDart("Image callback was invalid"); } @@ -198,10 +202,11 @@ Dart_Handle Picture::DoRasterizeToImage(const sk_sp& display_list, fml::TaskRunner::RunNowOrPostTask( raster_task_runner, fml::MakeCopyable([ui_task_runner, snapshot_delegate, display_list, - picture_bounds, ui_task, + &picture_bounds, ui_task, layer_tree = std::move(layer_tree)]() mutable { sk_sp image; if (layer_tree) { + FML_DCHECK(picture_bounds == layer_tree->frame_size()); auto display_list = layer_tree->Flatten( SkRect::MakeWH(picture_bounds.width(), picture_bounds.height()), snapshot_delegate->GetTextureRegistry(), diff --git a/lib/ui/painting/picture.h b/lib/ui/painting/picture.h index 14934eecded6a..89c84095336ed 100644 --- a/lib/ui/painting/picture.h +++ b/lib/ui/painting/picture.h @@ -52,9 +52,10 @@ class Picture : public RefCountedDartWrappable { std::unique_ptr layer_tree, Dart_Handle raw_image_callback); - // Callers may provide either a display list or a layer tree. If a layer tree - // is provided, it will be flattened on the raster thread. In this case the - // display list will be ignored. + // Callers may provide either a display list or a layer tree, but not both. + // + // If a layer tree is provided, it will be flattened on the raster thread, and + // picture_bounds should be the layer tree's frame_size(). static Dart_Handle DoRasterizeToImage(const sk_sp& display_list, std::unique_ptr layer_tree, uint32_t width, diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 50417a52ce797..b53832ecb4bb5 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -325,7 +325,8 @@ void RuntimeController::Render(Scene* scene) { } const auto& viewport_metrics = window->viewport_metrics(); client_.Render(scene->takeLayerTree(viewport_metrics.physical_width, - viewport_metrics.physical_height)); + viewport_metrics.physical_height, + viewport_metrics.device_pixel_ratio)); } // |PlatformConfigurationClient| From d7fa9d67f6408dc075d8c950b86348b755fdf33d Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 28 Apr 2023 00:58:10 -0700 Subject: [PATCH 088/147] Fix crash --- lib/ui/painting/picture.cc | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/ui/painting/picture.cc b/lib/ui/painting/picture.cc index 21104e2e41328..74c7b0dc78141 100644 --- a/lib/ui/painting/picture.cc +++ b/lib/ui/painting/picture.cc @@ -160,8 +160,6 @@ Dart_Handle Picture::DoRasterizeToImage(const sk_sp& display_list, // thread owns the sole reference to the layer tree. So we do it in the // raster thread. - auto picture_bounds = SkISize::Make(width, height); - auto ui_task = // The static leak checker gets confused by the use of fml::MakeCopyable. // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) @@ -201,16 +199,17 @@ Dart_Handle Picture::DoRasterizeToImage(const sk_sp& display_list, // Kick things off on the raster rask runner. fml::TaskRunner::RunNowOrPostTask( raster_task_runner, - fml::MakeCopyable([ui_task_runner, snapshot_delegate, display_list, - &picture_bounds, ui_task, + fml::MakeCopyable([ui_task_runner, snapshot_delegate, display_list, width, + height, ui_task, layer_tree = std::move(layer_tree)]() mutable { + auto picture_bounds = SkISize::Make(width, height); sk_sp image; if (layer_tree) { FML_DCHECK(picture_bounds == layer_tree->frame_size()); - auto display_list = layer_tree->Flatten( - SkRect::MakeWH(picture_bounds.width(), picture_bounds.height()), - snapshot_delegate->GetTextureRegistry(), - snapshot_delegate->GetGrContext()); + auto display_list = + layer_tree->Flatten(SkRect::MakeWH(width, height), + snapshot_delegate->GetTextureRegistry(), + snapshot_delegate->GetGrContext()); image = snapshot_delegate->MakeRasterSnapshot(display_list, picture_bounds); From 2ccc86975d839c9ccb7fb6411c035c3aa25a90a0 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 28 Apr 2023 04:10:16 -0700 Subject: [PATCH 089/147] To dart:ui --- lib/ui/compositing.dart | 32 ++++++++++++++++++++++++-------- lib/ui/compositing/scene.cc | 4 ++-- lib/ui/compositing/scene.h | 2 ++ lib/ui/dart_ui.cc | 4 ++-- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/lib/ui/compositing.dart b/lib/ui/compositing.dart index f20ecff7349c6..e2e0cb11159f1 100644 --- a/lib/ui/compositing.dart +++ b/lib/ui/compositing.dart @@ -21,21 +21,27 @@ class Scene extends NativeFieldWrapperClass1 { /// Synchronously creates a handle to an image from this scene. /// /// {@macro dart.ui.painting.Picture.toImageSync} - Image toImageSync(int width, int height) { + /// + /// The value of `pixelRatio` does not affect the dimension of the resulting + /// image. Instead, it is used to calculate the device's physical dimension + /// (for example, the device's physical width will be `width` / `pixelRatio`), + /// which is used by some deprecated shapes (see + /// [SceneBuilder.pushPhysicalShape]). This argument defaults to 1.0. + Image toImageSync(int width, int height, [double pixelRatio = 1.0]) { if (width <= 0 || height <= 0) { throw Exception('Invalid image dimensions.'); } final _Image image = _Image._(); - final String? result = _toImageSync(width, height, image); + final String? result = _toImageSync(width, height, pixelRatio, image); if (result != null) { throw PictureRasterizationException._(result); } return Image._(image, image.width, image.height); } - @Native, Uint32, Uint32, Handle)>(symbol: 'Scene::toImageSync') - external String? _toImageSync(int width, int height, _Image outImage); + @Native, Uint32, Uint32, Double, Handle)>(symbol: 'Scene::toImageSync') + external String? _toImageSync(int width, int height, double pixelRatio, _Image outImage); /// Creates a raster image representation of the current state of the scene. /// @@ -44,11 +50,21 @@ class Scene extends NativeFieldWrapperClass1 { /// Callers must dispose the [Image] when they are done with it. If the result /// will be shared with other methods or classes, [Image.clone] should be used /// and each handle created must be disposed. - Future toImage(int width, int height) { + /// + /// The returned image will be `width` pixels wide and `height` pixels high. + /// The picture is rasterized within the 0 (left), 0 (top), `width` (right), + /// `height` (bottom) bounds. Content outside these bounds is clipped. + /// + /// The value of `pixelRatio` does not affect the dimension of the resulting + /// image. Instead, it is used to calculate the device's physical dimension + /// (for example, the device's physical width will be `width` / `pixelRatio`), + /// which is used by some deprecated shapes (see + /// [SceneBuilder.pushPhysicalShape]). This argument defaults to 1.0. + Future toImage(int width, int height, [double pixelRatio = 1.0]) { if (width <= 0 || height <= 0) { throw Exception('Invalid image dimensions.'); } - return _futurize((_Callback callback) => _toImage(width, height, (_Image? image) { + return _futurize((_Callback callback) => _toImage(width, height, pixelRatio, (_Image? image) { if (image == null) { callback(null); } else { @@ -58,8 +74,8 @@ class Scene extends NativeFieldWrapperClass1 { ); } - @Native, Uint32, Uint32, Handle)>(symbol: 'Scene::toImage') - external String? _toImage(int width, int height, _Callback<_Image?> callback); + @Native, Uint32, Uint32, Double, Handle)>(symbol: 'Scene::toImage') + external String? _toImage(int width, int height, double pixelRatio, _Callback<_Image?> callback); /// Releases the resources used by this scene. /// diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index 6424e1b1eb237..f28c00adafedf 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -58,9 +58,9 @@ void Scene::dispose() { Dart_Handle Scene::toImageSync(uint32_t width, uint32_t height, + double pixel_ratio, Dart_Handle raw_image_handle) { TRACE_EVENT0("flutter", "Scene::toImageSync"); - double pixel_ratio = 1.0f; // TODO(dkwingsmt) if (!layer_tree_config_) { return tonic::ToDart("Scene's layer tree has been taken away."); @@ -73,9 +73,9 @@ Dart_Handle Scene::toImageSync(uint32_t width, Dart_Handle Scene::toImage(uint32_t width, uint32_t height, + double pixel_ratio, Dart_Handle raw_image_callback) { TRACE_EVENT0("flutter", "Scene::toImage"); - double pixel_ratio = 1.0f; // TODO(dkwingsmt) if (!layer_tree_config_) { return tonic::ToDart("Scene's layer tree has been taken away."); diff --git a/lib/ui/compositing/scene.h b/lib/ui/compositing/scene.h index 14fecc9a5ec2d..e2494011e7d76 100644 --- a/lib/ui/compositing/scene.h +++ b/lib/ui/compositing/scene.h @@ -32,10 +32,12 @@ class Scene : public RefCountedDartWrappable { Dart_Handle toImageSync(uint32_t width, uint32_t height, + double pixel_ratio, Dart_Handle raw_image_handle); Dart_Handle toImage(uint32_t width, uint32_t height, + double pixel_ratio, Dart_Handle raw_image_handle); void dispose(); diff --git a/lib/ui/dart_ui.cc b/lib/ui/dart_ui.cc index 57242c11ac9aa..7c6768497c666 100644 --- a/lib/ui/dart_ui.cc +++ b/lib/ui/dart_ui.cc @@ -293,8 +293,8 @@ typedef CanvasPath Path; V(SceneBuilder, setCheckerboardRasterCacheImages, 2) \ V(SceneBuilder, setRasterizerTracingThreshold, 2) \ V(Scene, dispose, 1) \ - V(Scene, toImage, 4) \ - V(Scene, toImageSync, 4) \ + V(Scene, toImage, 5) \ + V(Scene, toImageSync, 5) \ V(SemanticsUpdateBuilder, build, 2) \ V(SemanticsUpdateBuilder, updateCustomAction, 5) \ V(SemanticsUpdateBuilder, updateNode, 36) \ From 99bfd249a01f9ea8f786d125786529475a2bad51 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 28 Apr 2023 12:21:24 -0700 Subject: [PATCH 090/147] config is no longer a ptr --- lib/ui/compositing/scene.cc | 39 +++++++++++++++++-------------------- lib/ui/compositing/scene.h | 6 ++++-- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index f28c00adafedf..ca56375627139 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -40,19 +40,22 @@ Scene::Scene(std::shared_ptr rootLayer, uint32_t rasterizerTracingThreshold, bool checkerboardRasterCacheImages, bool checkerboardOffscreenLayers) { - layer_tree_config_ = std::make_unique(); - layer_tree_config_->root_layer = std::move(rootLayer); - layer_tree_config_->rasterizer_tracing_threshold = rasterizerTracingThreshold; - layer_tree_config_->checkerboard_raster_cache_images = + layer_tree_config_.root_layer = std::move(rootLayer); + layer_tree_config_.rasterizer_tracing_threshold = rasterizerTracingThreshold; + layer_tree_config_.checkerboard_raster_cache_images = checkerboardRasterCacheImages; - layer_tree_config_->checkerboard_offscreen_layers = + layer_tree_config_.checkerboard_offscreen_layers = checkerboardOffscreenLayers; } Scene::~Scene() {} +bool Scene::valid() { + return layer_tree_config_.root_layer != nullptr; +} + void Scene::dispose() { - layer_tree_config_.reset(); + layer_tree_config_.root_layer.reset(); ClearDartWrapper(); } @@ -62,8 +65,8 @@ Dart_Handle Scene::toImageSync(uint32_t width, Dart_Handle raw_image_handle) { TRACE_EVENT0("flutter", "Scene::toImageSync"); - if (!layer_tree_config_) { - return tonic::ToDart("Scene's layer tree has been taken away."); + if (!valid()) { + return tonic::ToDart("Scene has been disposed."); } Scene::RasterizeToImage(width, height, static_cast(pixel_ratio), @@ -77,8 +80,8 @@ Dart_Handle Scene::toImage(uint32_t width, Dart_Handle raw_image_callback) { TRACE_EVENT0("flutter", "Scene::toImage"); - if (!layer_tree_config_) { - return tonic::ToDart("Scene's layer tree has been taken away."); + if (!valid()) { + return tonic::ToDart("Scene has been disposed."); } return Picture::RasterizeLayerTreeToImage( @@ -133,22 +136,16 @@ void Scene::RasterizeToImage(uint32_t width, std::unique_ptr Scene::takeLayerTree(uint64_t width, uint64_t height, float pixel_ratio) { - if (layer_tree_config_ != nullptr) { - auto layer_tree = BuildLayerTree(width, height, pixel_ratio); - // TODO(dkwingsmt): We don't need to reset here. But certain unit tests test - // it. Let's keep it this way for now. - layer_tree_config_.reset(); - return layer_tree; - } else { - return nullptr; - } + return BuildLayerTree(width, height, pixel_ratio); } std::unique_ptr Scene::BuildLayerTree(uint32_t width, uint32_t height, float pixel_ratio) { - FML_CHECK(layer_tree_config_ != nullptr); - return std::make_unique(*layer_tree_config_, + if (!valid()) { + return nullptr; + } + return std::make_unique(layer_tree_config_, SkISize::Make(width, height), pixel_ratio); } diff --git a/lib/ui/compositing/scene.h b/lib/ui/compositing/scene.h index e2494011e7d76..c8b9883de7e40 100644 --- a/lib/ui/compositing/scene.h +++ b/lib/ui/compositing/scene.h @@ -48,6 +48,9 @@ class Scene : public RefCountedDartWrappable { bool checkerboardRasterCacheImages, bool checkerboardOffscreenLayers); + // Returns true if `dispose()` has not been called. + bool valid(); + void RasterizeToImage(uint32_t width, uint32_t height, float pixel_ratio, @@ -57,8 +60,7 @@ class Scene : public RefCountedDartWrappable { uint32_t height, float pixel_ratio); - // No longer valid after calling `takeLayerTreeConfig`. - std::unique_ptr layer_tree_config_; + flutter::LayerTree::Config layer_tree_config_; }; } // namespace flutter From 768aa5ef8f4c595dab8cd540564025262dd36397 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 28 Apr 2023 12:56:16 -0700 Subject: [PATCH 091/147] Implicit ratio --- lib/ui/compositing.dart | 32 ++++++++------------------------ lib/ui/compositing/scene.cc | 28 ++++++++++++++++++++++++---- lib/ui/compositing/scene.h | 15 +++++++++++++-- lib/ui/dart_ui.cc | 4 ++-- 4 files changed, 47 insertions(+), 32 deletions(-) diff --git a/lib/ui/compositing.dart b/lib/ui/compositing.dart index e2e0cb11159f1..f20ecff7349c6 100644 --- a/lib/ui/compositing.dart +++ b/lib/ui/compositing.dart @@ -21,27 +21,21 @@ class Scene extends NativeFieldWrapperClass1 { /// Synchronously creates a handle to an image from this scene. /// /// {@macro dart.ui.painting.Picture.toImageSync} - /// - /// The value of `pixelRatio` does not affect the dimension of the resulting - /// image. Instead, it is used to calculate the device's physical dimension - /// (for example, the device's physical width will be `width` / `pixelRatio`), - /// which is used by some deprecated shapes (see - /// [SceneBuilder.pushPhysicalShape]). This argument defaults to 1.0. - Image toImageSync(int width, int height, [double pixelRatio = 1.0]) { + Image toImageSync(int width, int height) { if (width <= 0 || height <= 0) { throw Exception('Invalid image dimensions.'); } final _Image image = _Image._(); - final String? result = _toImageSync(width, height, pixelRatio, image); + final String? result = _toImageSync(width, height, image); if (result != null) { throw PictureRasterizationException._(result); } return Image._(image, image.width, image.height); } - @Native, Uint32, Uint32, Double, Handle)>(symbol: 'Scene::toImageSync') - external String? _toImageSync(int width, int height, double pixelRatio, _Image outImage); + @Native, Uint32, Uint32, Handle)>(symbol: 'Scene::toImageSync') + external String? _toImageSync(int width, int height, _Image outImage); /// Creates a raster image representation of the current state of the scene. /// @@ -50,21 +44,11 @@ class Scene extends NativeFieldWrapperClass1 { /// Callers must dispose the [Image] when they are done with it. If the result /// will be shared with other methods or classes, [Image.clone] should be used /// and each handle created must be disposed. - /// - /// The returned image will be `width` pixels wide and `height` pixels high. - /// The picture is rasterized within the 0 (left), 0 (top), `width` (right), - /// `height` (bottom) bounds. Content outside these bounds is clipped. - /// - /// The value of `pixelRatio` does not affect the dimension of the resulting - /// image. Instead, it is used to calculate the device's physical dimension - /// (for example, the device's physical width will be `width` / `pixelRatio`), - /// which is used by some deprecated shapes (see - /// [SceneBuilder.pushPhysicalShape]). This argument defaults to 1.0. - Future toImage(int width, int height, [double pixelRatio = 1.0]) { + Future toImage(int width, int height) { if (width <= 0 || height <= 0) { throw Exception('Invalid image dimensions.'); } - return _futurize((_Callback callback) => _toImage(width, height, pixelRatio, (_Image? image) { + return _futurize((_Callback callback) => _toImage(width, height, (_Image? image) { if (image == null) { callback(null); } else { @@ -74,8 +58,8 @@ class Scene extends NativeFieldWrapperClass1 { ); } - @Native, Uint32, Uint32, Double, Handle)>(symbol: 'Scene::toImage') - external String? _toImage(int width, int height, double pixelRatio, _Callback<_Image?> callback); + @Native, Uint32, Uint32, Handle)>(symbol: 'Scene::toImage') + external String? _toImage(int width, int height, _Callback<_Image?> callback); /// Releases the resources used by this scene. /// diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index ca56375627139..cdffbd1211c5f 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -40,6 +40,7 @@ Scene::Scene(std::shared_ptr rootLayer, uint32_t rasterizerTracingThreshold, bool checkerboardRasterCacheImages, bool checkerboardOffscreenLayers) { + initializeDefaultViewPixelRatioIfNecessary(); layer_tree_config_.root_layer = std::move(rootLayer); layer_tree_config_.rasterizer_tracing_threshold = rasterizerTracingThreshold; layer_tree_config_.checkerboard_raster_cache_images = @@ -61,7 +62,6 @@ void Scene::dispose() { Dart_Handle Scene::toImageSync(uint32_t width, uint32_t height, - double pixel_ratio, Dart_Handle raw_image_handle) { TRACE_EVENT0("flutter", "Scene::toImageSync"); @@ -69,14 +69,13 @@ Dart_Handle Scene::toImageSync(uint32_t width, return tonic::ToDart("Scene has been disposed."); } - Scene::RasterizeToImage(width, height, static_cast(pixel_ratio), + Scene::RasterizeToImage(width, height, defaultViewPixelRatio(), raw_image_handle); return Dart_Null(); } Dart_Handle Scene::toImage(uint32_t width, uint32_t height, - double pixel_ratio, Dart_Handle raw_image_callback) { TRACE_EVENT0("flutter", "Scene::toImage"); @@ -85,7 +84,7 @@ Dart_Handle Scene::toImage(uint32_t width, } return Picture::RasterizeLayerTreeToImage( - BuildLayerTree(width, height, static_cast(pixel_ratio)), + BuildLayerTree(width, height, defaultViewPixelRatio()), raw_image_callback); } @@ -149,4 +148,25 @@ std::unique_ptr Scene::BuildLayerTree(uint32_t width, SkISize::Make(width, height), pixel_ratio); } +float Scene::default_view_pixel_ratio_ = -1.0f; + +void Scene::initializeDefaultViewPixelRatioIfNecessary() { + if (default_view_pixel_ratio_ > 0) { + return; + } + auto window = UIDartState::Current()->platform_configuration()->get_window(0); + if (window != nullptr) { + default_view_pixel_ratio_ = + static_cast(window->viewport_metrics().device_pixel_ratio); + } +} + +float Scene::defaultViewPixelRatio() { + if (default_view_pixel_ratio_ > 0) { + return default_view_pixel_ratio_; + } + // A default value. + return 2.0f; +} + } // namespace flutter diff --git a/lib/ui/compositing/scene.h b/lib/ui/compositing/scene.h index c8b9883de7e40..5ede01fec0438 100644 --- a/lib/ui/compositing/scene.h +++ b/lib/ui/compositing/scene.h @@ -32,12 +32,10 @@ class Scene : public RefCountedDartWrappable { Dart_Handle toImageSync(uint32_t width, uint32_t height, - double pixel_ratio, Dart_Handle raw_image_handle); Dart_Handle toImage(uint32_t width, uint32_t height, - double pixel_ratio, Dart_Handle raw_image_handle); void dispose(); @@ -61,6 +59,19 @@ class Scene : public RefCountedDartWrappable { float pixel_ratio); flutter::LayerTree::Config layer_tree_config_; + + static void initializeDefaultViewPixelRatioIfNecessary(); + static float defaultViewPixelRatio(); + + // The pixel ratio of window #0. + // + // Pixel ratio is used to render physical shapes (`PhysicalShapeLayer`), + // which have been deprecated. This field is used to keep backward + // compatibility, where physical shapes are drawn with the pixel ratio + // of window #0. Once `PhysicalShapeLayer` is removed as well as + // `{Preroll,Paint}Context.frame_device_pixel_ratio`, we should remove + // this field. + static float default_view_pixel_ratio_; }; } // namespace flutter diff --git a/lib/ui/dart_ui.cc b/lib/ui/dart_ui.cc index 7c6768497c666..57242c11ac9aa 100644 --- a/lib/ui/dart_ui.cc +++ b/lib/ui/dart_ui.cc @@ -293,8 +293,8 @@ typedef CanvasPath Path; V(SceneBuilder, setCheckerboardRasterCacheImages, 2) \ V(SceneBuilder, setRasterizerTracingThreshold, 2) \ V(Scene, dispose, 1) \ - V(Scene, toImage, 5) \ - V(Scene, toImageSync, 5) \ + V(Scene, toImage, 4) \ + V(Scene, toImageSync, 4) \ V(SemanticsUpdateBuilder, build, 2) \ V(SemanticsUpdateBuilder, updateCustomAction, 5) \ V(SemanticsUpdateBuilder, updateNode, 36) \ From 332f86aeee4ec7993a954c8a9ae7d6d174f5ea44 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 28 Apr 2023 13:00:11 -0700 Subject: [PATCH 092/147] An easier way --- lib/ui/compositing/scene.cc | 22 +++++----------------- lib/ui/compositing/scene.h | 11 ----------- 2 files changed, 5 insertions(+), 28 deletions(-) diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index cdffbd1211c5f..1e59056be56c2 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -23,6 +23,8 @@ namespace flutter { +static constexpr float kFallbackPixelRatio = 2.0f; + IMPLEMENT_WRAPPERTYPEINFO(ui, Scene); void Scene::create(Dart_Handle scene_handle, @@ -40,7 +42,6 @@ Scene::Scene(std::shared_ptr rootLayer, uint32_t rasterizerTracingThreshold, bool checkerboardRasterCacheImages, bool checkerboardOffscreenLayers) { - initializeDefaultViewPixelRatioIfNecessary(); layer_tree_config_.root_layer = std::move(rootLayer); layer_tree_config_.rasterizer_tracing_threshold = rasterizerTracingThreshold; layer_tree_config_.checkerboard_raster_cache_images = @@ -148,25 +149,12 @@ std::unique_ptr Scene::BuildLayerTree(uint32_t width, SkISize::Make(width, height), pixel_ratio); } -float Scene::default_view_pixel_ratio_ = -1.0f; - -void Scene::initializeDefaultViewPixelRatioIfNecessary() { - if (default_view_pixel_ratio_ > 0) { - return; - } +float Scene::defaultViewPixelRatio() { auto window = UIDartState::Current()->platform_configuration()->get_window(0); if (window != nullptr) { - default_view_pixel_ratio_ = - static_cast(window->viewport_metrics().device_pixel_ratio); - } -} - -float Scene::defaultViewPixelRatio() { - if (default_view_pixel_ratio_ > 0) { - return default_view_pixel_ratio_; + return static_cast(window->viewport_metrics().device_pixel_ratio); } - // A default value. - return 2.0f; + return kFallbackPixelRatio; } } // namespace flutter diff --git a/lib/ui/compositing/scene.h b/lib/ui/compositing/scene.h index 5ede01fec0438..ac4967b79bd96 100644 --- a/lib/ui/compositing/scene.h +++ b/lib/ui/compositing/scene.h @@ -60,18 +60,7 @@ class Scene : public RefCountedDartWrappable { flutter::LayerTree::Config layer_tree_config_; - static void initializeDefaultViewPixelRatioIfNecessary(); static float defaultViewPixelRatio(); - - // The pixel ratio of window #0. - // - // Pixel ratio is used to render physical shapes (`PhysicalShapeLayer`), - // which have been deprecated. This field is used to keep backward - // compatibility, where physical shapes are drawn with the pixel ratio - // of window #0. Once `PhysicalShapeLayer` is removed as well as - // `{Preroll,Paint}Context.frame_device_pixel_ratio`, we should remove - // this field. - static float default_view_pixel_ratio_; }; } // namespace flutter From c829f37c97e13c38d5f7ba9d2fab81cc59b839a3 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 28 Apr 2023 15:29:44 -0700 Subject: [PATCH 093/147] Add TODO and comment --- lib/ui/compositing/scene.h | 15 +++++++++++++++ runtime/runtime_controller.cc | 4 +++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/ui/compositing/scene.h b/lib/ui/compositing/scene.h index ac4967b79bd96..ed529b27a705a 100644 --- a/lib/ui/compositing/scene.h +++ b/lib/ui/compositing/scene.h @@ -60,6 +60,21 @@ class Scene : public RefCountedDartWrappable { flutter::LayerTree::Config layer_tree_config_; + // Fetches the pixel ratio from view 0, or if the window doesn't exist, + // fallback to 2.0f. + // + // The pixel ratio is used in toImage() and toImageSync(), and its only effect + // is to calculate the device's physical dimension, which is used by some + // physical shapes (see PhysicalShapeLayer). + // + // Physical shapes have been deprecated and should be removed soon. This + // method aims to keep the legacy behavior in single-window Flutter, which + // feeds the toImage and toImageSync with the pixel ratio of the only window. + // + // TODO(dkwingsmt): If PhysicalShapeLayer has been removed as well as + // {Preroll,Paint}Context.frame_device_pixel_ratio, remove this method and its + // related logic. + // https://github.com/flutter/flutter/issues/125720 static float defaultViewPixelRatio(); }; diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index b53832ecb4bb5..d85bdaa4326ed 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -20,6 +20,8 @@ namespace flutter { +const uint64_t kFlutterDefaultViewId = 0llu; + RuntimeController::RuntimeController(RuntimeDelegate& p_client, const TaskRunners& task_runners) : client_(p_client), vm_(nullptr), context_(task_runners) {} @@ -317,7 +319,7 @@ void RuntimeController::ScheduleFrame() { // |PlatformConfigurationClient| void RuntimeController::Render(Scene* scene) { // TODO(dkwingsmt): Currently only supports a single window. - int64_t view_id = 0ll; + int64_t view_id = kFlutterDefaultViewId; auto window = UIDartState::Current()->platform_configuration()->get_window(view_id); if (window == nullptr) { From 1c6d74d7d8b1b53871ce55b618a9bcb571b805d3 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 1 May 2023 13:12:28 -0700 Subject: [PATCH 094/147] Fix compile --- shell/gpu/gpu_studio_gl_impeller.cc | 2 +- shell/gpu/gpu_studio_vulkan_impeller.cc | 2 +- .../android/platform_view_android_jni_impl.cc | 5 +- .../fuchsia/flutter/flatland_platform_view.cc | 49 ++++++++++--------- .../fuchsia/flutter/gfx_platform_view.cc | 46 +++++++++-------- shell/platform/linux/fl_engine.cc | 6 ++- shell/platform/linux/fl_engine_test.cc | 21 ++++---- shell/platform/linux/testing/mock_engine.cc | 1 + .../windows/flutter_windows_engine.cc | 3 +- .../platform/windows/flutter_windows_engine.h | 3 +- .../platform/windows/flutter_windows_view.cc | 6 ++- .../windows/flutter_windows_view_unittests.cc | 2 +- .../platform/windows/testing/test_keyboard.cc | 2 +- 13 files changed, 85 insertions(+), 63 deletions(-) diff --git a/shell/gpu/gpu_studio_gl_impeller.cc b/shell/gpu/gpu_studio_gl_impeller.cc index cae6e498f1e99..1ce359d28f92f 100644 --- a/shell/gpu/gpu_studio_gl_impeller.cc +++ b/shell/gpu/gpu_studio_gl_impeller.cc @@ -5,7 +5,7 @@ #include "flutter/shell/gpu/gpu_studio_gl_impeller.h" #include "flutter/fml/make_copyable.h" -#include "flutter/impeller/display_list/display_list_dispatcher.h" +#include "flutter/impeller/display_list/dl_dispatcher.h" #include "flutter/impeller/renderer/backend/gles/surface_gles.h" #include "flutter/impeller/renderer/renderer.h" diff --git a/shell/gpu/gpu_studio_vulkan_impeller.cc b/shell/gpu/gpu_studio_vulkan_impeller.cc index bc94f8e4a8bf4..7dc5d69d15f7a 100644 --- a/shell/gpu/gpu_studio_vulkan_impeller.cc +++ b/shell/gpu/gpu_studio_vulkan_impeller.cc @@ -5,7 +5,7 @@ #include "flutter/shell/gpu/gpu_studio_vulkan_impeller.h" #include "flutter/fml/make_copyable.h" -#include "flutter/impeller/display_list/display_list_dispatcher.h" +#include "flutter/impeller/display_list/dl_dispatcher.h" #include "flutter/impeller/renderer/renderer.h" #include "impeller/renderer/backend/vulkan/context_vk.h" diff --git a/shell/platform/android/platform_view_android_jni_impl.cc b/shell/platform/android/platform_view_android_jni_impl.cc index 031ec1057eb22..69b3bf7697b25 100644 --- a/shell/platform/android/platform_view_android_jni_impl.cc +++ b/shell/platform/android/platform_view_android_jni_impl.cc @@ -37,6 +37,8 @@ namespace flutter { +static constexpr int64_t kFlutterDefaultViewId = 0ll; + static fml::jni::ScopedJavaGlobalRef* g_flutter_callback_info_class = nullptr; @@ -337,7 +339,8 @@ static void SetViewportMetrics(JNIEnv* env, displayFeaturesState, }; - ANDROID_SHELL_HOLDER->GetPlatformView()->SetViewportMetrics(metrics); + ANDROID_SHELL_HOLDER->GetPlatformView()->SetViewportMetrics( + kFlutterDefaultViewId, metrics); } static jobject GetBitmap(JNIEnv* env, jobject jcaller, jlong shell_holder) { diff --git a/shell/platform/fuchsia/flutter/flatland_platform_view.cc b/shell/platform/fuchsia/flutter/flatland_platform_view.cc index 2206e3cc642c2..ba213774af070 100644 --- a/shell/platform/fuchsia/flutter/flatland_platform_view.cc +++ b/shell/platform/fuchsia/flutter/flatland_platform_view.cc @@ -8,6 +8,8 @@ namespace flutter_runner { +static constexpr int64_t kFlutterDefaultViewId = 0ll; + FlatlandPlatformView::FlatlandPlatformView( flutter::PlatformView::Delegate& delegate, flutter::TaskRunners task_runners, @@ -85,29 +87,30 @@ void FlatlandPlatformView::OnGetLayout( float pixel_ratio = view_pixel_ratio_ ? *view_pixel_ratio_ : 1.0f; - SetViewportMetrics({ - pixel_ratio, // device_pixel_ratio - std::round(view_logical_size_.value()[0] * - pixel_ratio), // physical_width - std::round(view_logical_size_.value()[1] * - pixel_ratio), // physical_height - 0.0f, // physical_padding_top - 0.0f, // physical_padding_right - 0.0f, // physical_padding_bottom - 0.0f, // physical_padding_left - 0.0f, // physical_view_inset_top - 0.0f, // physical_view_inset_right - 0.0f, // physical_view_inset_bottom - 0.0f, // physical_view_inset_left - 0.0f, // p_physical_system_gesture_inset_top - 0.0f, // p_physical_system_gesture_inset_right - 0.0f, // p_physical_system_gesture_inset_bottom - 0.0f, // p_physical_system_gesture_inset_left, - -1.0, // p_physical_touch_slop, - {}, // p_physical_display_features_bounds - {}, // p_physical_display_features_type - {}, // p_physical_display_features_state - }); + SetViewportMetrics(kFlutterDefaultViewId, + { + pixel_ratio, // device_pixel_ratio + std::round(view_logical_size_.value()[0] * + pixel_ratio), // physical_width + std::round(view_logical_size_.value()[1] * + pixel_ratio), // physical_height + 0.0f, // physical_padding_top + 0.0f, // physical_padding_right + 0.0f, // physical_padding_bottom + 0.0f, // physical_padding_left + 0.0f, // physical_view_inset_top + 0.0f, // physical_view_inset_right + 0.0f, // physical_view_inset_bottom + 0.0f, // physical_view_inset_left + 0.0f, // p_physical_system_gesture_inset_top + 0.0f, // p_physical_system_gesture_inset_right + 0.0f, // p_physical_system_gesture_inset_bottom + 0.0f, // p_physical_system_gesture_inset_left, + -1.0, // p_physical_touch_slop, + {}, // p_physical_display_features_bounds + {}, // p_physical_display_features_type + {}, // p_physical_display_features_state + }); parent_viewport_watcher_->GetLayout( fit::bind_member(this, &FlatlandPlatformView::OnGetLayout)); diff --git a/shell/platform/fuchsia/flutter/gfx_platform_view.cc b/shell/platform/fuchsia/flutter/gfx_platform_view.cc index 1dcb2c2e8c534..12cecee4f85d7 100644 --- a/shell/platform/fuchsia/flutter/gfx_platform_view.cc +++ b/shell/platform/fuchsia/flutter/gfx_platform_view.cc @@ -8,6 +8,8 @@ namespace flutter_runner { +static constexpr int64_t kFlutterDefaultViewId = 0ll; + GfxPlatformView::GfxPlatformView( flutter::PlatformView::Delegate& delegate, flutter::TaskRunners task_runners, @@ -213,27 +215,29 @@ void GfxPlatformView::OnScenicEvent( metrics_changed) { const float pixel_ratio = *view_pixel_ratio_; const std::array logical_size = *view_logical_size_; - SetViewportMetrics({ - pixel_ratio, // device_pixel_ratio - std::round(logical_size[0] * pixel_ratio), // physical_width - std::round(logical_size[1] * pixel_ratio), // physical_height - 0.0f, // physical_padding_top - 0.0f, // physical_padding_right - 0.0f, // physical_padding_bottom - 0.0f, // physical_padding_left - 0.0f, // physical_view_inset_top - 0.0f, // physical_view_inset_right - 0.0f, // physical_view_inset_bottom - 0.0f, // physical_view_inset_left - 0.0f, // p_physical_system_gesture_inset_top - 0.0f, // p_physical_system_gesture_inset_right - 0.0f, // p_physical_system_gesture_inset_bottom - 0.0f, // p_physical_system_gesture_inset_left, - -1.0, // p_physical_touch_slop, - {}, // p_physical_display_features_bounds - {}, // p_physical_display_features_type - {}, // p_physical_display_features_state - }); + SetViewportMetrics( + kFlutterDefaultViewId, + { + pixel_ratio, // device_pixel_ratio + std::round(logical_size[0] * pixel_ratio), // physical_width + std::round(logical_size[1] * pixel_ratio), // physical_height + 0.0f, // physical_padding_top + 0.0f, // physical_padding_right + 0.0f, // physical_padding_bottom + 0.0f, // physical_padding_left + 0.0f, // physical_view_inset_top + 0.0f, // physical_view_inset_right + 0.0f, // physical_view_inset_bottom + 0.0f, // physical_view_inset_left + 0.0f, // p_physical_system_gesture_inset_top + 0.0f, // p_physical_system_gesture_inset_right + 0.0f, // p_physical_system_gesture_inset_bottom + 0.0f, // p_physical_system_gesture_inset_left, + -1.0, // p_physical_touch_slop, + {}, // p_physical_display_features_bounds + {}, // p_physical_display_features_type + {}, // p_physical_display_features_state + }); } } diff --git a/shell/platform/linux/fl_engine.cc b/shell/platform/linux/fl_engine.cc index b8a6a1942dcc7..4d339ab9a0e2d 100644 --- a/shell/platform/linux/fl_engine.cc +++ b/shell/platform/linux/fl_engine.cc @@ -24,6 +24,8 @@ #include "flutter/shell/platform/linux/fl_texture_registrar_private.h" #include "flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registry.h" +static constexpr int64_t kFlutterDefaultViewId = 0ll; + // Unique number associated with platform tasks. static constexpr size_t kPlatformTaskRunnerIdentifier = 1; @@ -732,12 +734,14 @@ void fl_engine_send_window_metrics_event(FlEngine* self, return; } + // TODO(dkwingsmt) + int64_t view_id = kFlutterDefaultViewId; FlutterWindowMetricsEvent event = {}; event.struct_size = sizeof(FlutterWindowMetricsEvent); event.width = width; event.height = height; event.pixel_ratio = pixel_ratio; - self->embedder_api.SendWindowMetricsEvent(self->engine, &event); + self->embedder_api.SendWindowMetricsEvent(self->engine, view_id, &event); } void fl_engine_send_mouse_pointer_event(FlEngine* self, diff --git a/shell/platform/linux/fl_engine_test.cc b/shell/platform/linux/fl_engine_test.cc index a1d6e1d650eaf..d4e0a79466bee 100644 --- a/shell/platform/linux/fl_engine_test.cc +++ b/shell/platform/linux/fl_engine_test.cc @@ -20,16 +20,17 @@ TEST(FlEngineTest, WindowMetrics) { FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine); bool called = false; - embedder_api->SendWindowMetricsEvent = MOCK_ENGINE_PROC( - SendWindowMetricsEvent, - ([&called](auto engine, const FlutterWindowMetricsEvent* event) { - called = true; - EXPECT_EQ(event->width, static_cast(3840)); - EXPECT_EQ(event->height, static_cast(2160)); - EXPECT_EQ(event->pixel_ratio, 2.0); - - return kSuccess; - })); + embedder_api->SendWindowMetricsEvent = + MOCK_ENGINE_PROC(SendWindowMetricsEvent, + ([&called](auto engine, int64_t view_id, + const FlutterWindowMetricsEvent* event) { + called = true; + EXPECT_EQ(event->width, static_cast(3840)); + EXPECT_EQ(event->height, static_cast(2160)); + EXPECT_EQ(event->pixel_ratio, 2.0); + + return kSuccess; + })); g_autoptr(GError) error = nullptr; EXPECT_TRUE(fl_engine_start(engine, &error)); diff --git a/shell/platform/linux/testing/mock_engine.cc b/shell/platform/linux/testing/mock_engine.cc index eac0cb3809cc9..d704aaaf0608a 100644 --- a/shell/platform/linux/testing/mock_engine.cc +++ b/shell/platform/linux/testing/mock_engine.cc @@ -221,6 +221,7 @@ FlutterEngineResult FlutterEngineDeinitialize(FLUTTER_API_SYMBOL(FlutterEngine) FlutterEngineResult FlutterEngineSendWindowMetricsEvent( FLUTTER_API_SYMBOL(FlutterEngine) engine, + int64_t view_id, const FlutterWindowMetricsEvent* event) { EXPECT_TRUE(engine->running); return kSuccess; diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index 39dbe8ea6be05..95b8f8ebe25e8 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -462,9 +462,10 @@ void FlutterWindowsEngine::AddPluginRegistrarDestructionCallback( } void FlutterWindowsEngine::SendWindowMetricsEvent( + int64_t view_id, const FlutterWindowMetricsEvent& event) { if (engine_) { - embedder_api_.SendWindowMetricsEvent(engine_, &event); + embedder_api_.SendWindowMetricsEvent(engine_, view_id, &event); } } diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index 797cc2142da3c..700942bb85bb5 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -153,7 +153,8 @@ class FlutterWindowsEngine { } // Informs the engine that the window metrics have changed. - void SendWindowMetricsEvent(const FlutterWindowMetricsEvent& event); + void SendWindowMetricsEvent(int64_t view_id, + const FlutterWindowMetricsEvent& event); // Informs the engine of an incoming pointer event. void SendPointerEvent(const FlutterPointerEvent& event); diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index 4107546669a1e..741b4ce04df69 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -15,6 +15,8 @@ namespace flutter { namespace { +static constexpr int64_t kFlutterDefaultViewId = 0ll; + // The maximum duration to block the platform thread for while waiting // for a window resize operation to complete. constexpr std::chrono::milliseconds kWindowResizeTimeout{100}; @@ -268,12 +270,14 @@ void FlutterWindowsView::OnResetImeComposing() { void FlutterWindowsView::SendWindowMetrics(size_t width, size_t height, double dpiScale) const { + // TODO(dkwingsmt) + int64_t view_id = kFlutterDefaultViewId; FlutterWindowMetricsEvent event = {}; event.struct_size = sizeof(event); event.width = width; event.height = height; event.pixel_ratio = dpiScale; - engine_->SendWindowMetricsEvent(event); + engine_->SendWindowMetricsEvent(view_id, event); } void FlutterWindowsView::SendInitialBounds() { diff --git a/shell/platform/windows/flutter_windows_view_unittests.cc b/shell/platform/windows/flutter_windows_view_unittests.cc index 89082dd46cba1..ae24ae3e5c7a0 100644 --- a/shell/platform/windows/flutter_windows_view_unittests.cc +++ b/shell/platform/windows/flutter_windows_view_unittests.cc @@ -739,7 +739,7 @@ TEST(FlutterWindowsViewTest, WindowResizeTests) { fml::AutoResetWaitableEvent metrics_sent_latch; modifier.embedder_api().SendWindowMetricsEvent = MOCK_ENGINE_PROC( SendWindowMetricsEvent, - ([&metrics_sent_latch](auto engine, + ([&metrics_sent_latch](auto engine, int64_t view_id, const FlutterWindowMetricsEvent* event) { metrics_sent_latch.Signal(); return kSuccess; diff --git a/shell/platform/windows/testing/test_keyboard.cc b/shell/platform/windows/testing/test_keyboard.cc index 4d2109a657108..e9140088c38c7 100644 --- a/shell/platform/windows/testing/test_keyboard.cc +++ b/shell/platform/windows/testing/test_keyboard.cc @@ -192,7 +192,7 @@ void MockEmbedderApiForKeyboard( return kSuccess; }; modifier.embedder_api().SendWindowMetricsEvent = - [](auto engine, const FlutterWindowMetricsEvent* event) { + [](auto engine, int64_t view_id, const FlutterWindowMetricsEvent* event) { return kSuccess; }; modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; }; From aa02906702e426669072be94ad7604c7de459f3b Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 1 May 2023 14:00:02 -0700 Subject: [PATCH 095/147] Fix more --- .../ios/framework/Source/FlutterEngine.mm | 4 +- .../darwin/ios/ios_external_view_embedder.mm | 3 +- .../darwin/ios/ios_surface_metal_impeller.mm | 2 +- .../embedder/tests/embedder_gl_unittests.cc | 273 +++++++++++------- shell/platform/glfw/flutter_glfw.cc | 4 +- 5 files changed, 174 insertions(+), 112 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 10b87e099a50b..b7838f5bf1357 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -38,6 +38,8 @@ #import "flutter/shell/platform/darwin/ios/rendering_api_selection.h" #include "flutter/shell/profiling/sampling_profiler.h" +static constexpr int64_t kFlutterDefaultViewId = 0ll; + /// Inheriting ThreadConfigurer and use iOS platform thread API to configure the thread priorities /// Using iOS platform thread API to configure thread priority static void IOSPlatformThreadConfigSetter(const fml::Thread::ThreadConfig& config) { @@ -308,7 +310,7 @@ - (void)updateViewportMetrics:(flutter::ViewportMetrics)viewportMetrics { if (!self.platformView) { return; } - self.platformView->SetViewportMetrics(viewportMetrics); + self.platformView->SetViewportMetrics(kFlutterDefaultViewId, viewportMetrics); } - (void)dispatchPointerDataPacket:(std::unique_ptr)packet { diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm index d93d8cb1ee76d..e0351b8ae74b0 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.mm +++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm @@ -69,7 +69,8 @@ // |ExternalViewEmbedder| void IOSExternalViewEmbedder::SubmitFrame(GrDirectContext* context, - std::unique_ptr frame) { + std::unique_ptr frame, + int64_t window_view_id) { TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::SubmitFrame"); FML_CHECK(platform_views_controller_); // TODO(dkwingsmt) diff --git a/shell/platform/darwin/ios/ios_surface_metal_impeller.mm b/shell/platform/darwin/ios/ios_surface_metal_impeller.mm index 35e0701f6ce1f..5f56d54f54af0 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_impeller.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_impeller.mm @@ -39,7 +39,7 @@ // |IOSSurface| std::unique_ptr IOSSurfaceMetalImpeller::CreateGPUStudio(GrDirectContext*) { impeller_context_->UpdateOffscreenLayerPixelFormat( - InferOffscreenLayerPixelFormat(impeller::FromMTLPixelFormat(layer_.get().pixelFormat))); + impeller::FromMTLPixelFormat(layer_.get().pixelFormat)); return std::make_unique(this, // impeller_context_ // ); diff --git a/shell/platform/embedder/tests/embedder_gl_unittests.cc b/shell/platform/embedder/tests/embedder_gl_unittests.cc index 65d698d01448b..aa90c76430d11 100644 --- a/shell/platform/embedder/tests/embedder_gl_unittests.cc +++ b/shell/platform/embedder/tests/embedder_gl_unittests.cc @@ -43,6 +43,8 @@ namespace flutter { namespace testing { +static constexpr int64_t kDefaultViewId = 0ll; + using EmbedderTest = testing::EmbedderTest; TEST_F(EmbedderTest, CanGetVulkanEmbedderContext) { @@ -161,8 +163,9 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToOpenGLFramebuffer) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -254,8 +257,9 @@ TEST_F(EmbedderTest, RasterCacheDisabledWithPlatformViews) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); setup.Wait(); @@ -326,8 +330,9 @@ TEST_F(EmbedderTest, RasterCacheEnabled) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); setup.Wait(); @@ -427,8 +432,9 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToOpenGLTexture) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -519,8 +525,9 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderToSoftwareBuffer) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -684,8 +691,9 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderKnownScene) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -804,7 +812,8 @@ TEST_F(EmbedderTest, CustomCompositorMustWorkWithCustomTaskRunner) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, + &event), kSuccess); ASSERT_TRUE(engine.is_valid()); sync_latch.Signal(); @@ -878,8 +887,9 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderWithRootLayerOnly) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -988,8 +998,9 @@ TEST_F(EmbedderTest, CompositorMustBeAbleToRenderWithPlatformLayerOnBottom) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -1169,8 +1180,9 @@ TEST_F(EmbedderTest, event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -1198,8 +1210,9 @@ TEST_F(EmbedderTest, CanRenderSceneWithoutCustomCompositor) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(ImageMatchesFixture("scene_without_custom_compositor.png", rendered_scene)); @@ -1231,8 +1244,9 @@ TEST_F(EmbedderTest, CanRenderSceneWithoutCustomCompositorWithTransformation) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(ImageMatchesFixture( "scene_without_custom_compositor_with_xform.png", rendered_scene)); @@ -1258,8 +1272,9 @@ TEST_P(EmbedderTestMultiBackend, CanRenderGradientWithoutCompositor) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(ImageMatchesFixture( FixtureNameForBackend(backend, "gradient.png"), rendered_scene)); @@ -1292,8 +1307,9 @@ TEST_F(EmbedderTest, CanRenderGradientWithoutCompositorWithXform) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(ImageMatchesFixture("gradient_xform.png", rendered_scene)); } @@ -1320,8 +1336,9 @@ TEST_P(EmbedderTestMultiBackend, CanRenderGradientWithCompositor) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(ImageMatchesFixture( FixtureNameForBackend(backend, "gradient.png"), rendered_scene)); @@ -1358,8 +1375,9 @@ TEST_F(EmbedderTest, CanRenderGradientWithCompositorWithXform) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(ImageMatchesFixture("gradient_xform.png", rendered_scene)); } @@ -1462,8 +1480,9 @@ TEST_P(EmbedderTestMultiBackend, event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(ImageMatchesFixture( FixtureNameForBackend(backend, "gradient.png"), rendered_scene)); @@ -1577,8 +1596,9 @@ TEST_F(EmbedderTest, CanRenderGradientWithCompositorOnNonRootLayerWithXform) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(ImageMatchesFixture("gradient_xform.png", rendered_scene)); } @@ -1680,8 +1700,9 @@ TEST_F(EmbedderTest, VerifyB141980393) { event.width = flutter_application_rect.width(); event.height = flutter_application_rect.height(); event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -1725,8 +1746,9 @@ TEST_F(EmbedderTest, CanCreateEmbedderWithCustomRenderTaskRunner) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); task_latch.Wait(); ASSERT_TRUE(task_executed); ASSERT_EQ(FlutterEngineDeinitialize(engine.get()), kSuccess); @@ -1786,7 +1808,8 @@ TEST_P(EmbedderTestMultiBackend, event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, + &event), kSuccess); }); @@ -1895,8 +1918,9 @@ TEST_P(EmbedderTestMultiBackend, event.width = 400 * 2.0; event.height = 300 * 2.0; event.pixel_ratio = 2.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -1991,8 +2015,9 @@ TEST_F( event.width = 400 * 2.0; event.height = 300 * 2.0; event.pixel_ratio = 2.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -2025,8 +2050,9 @@ TEST_F(EmbedderTest, event.width = 1024; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); constexpr size_t frames_expected = 10; @@ -2065,8 +2091,9 @@ TEST_F(EmbedderTest, event.width = 1024; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); constexpr size_t frames_expected = 10; @@ -2172,8 +2199,9 @@ TEST_P(EmbedderTestMultiBackend, PlatformViewMutatorsAreValid) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -2268,8 +2296,9 @@ TEST_F(EmbedderTest, PlatformViewMutatorsAreValidWithPixelRatio) { event.width = 800; event.height = 600; event.pixel_ratio = 2.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -2371,8 +2400,9 @@ TEST_F(EmbedderTest, event.width = 800; event.height = 600; event.pixel_ratio = 2.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -2399,8 +2429,9 @@ TEST_F(EmbedderTest, EmptySceneIsAcceptable) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); latch.Wait(); } @@ -2427,8 +2458,9 @@ TEST_F(EmbedderTest, SceneWithNoRootContainerIsAcceptable) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); latch.Wait(); } @@ -2462,8 +2494,9 @@ TEST_F(EmbedderTest, ArcEndCapsAreDrawnCorrectly) { event.width = 800; event.height = 1024; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(ImageMatchesFixture("arc_end_caps.png", scene_image)); } @@ -2541,8 +2574,9 @@ TEST_F(EmbedderTest, ClipsAreCorrectlyCalculated) { event.width = 400; event.height = 300; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); latch.Wait(); } @@ -2625,8 +2659,9 @@ TEST_F(EmbedderTest, ComplexClipsAreCorrectlyCalculated) { event.width = 1024; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); latch.Wait(); } @@ -2855,8 +2890,9 @@ TEST_F(EmbedderTest, CompositorCanPostZeroLayersForPresentation) { event.width = 300; event.height = 200; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -2918,8 +2954,9 @@ TEST_F(EmbedderTest, CompositorCanPostOnlyPlatformViews) { event.width = 300; event.height = 200; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); @@ -2957,8 +2994,9 @@ TEST_F(EmbedderTest, CompositorRenderTargetsAreRecycled) { event.width = 300; event.height = 200; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); latch.Wait(); ASSERT_EQ(context.GetCompositor().GetPendingBackingStoresCount(), 10u); @@ -3034,8 +3072,9 @@ TEST_F(EmbedderTest, CompositorRenderTargetsAreInStableOrder) { event.width = 300; event.height = 200; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); latch.Wait(); } @@ -3060,8 +3099,9 @@ TEST_F(EmbedderTest, FrameInfoContainsValidWidthAndHeight) { event.width = 1024; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); static fml::CountDownLatch frame_latch(10); @@ -3191,8 +3231,9 @@ TEST_F(EmbedderTest, PresentInfoContainsValidFBOId) { event.width = 1024; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); static fml::CountDownLatch frame_latch(10); @@ -3265,8 +3306,9 @@ TEST_F(EmbedderTest, event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); // Because it's the same as the first frame, the second frame damage should // be empty but, because there was a full existing buffer damage, the buffer @@ -3287,8 +3329,9 @@ TEST_F(EmbedderTest, ASSERT_EQ(present_info.buffer_damage.damage->bottom, 600); }); - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); } TEST_F(EmbedderTest, PresentInfoReceivesEmptyDamage) { @@ -3341,8 +3384,9 @@ TEST_F(EmbedderTest, PresentInfoReceivesEmptyDamage) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); // Because it's the same as the first frame, the second frame should not be // rerendered assuming there is no existing damage. @@ -3362,8 +3406,9 @@ TEST_F(EmbedderTest, PresentInfoReceivesEmptyDamage) { ASSERT_EQ(present_info.buffer_damage.damage->bottom, 0); }); - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); } TEST_F(EmbedderTest, PresentInfoReceivesPartialDamage) { @@ -3416,8 +3461,9 @@ TEST_F(EmbedderTest, PresentInfoReceivesPartialDamage) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); // Because it's the same as the first frame, the second frame damage should be // empty but, because there was a partial existing damage, the buffer damage @@ -3438,8 +3484,9 @@ TEST_F(EmbedderTest, PresentInfoReceivesPartialDamage) { ASSERT_EQ(present_info.buffer_damage.damage->bottom, 300); }); - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); } TEST_F(EmbedderTest, PopulateExistingDamageReceivesValidID) { @@ -3473,8 +3520,9 @@ TEST_F(EmbedderTest, PopulateExistingDamageReceivesValidID) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); } TEST_F(EmbedderTest, PopulateExistingDamageReceivesInvalidID) { @@ -3519,8 +3567,9 @@ TEST_F(EmbedderTest, PopulateExistingDamageReceivesInvalidID) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); } TEST_F(EmbedderTest, SetSingleDisplayConfigurationWithDisplayId) { @@ -3559,8 +3608,9 @@ TEST_F(EmbedderTest, SetSingleDisplayConfigurationWithDisplayId) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); latch.Wait(); } @@ -3601,8 +3651,9 @@ TEST_F(EmbedderTest, SetSingleDisplayConfigurationWithoutDisplayId) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); latch.Wait(); } @@ -3650,8 +3701,9 @@ TEST_F(EmbedderTest, SetValidMultiDisplayConfiguration) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); latch.Wait(); } @@ -3696,8 +3748,9 @@ TEST_F(EmbedderTest, MultipleDisplaysWithSingleDisplayTrueIsInvalid) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); latch.Wait(); } @@ -3742,8 +3795,9 @@ TEST_F(EmbedderTest, MultipleDisplaysWithSameDisplayIdIsInvalid) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); latch.Wait(); } @@ -3783,8 +3837,9 @@ TEST_F(EmbedderTest, CompositorRenderTargetsNotRecycledWhenAvoidsCacheSet) { event.width = 300; event.height = 200; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); latch.Wait(); @@ -3945,8 +4000,9 @@ TEST_F(EmbedderTest, CreateInvalidBackingstoreOpenGLTexture) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); } @@ -4008,8 +4064,9 @@ TEST_F(EmbedderTest, CreateInvalidBackingstoreOpenGLFramebuffer) { event.width = 800; event.height = 600; event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); + ASSERT_EQ( + FlutterEngineSendWindowMetricsEvent(engine.get(), kDefaultViewId, &event), + kSuccess); ASSERT_TRUE(engine.is_valid()); latch.Wait(); } diff --git a/shell/platform/glfw/flutter_glfw.cc b/shell/platform/glfw/flutter_glfw.cc index d50871bd6f14c..de7dcb61fe11f 100644 --- a/shell/platform/glfw/flutter_glfw.cc +++ b/shell/platform/glfw/flutter_glfw.cc @@ -41,6 +41,7 @@ static_assert(FLUTTER_ENGINE_VERSION == 1, ""); const int kFlutterDesktopDontCare = GLFW_DONT_CARE; static constexpr double kDpPerInch = 160.0; +static constexpr int64_t kFlutterDefaultViewId = 0ll; // Struct for storing state within an instance of the GLFW Window. struct FlutterDesktopWindowControllerState { @@ -281,6 +282,7 @@ static void SendWindowMetrics(FlutterDesktopWindowControllerState* controller, double dpi = controller->window_wrapper->pixels_per_screen_coordinate * controller->monitor_screen_coordinates_per_inch; + int64_t view_id = kFlutterDefaultViewId; FlutterWindowMetricsEvent event = {}; event.struct_size = sizeof(event); event.width = width; @@ -294,7 +296,7 @@ static void SendWindowMetrics(FlutterDesktopWindowControllerState* controller, event.pixel_ratio = controller->window_wrapper->pixel_ratio_override; } FlutterEngineSendWindowMetricsEvent(controller->engine->flutter_engine, - &event); + view_id, &event); } // Populates |task_runner| with a description that uses |engine_state|'s event From 2f000544f5e02528a12abb61090b094e3eef36b7 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 1 May 2023 23:00:22 -0700 Subject: [PATCH 096/147] Fix some compile --- lib/ui/compositing.dart | 8 ++++---- lib/ui/dart_ui.cc | 2 +- shell/platform/fuchsia/flutter/engine.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/ui/compositing.dart b/lib/ui/compositing.dart index cfb4e1908064b..f20ecff7349c6 100644 --- a/lib/ui/compositing.dart +++ b/lib/ui/compositing.dart @@ -231,12 +231,12 @@ class PhysicalShapeEngineLayer extends _EngineLayerWrapper { class SceneBuilder extends NativeFieldWrapperClass1 { /// Creates an empty [SceneBuilder] object. @pragma('vm:entry-point') - SceneBuilder([Object viewId = 0]) { - _constructor(viewId as int); + SceneBuilder() { + _constructor(); } - @Native(symbol: 'SceneBuilder::Create') - external void _constructor(int viewId); + @Native(symbol: 'SceneBuilder::Create') + external void _constructor(); // Layers used in this scene. // diff --git a/lib/ui/dart_ui.cc b/lib/ui/dart_ui.cc index f9e4c29a378ec..e26aeb307fd55 100644 --- a/lib/ui/dart_ui.cc +++ b/lib/ui/dart_ui.cc @@ -81,7 +81,7 @@ typedef CanvasPath Path; V(PathMeasure::Create, 3) \ V(Path::Create, 1) \ V(PictureRecorder::Create, 1) \ - V(SceneBuilder::Create, 2) \ + V(SceneBuilder::Create, 1) \ V(SemanticsUpdateBuilder::Create, 1) \ /* Other */ \ V(FontCollection::LoadFontFromList, 3) \ diff --git a/shell/platform/fuchsia/flutter/engine.h b/shell/platform/fuchsia/flutter/engine.h index f17c48832f6f2..dd5b6049c4311 100644 --- a/shell/platform/fuchsia/flutter/engine.h +++ b/shell/platform/fuchsia/flutter/engine.h @@ -149,7 +149,7 @@ class Engine final : public fuchsia::memorypressure::Watcher { std::unique_ptr CreateStudio(); - std::unique_ptr CreateSurface(int64_t view_id); + std::unique_ptr CreateSurface(); Delegate& delegate_; From 8bd5109e054b025818c9033d0b56493787ff31ef Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 1 May 2023 23:38:49 -0700 Subject: [PATCH 097/147] More compile errors --- .../ios/framework/Source/FlutterEnginePlatformViewTest.mm | 2 +- .../darwin/ios/framework/Source/FlutterPlatformViewsTest.mm | 2 +- .../darwin/ios/framework/Source/accessibility_bridge_test.mm | 2 +- shell/platform/fuchsia/flutter/platform_view_unittest.cc | 2 +- .../fuchsia/flutter/tests/flatland_platform_view_unittest.cc | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm index 61c3fdcb6ef52..beef56f3fa087 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm @@ -24,7 +24,7 @@ void OnPlatformViewCreated() override {} void OnPlatformViewDestroyed() override {} void OnPlatformViewScheduleFrame() override {} void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override {} - void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override {} + void OnPlatformViewSetViewportMetrics(int64_t view_id, const ViewportMetrics& metrics) override {} const flutter::Settings& OnPlatformViewGetSettings() const override { return settings_; } void OnPlatformViewDispatchPlatformMessage(std::unique_ptr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index d7a4c2a0876e6..f1a15c061881e 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -96,7 +96,7 @@ void OnPlatformViewCreated() override {} void OnPlatformViewDestroyed() override {} void OnPlatformViewScheduleFrame() override {} void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override {} - void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override {} + void OnPlatformViewSetViewportMetrics(int64_t view_id, const ViewportMetrics& metrics) override {} const flutter::Settings& OnPlatformViewGetSettings() const override { return settings_; } void OnPlatformViewDispatchPlatformMessage(std::unique_ptr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm index a0db444553f29..9ff01fb153fa1 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm @@ -77,7 +77,7 @@ void OnPlatformViewCreated() override {} void OnPlatformViewDestroyed() override {} void OnPlatformViewScheduleFrame() override {} void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override {} - void OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) override {} + void OnPlatformViewSetViewportMetrics(int64_t view_id, const ViewportMetrics& metrics) override {} const flutter::Settings& OnPlatformViewGetSettings() const override { return settings_; } void OnPlatformViewDispatchPlatformMessage(std::unique_ptr message) override {} void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr packet) override { diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index 6f30c64e79e3a..23eed34073197 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -87,7 +87,7 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) {} // |flutter::PlatformView::Delegate| void OnPlatformViewSetViewportMetrics( - const flutter::ViewportMetrics& metrics) { + int64_t view_id, const flutter::ViewportMetrics& metrics) { metrics_ = metrics; } // |flutter::PlatformView::Delegate| diff --git a/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc b/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc index 01326fc07a4ac..d10155ad40eeb 100644 --- a/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc @@ -86,7 +86,7 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) {} // |flutter::PlatformView::Delegate| void OnPlatformViewSetViewportMetrics( - const flutter::ViewportMetrics& metrics) { + int64_t view_id, const flutter::ViewportMetrics& metrics) { metrics_ = metrics; } // |flutter::PlatformView::Delegate| From af9c6a7a1d62acc6f95c2c1ac305bc9812d743cc Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 1 May 2023 23:39:24 -0700 Subject: [PATCH 098/147] Format --- shell/platform/fuchsia/flutter/platform_view_unittest.cc | 3 ++- .../fuchsia/flutter/tests/flatland_platform_view_unittest.cc | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index 23eed34073197..6f5b4d5fd444c 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -87,7 +87,8 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) {} // |flutter::PlatformView::Delegate| void OnPlatformViewSetViewportMetrics( - int64_t view_id, const flutter::ViewportMetrics& metrics) { + int64_t view_id, + const flutter::ViewportMetrics& metrics) { metrics_ = metrics; } // |flutter::PlatformView::Delegate| diff --git a/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc b/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc index d10155ad40eeb..0de1588d7b511 100644 --- a/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc @@ -86,7 +86,8 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) {} // |flutter::PlatformView::Delegate| void OnPlatformViewSetViewportMetrics( - int64_t view_id, const flutter::ViewportMetrics& metrics) { + int64_t view_id, + const flutter::ViewportMetrics& metrics) { metrics_ = metrics; } // |flutter::PlatformView::Delegate| From 0a4eb92bf27671ac9b105068354f56a2ac324513 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 2 May 2023 00:29:06 -0700 Subject: [PATCH 099/147] Fix crash --- shell/common/snapshot_controller_impeller.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/common/snapshot_controller_impeller.cc b/shell/common/snapshot_controller_impeller.cc index 77a8c2502ba48..ea68bd91188a4 100644 --- a/shell/common/snapshot_controller_impeller.cc +++ b/shell/common/snapshot_controller_impeller.cc @@ -37,7 +37,7 @@ sk_sp SnapshotControllerImpeller::DoMakeRasterSnapshot( impeller::DlDispatcher dispatcher; display_list->Dispatch(dispatcher); impeller::Picture picture = dispatcher.EndRecordingAsPicture(); - auto context = GetDelegate().GetStudio()->GetAiksContext(); + auto context = GetDelegate().GetAiksContext(); if (context) { auto max_size = context->GetContext() ->GetResourceAllocator() From e5fe6dfdc9480ca2c2b771bcb8cbc25c1425cc63 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 4 May 2023 16:56:57 -0700 Subject: [PATCH 100/147] Remove embedderSurface::CreateResourceContext --- shell/platform/embedder/embedder_surface.h | 2 -- shell/platform/embedder/embedder_surface_gl.cc | 5 ----- shell/platform/embedder/embedder_surface_gl.h | 3 --- shell/platform/embedder/embedder_surface_metal.h | 3 --- shell/platform/embedder/embedder_surface_metal.mm | 4 ---- shell/platform/embedder/embedder_surface_software.cc | 5 ----- shell/platform/embedder/embedder_surface_software.h | 3 --- shell/platform/embedder/embedder_surface_vulkan.cc | 5 ----- shell/platform/embedder/embedder_surface_vulkan.h | 3 --- 9 files changed, 33 deletions(-) diff --git a/shell/platform/embedder/embedder_surface.h b/shell/platform/embedder/embedder_surface.h index ed8dbff74b8d0..aae2e5d27585b 100644 --- a/shell/platform/embedder/embedder_surface.h +++ b/shell/platform/embedder/embedder_surface.h @@ -21,8 +21,6 @@ class EmbedderSurface { virtual std::unique_ptr CreateGPUSurface() = 0; - virtual sk_sp CreateResourceContext() const = 0; - private: FML_DISALLOW_COPY_AND_ASSIGN(EmbedderSurface); }; diff --git a/shell/platform/embedder/embedder_surface_gl.cc b/shell/platform/embedder/embedder_surface_gl.cc index 6159a15e68af5..3fb102ded3692 100644 --- a/shell/platform/embedder/embedder_surface_gl.cc +++ b/shell/platform/embedder/embedder_surface_gl.cc @@ -37,9 +37,4 @@ std::unique_ptr EmbedderSurfaceGL::CreateGPUSurface() { ); } -// |EmbedderSurface| -sk_sp EmbedderSurfaceGL::CreateResourceContext() const { - return studio_->CreateResourceContext(); -} - } // namespace flutter diff --git a/shell/platform/embedder/embedder_surface_gl.h b/shell/platform/embedder/embedder_surface_gl.h index 05456635661de..f9ed3a2fd5b22 100644 --- a/shell/platform/embedder/embedder_surface_gl.h +++ b/shell/platform/embedder/embedder_surface_gl.h @@ -32,9 +32,6 @@ class EmbedderSurfaceGL final : public EmbedderSurface { // |EmbedderSurface| std::unique_ptr CreateGPUSurface() override; - // |EmbedderSurface| - sk_sp CreateResourceContext() const override; - FML_DISALLOW_COPY_AND_ASSIGN(EmbedderSurfaceGL); }; diff --git a/shell/platform/embedder/embedder_surface_metal.h b/shell/platform/embedder/embedder_surface_metal.h index 63bc238dbefd6..168b3d7093cdc 100644 --- a/shell/platform/embedder/embedder_surface_metal.h +++ b/shell/platform/embedder/embedder_surface_metal.h @@ -38,9 +38,6 @@ class EmbedderSurfaceMetal final : public EmbedderSurface { // |EmbedderSurface| std::unique_ptr CreateGPUSurface() override; - // |EmbedderSurface| - sk_sp CreateResourceContext() const override; - FML_DISALLOW_COPY_AND_ASSIGN(EmbedderSurfaceMetal); }; diff --git a/shell/platform/embedder/embedder_surface_metal.mm b/shell/platform/embedder/embedder_surface_metal.mm index 32b9a34409c4d..1c164013d92c0 100644 --- a/shell/platform/embedder/embedder_surface_metal.mm +++ b/shell/platform/embedder/embedder_surface_metal.mm @@ -50,8 +50,4 @@ return surface; } -sk_sp EmbedderSurfaceMetal::CreateResourceContext() const { - return studio_->CreateResourceContext(); -} - } // namespace flutter diff --git a/shell/platform/embedder/embedder_surface_software.cc b/shell/platform/embedder/embedder_surface_software.cc index 6db94313126b1..8e270bbf4a7ba 100644 --- a/shell/platform/embedder/embedder_surface_software.cc +++ b/shell/platform/embedder/embedder_surface_software.cc @@ -41,9 +41,4 @@ std::unique_ptr EmbedderSurfaceSoftware::CreateGPUSurface() { return surface; } -// |EmbedderSurface| -sk_sp EmbedderSurfaceSoftware::CreateResourceContext() const { - return nullptr; -} - } // namespace flutter diff --git a/shell/platform/embedder/embedder_surface_software.h b/shell/platform/embedder/embedder_surface_software.h index 6d938b6627f29..858afc4c9eb86 100644 --- a/shell/platform/embedder/embedder_surface_software.h +++ b/shell/platform/embedder/embedder_surface_software.h @@ -32,9 +32,6 @@ class EmbedderSurfaceSoftware final : public EmbedderSurface { // |EmbedderSurface| std::unique_ptr CreateGPUSurface() override; - // |EmbedderSurface| - sk_sp CreateResourceContext() const override; - FML_DISALLOW_COPY_AND_ASSIGN(EmbedderSurfaceSoftware); }; diff --git a/shell/platform/embedder/embedder_surface_vulkan.cc b/shell/platform/embedder/embedder_surface_vulkan.cc index de92ed3babc0c..0f80a7786d511 100644 --- a/shell/platform/embedder/embedder_surface_vulkan.cc +++ b/shell/platform/embedder/embedder_surface_vulkan.cc @@ -38,9 +38,4 @@ std::unique_ptr EmbedderSurfaceVulkan::CreateGPUSurface() { render_to_surface_); } -// |EmbedderSurface| -sk_sp EmbedderSurfaceVulkan::CreateResourceContext() const { - return studio_->CreateResourceContext(); -} - } // namespace flutter diff --git a/shell/platform/embedder/embedder_surface_vulkan.h b/shell/platform/embedder/embedder_surface_vulkan.h index f26a44591d407..df25d52c8a5fd 100644 --- a/shell/platform/embedder/embedder_surface_vulkan.h +++ b/shell/platform/embedder/embedder_surface_vulkan.h @@ -36,9 +36,6 @@ class EmbedderSurfaceVulkan final : public EmbedderSurface { // |EmbedderSurface| std::unique_ptr CreateGPUSurface() override; - // |EmbedderSurface| - sk_sp CreateResourceContext() const override; - sk_sp CreateGrContext(VkInstance instance, uint32_t version, size_t instance_extension_count, From bda89f01fb2fe6fc15f8094baba9edbe2bcc0baa Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 8 May 2023 17:18:29 -0700 Subject: [PATCH 101/147] No more shared synchronizers, but passed from engine --- lib/ui/compositing/scene_builder.cc | 8 +++++ .../macos/framework/Source/FlutterEngine.mm | 10 ++++++- .../framework/Source/FlutterEngineTest.mm | 2 +- .../framework/Source/FlutterEngine_Internal.h | 2 ++ .../Source/FlutterThreadSynchronizer.mm | 23 ++++++++++++++- .../macos/framework/Source/FlutterView.h | 13 +-------- .../macos/framework/Source/FlutterView.mm | 29 +++++++------------ .../framework/Source/FlutterViewController.mm | 6 ++++ .../macos/framework/Source/FlutterViewTest.mm | 2 ++ 9 files changed, 61 insertions(+), 34 deletions(-) diff --git a/lib/ui/compositing/scene_builder.cc b/lib/ui/compositing/scene_builder.cc index 9f6d58de31ac9..439e004cc2f44 100644 --- a/lib/ui/compositing/scene_builder.cc +++ b/lib/ui/compositing/scene_builder.cc @@ -214,14 +214,20 @@ void SceneBuilder::addPicture(double dx, double dy, Picture* picture, int hints) { + printf("SceneBuilder::addPicture start\n"); + fflush(stdout); if (!picture) { // Picture::dispose was called and it has been collected. + printf("SceneBuilder::addPicture ret 1\n"); + fflush(stdout); return; } // Explicitly check for display_list, since the picture object might have // been disposed but not collected yet, but the display list is null. if (picture->display_list()) { + printf("SceneBuilder::addPicture actual\n"); + fflush(stdout); auto layer = std::make_unique( SkPoint::Make(SafeNarrow(dx), SafeNarrow(dy)), picture->display_list(), !!(hints & 1), !!(hints & 2)); @@ -294,6 +300,8 @@ void SceneBuilder::AddLayer(std::shared_ptr layer) { FML_DCHECK(layer); if (!layer_stack_.empty()) { + printf("SceneBuilder::AddLayer actual\n"); + fflush(stdout); layer_stack_.back()->Add(std::move(layer)); } } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 3f3b9489db950..75ae047976dc8 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -382,6 +382,8 @@ @implementation FlutterEngine { // A method channel for miscellaneous platform functionality. FlutterMethodChannel* _platformChannel; + FlutterThreadSynchronizer* _synchronizer; + int _nextViewId; } @@ -421,6 +423,7 @@ - (instancetype)initWithName:(NSString*)labelPrefix object:nil]; _platformViewController = [[FlutterPlatformViewController alloc] init]; + _synchronizer = [[FlutterThreadSynchronizer alloc] init]; [self setUpPlatformViewChannel]; [self setUpAccessibilityChannel]; [self setUpNotificationCenterListeners]; @@ -897,7 +900,8 @@ - (void)shutDownEngine { } NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator]; - [[FlutterView sharedThreadSynchronizer] shutdown]; + [_synchronizer shutdown]; + _synchronizer = nil; FlutterViewController* nextViewController; while ((nextViewController = [viewControllerEnumerator nextObject])) { [nextViewController.flutterView shutdown]; @@ -1065,6 +1069,10 @@ - (NSPasteboard*)pasteboard { return flutter::GetSwitchesFromEnvironment(); } +- (FlutterThreadSynchronizer*)synchronizer { + return _synchronizer; +} + #pragma mark - FlutterBinaryMessenger - (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message { diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm index fd5a82370db8e..4322509ebc227 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm @@ -437,7 +437,7 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable result:^(id result){ }]; - [viewController.flutterView.threadSynchronizer blockUntilFrameAvailable]; + [engine.synchronizer blockUntilFrameAvailable]; CALayer* rootLayer = viewController.flutterView.layer; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h b/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h index e291094eaa342..72ad411e31cfb 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h @@ -107,6 +107,8 @@ typedef NS_ENUM(NSInteger, FlutterAppExitResponse) { */ @property(nonatomic, readonly) FlutterEngineTerminationHandler* terminationHandler; +@property(nonatomic, readonly) FlutterThreadSynchronizer* synchronizer; + /** * Attach a view controller to the engine as its default controller. * diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm index bf4506994e3cd..82f65f932e61d 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm @@ -21,8 +21,20 @@ @interface FlutterThreadSynchronizer () { std::condition_variable _condBlockBeginResize; } +/** + * Returns true if all existing views have a non-zero size. + * + * If there are no views, still returns true. + */ - (BOOL)allViewsHaveFrame; +/** + * Returns true if there are any views that have a non-zero size. + * + * If there are no views, returns false. + */ +- (BOOL)someViewsHaveFrame; + @end @implementation FlutterThreadSynchronizer @@ -36,6 +48,15 @@ - (BOOL)allViewsHaveFrame { return YES; } +- (BOOL)someViewsHaveFrame { + for (auto const& [viewId, contentSize] : _contentSizes) { + if (!CGSizeEqualToSize(contentSize, CGSizeZero)) { + return YES; + } + } + return NO; +} + - (void)drain { FML_DCHECK([NSThread isMainThread]); @@ -53,7 +74,7 @@ - (void)blockUntilFrameAvailable { _beginResizeWaiting = YES; - while (![self allViewsHaveFrame] && !_shuttingDown) { + while (![self someViewsHaveFrame] && !_shuttingDown) { _condBlockBeginResize.wait(lock); [self drain]; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.h b/shell/platform/darwin/macos/framework/Source/FlutterView.h index 0881c4f6bc6ab..579f7b4fcfdc4 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.h @@ -44,6 +44,7 @@ constexpr FlutterViewId kFlutterDefaultViewId = 0ll; - (nullable instancetype)initWithMTLDevice:(nonnull id)device commandQueue:(nonnull id)commandQueue reshapeListener:(nonnull id)reshapeListener + synchronizer:(nonnull FlutterThreadSynchronizer*)synchronizer viewId:(int64_t)viewId NS_DESIGNATED_INITIALIZER; - (nullable instancetype)initWithFrame:(NSRect)frameRect @@ -73,16 +74,4 @@ constexpr FlutterViewId kFlutterDefaultViewId = 0ll; */ - (void)setBackgroundColor:(nonnull NSColor*)color; -+ (nonnull FlutterThreadSynchronizer*)sharedThreadSynchronizer; - -@end - -@interface FlutterView (FlutterViewPrivate) - -/** - * Returns FlutterThreadSynchronizer for this view. - * Used for FlutterEngineTest. - */ -- (nonnull FlutterThreadSynchronizer*)threadSynchronizer; - @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.mm b/shell/platform/darwin/macos/framework/Source/FlutterView.mm index a5be0767e783a..3bd59f6a6b758 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.mm @@ -13,6 +13,7 @@ @interface FlutterView () { int64_t _viewId; __weak id _reshapeListener; FlutterSurfaceManager* _surfaceManager; + FlutterThreadSynchronizer* _synchronizer; } @end @@ -22,6 +23,7 @@ @implementation FlutterView - (instancetype)initWithMTLDevice:(id)device commandQueue:(id)commandQueue reshapeListener:(id)reshapeListener + synchronizer:(FlutterThreadSynchronizer*)synchronizer viewId:(int64_t)viewId { self = [super initWithFrame:NSZeroRect]; if (self) { @@ -30,34 +32,31 @@ - (instancetype)initWithMTLDevice:(id)device [self setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawDuringViewResize]; _viewId = viewId; _reshapeListener = reshapeListener; + _synchronizer = synchronizer; _surfaceManager = [[FlutterSurfaceManager alloc] initWithDevice:device commandQueue:commandQueue layer:self.layer delegate:self]; - [[FlutterView sharedThreadSynchronizer] registerView:viewId]; + [_synchronizer registerView:viewId]; } return self; } - (void)onPresent:(CGSize)frameSize withBlock:(dispatch_block_t)block { - [[FlutterView sharedThreadSynchronizer] performCommitForView:_viewId size:frameSize notify:block]; + [_synchronizer performCommitForView:_viewId size:frameSize notify:block]; } - (FlutterSurfaceManager*)surfaceManager { return _surfaceManager; } -- (FlutterThreadSynchronizer*)threadSynchronizer { - return [FlutterView sharedThreadSynchronizer]; -} - - (void)reshaped { CGSize scaledSize = [self convertSizeToBacking:self.bounds.size]; - [[FlutterView sharedThreadSynchronizer] beginResizeForView:_viewId - size:scaledSize - notify:^{ - [_reshapeListener viewDidReshape:self]; - }]; + [_synchronizer beginResizeForView:_viewId + size:scaledSize + notify:^{ + [_reshapeListener viewDidReshape:self]; + }]; } - (void)setBackgroundColor:(NSColor*)color { @@ -139,12 +138,4 @@ - (NSString*)accessibilityLabel { return applicationName; } -static FlutterThreadSynchronizer* _synchronizer; -+ (FlutterThreadSynchronizer*)sharedThreadSynchronizer { - if (_synchronizer == nil) { - _synchronizer = [[FlutterThreadSynchronizer alloc] init]; - } - return _synchronizer; -} - @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index 33d73932d09cc..540c16cd4a9a6 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -372,6 +372,10 @@ @implementation FlutterViewController { std::shared_ptr _bridge; FlutterViewId _id; + + // FlutterViewController does not actually uses the synchronizer, but only + // passes it to FlutterView. + __weak FlutterThreadSynchronizer* _weakSynchronizer; } @synthesize viewId = _viewId; @@ -398,6 +402,7 @@ static void CommonInit(FlutterViewController* controller, FlutterEngine* engine) controller.engine, controller.viewId); controller->_mouseTrackingMode = FlutterMouseTrackingModeInKeyWindow; controller->_textInputPlugin = [[FlutterTextInputPlugin alloc] initWithViewController:controller]; + controller->_weakSynchronizer = engine.synchronizer; [controller initializeKeyboard]; [controller notifySemanticsEnabledChanged]; // macOS fires this message when changing IMEs. @@ -863,6 +868,7 @@ - (nonnull FlutterView*)createFlutterViewWithMTLDevice:(id)device return [[FlutterView alloc] initWithMTLDevice:device commandQueue:commandQueue reshapeListener:self + synchronizer:_weakSynchronizer viewId:_viewId]; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewTest.mm index 40faed5b13efd..82906119f98ee 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewTest.mm @@ -25,9 +25,11 @@ - (void)viewDidReshape:(nonnull NSView*)view { id device = MTLCreateSystemDefaultDevice(); id queue = [device newCommandQueue]; TestReshapeListener* listener = [[TestReshapeListener alloc] init]; + FlutterThreadSynchronizer* synchronizer = [[FlutterThreadSynchronizer alloc] init]; FlutterView* view = [[FlutterView alloc] initWithMTLDevice:device commandQueue:queue reshapeListener:listener + synchronizer:synchronizer viewId:kDefaultViewId]; EXPECT_EQ([view layer:view.layer shouldInheritContentsScale:3.0 fromWindow:view.window], YES); } From f6be4c1585b23f7ab24b6793fa67be64e82b7d17 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 8 May 2023 18:00:11 -0700 Subject: [PATCH 102/147] Fix TestCreate bug --- .../darwin/macos/framework/Source/FlutterCompositorTest.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterCompositorTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterCompositorTest.mm index f7ac37e01be68..d447f75ca05d4 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterCompositorTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterCompositorTest.mm @@ -95,7 +95,8 @@ - (nullable FlutterView*)viewForId:(FlutterViewId)viewId { config.struct_size = sizeof(FlutterBackingStoreConfig); config.size.width = 800; config.size.height = 600; - macos_compositor->CreateBackingStore(&config, &backing_store); + config.view_id = 0; + ASSERT_TRUE(macos_compositor->CreateBackingStore(&config, &backing_store)); ASSERT_EQ(backing_store.type, kFlutterBackingStoreTypeMetal); ASSERT_NE(backing_store.metal.texture.texture, nil); From ff3bfd0e96bfa4c08f48089e9b1ec2e2ff8cf83a Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 8 May 2023 18:05:38 -0700 Subject: [PATCH 103/147] Fix compile error in last merge --- lib/ui/hooks.dart | 1 + .../android/platform_view_android_jni_impl.cc | 3 +- .../fuchsia/flutter/flatland_platform_view.cc | 49 ++++++++++--------- .../fuchsia/flutter/gfx_platform_view.cc | 46 ++++++++--------- 4 files changed, 52 insertions(+), 47 deletions(-) diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index f2131600bd91a..08c5ddb8cf191 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -18,6 +18,7 @@ void _removeView( PlatformDispatcher.instance._removeView(id); } +@pragma('vm:entry-point') void _updateDisplays( List ids, List widths, diff --git a/shell/platform/android/platform_view_android_jni_impl.cc b/shell/platform/android/platform_view_android_jni_impl.cc index a68799f358b27..05637727521f8 100644 --- a/shell/platform/android/platform_view_android_jni_impl.cc +++ b/shell/platform/android/platform_view_android_jni_impl.cc @@ -340,7 +340,8 @@ static void SetViewportMetrics(JNIEnv* env, 0, // Display ID }; - ANDROID_SHELL_HOLDER->GetPlatformView()->SetViewportMetrics(metrics); + ANDROID_SHELL_HOLDER->GetPlatformView()->SetViewportMetrics( + kFlutterDefaultViewId, metrics); } static void UpdateDisplayMetrics(JNIEnv* env, diff --git a/shell/platform/fuchsia/flutter/flatland_platform_view.cc b/shell/platform/fuchsia/flutter/flatland_platform_view.cc index 7ed7099ac96dc..11a46d0c7ff80 100644 --- a/shell/platform/fuchsia/flutter/flatland_platform_view.cc +++ b/shell/platform/fuchsia/flutter/flatland_platform_view.cc @@ -87,30 +87,31 @@ void FlatlandPlatformView::OnGetLayout( float pixel_ratio = view_pixel_ratio_ ? *view_pixel_ratio_ : 1.0f; - SetViewportMetrics({ - pixel_ratio, // device_pixel_ratio - std::round(view_logical_size_.value()[0] * - pixel_ratio), // physical_width - std::round(view_logical_size_.value()[1] * - pixel_ratio), // physical_height - 0.0f, // physical_padding_top - 0.0f, // physical_padding_right - 0.0f, // physical_padding_bottom - 0.0f, // physical_padding_left - 0.0f, // physical_view_inset_top - 0.0f, // physical_view_inset_right - 0.0f, // physical_view_inset_bottom - 0.0f, // physical_view_inset_left - 0.0f, // p_physical_system_gesture_inset_top - 0.0f, // p_physical_system_gesture_inset_right - 0.0f, // p_physical_system_gesture_inset_bottom - 0.0f, // p_physical_system_gesture_inset_left, - -1.0, // p_physical_touch_slop, - {}, // p_physical_display_features_bounds - {}, // p_physical_display_features_type - {}, // p_physical_display_features_state - kFlutterDefaultViewId, // p_display_id - }); + SetViewportMetrics(kFlutterDefaultViewId, + { + pixel_ratio, // device_pixel_ratio + std::round(view_logical_size_.value()[0] * + pixel_ratio), // physical_width + std::round(view_logical_size_.value()[1] * + pixel_ratio), // physical_height + 0.0f, // physical_padding_top + 0.0f, // physical_padding_right + 0.0f, // physical_padding_bottom + 0.0f, // physical_padding_left + 0.0f, // physical_view_inset_top + 0.0f, // physical_view_inset_right + 0.0f, // physical_view_inset_bottom + 0.0f, // physical_view_inset_left + 0.0f, // p_physical_system_gesture_inset_top + 0.0f, // p_physical_system_gesture_inset_right + 0.0f, // p_physical_system_gesture_inset_bottom + 0.0f, // p_physical_system_gesture_inset_left, + -1.0, // p_physical_touch_slop, + {}, // p_physical_display_features_bounds + {}, // p_physical_display_features_type + {}, // p_physical_display_features_state + 0, // p_display_id + }); parent_viewport_watcher_->GetLayout( fit::bind_member(this, &FlatlandPlatformView::OnGetLayout)); diff --git a/shell/platform/fuchsia/flutter/gfx_platform_view.cc b/shell/platform/fuchsia/flutter/gfx_platform_view.cc index f4eacd6353915..d79826483d448 100644 --- a/shell/platform/fuchsia/flutter/gfx_platform_view.cc +++ b/shell/platform/fuchsia/flutter/gfx_platform_view.cc @@ -215,28 +215,30 @@ void GfxPlatformView::OnScenicEvent( metrics_changed) { const float pixel_ratio = *view_pixel_ratio_; const std::array logical_size = *view_logical_size_; - SetViewportMetrics({ - pixel_ratio, // device_pixel_ratio - std::round(logical_size[0] * pixel_ratio), // physical_width - std::round(logical_size[1] * pixel_ratio), // physical_height - 0.0f, // physical_padding_top - 0.0f, // physical_padding_right - 0.0f, // physical_padding_bottom - 0.0f, // physical_padding_left - 0.0f, // physical_view_inset_top - 0.0f, // physical_view_inset_right - 0.0f, // physical_view_inset_bottom - 0.0f, // physical_view_inset_left - 0.0f, // p_physical_system_gesture_inset_top - 0.0f, // p_physical_system_gesture_inset_right - 0.0f, // p_physical_system_gesture_inset_bottom - 0.0f, // p_physical_system_gesture_inset_left, - -1.0, // p_physical_touch_slop, - {}, // p_physical_display_features_bounds - {}, // p_physical_display_features_type - {}, // p_physical_display_features_state - kFlutterDefaultViewId, // pdisplay_id - }); + SetViewportMetrics( + kFlutterDefaultViewId, + { + pixel_ratio, // device_pixel_ratio + std::round(logical_size[0] * pixel_ratio), // physical_width + std::round(logical_size[1] * pixel_ratio), // physical_height + 0.0f, // physical_padding_top + 0.0f, // physical_padding_right + 0.0f, // physical_padding_bottom + 0.0f, // physical_padding_left + 0.0f, // physical_view_inset_top + 0.0f, // physical_view_inset_right + 0.0f, // physical_view_inset_bottom + 0.0f, // physical_view_inset_left + 0.0f, // p_physical_system_gesture_inset_top + 0.0f, // p_physical_system_gesture_inset_right + 0.0f, // p_physical_system_gesture_inset_bottom + 0.0f, // p_physical_system_gesture_inset_left, + -1.0, // p_physical_touch_slop, + {}, // p_physical_display_features_bounds + {}, // p_physical_display_features_type + {}, // p_physical_display_features_state + 0, // pdisplay_id + }); } } From 44ab24bf7c71670001c981fc667805a9bef288b6 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 8 May 2023 20:14:50 -0700 Subject: [PATCH 104/147] Fix compile --- .../ios/framework/Source/FlutterPlatformViewsTest.mm | 8 ++++---- shell/platform/darwin/ios/ios_external_view_embedder.mm | 4 +--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index f1a15c061881e..4e63e60fcb462 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -2863,8 +2863,8 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { std::move(mock_sk_surface), framebuffer_info, [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - XCTAssertTrue( - flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, kDefaultViewId, + std::move(mock_surface))); // Disposing won't remove embedded views until the view is removed from the composition_order_ XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL); @@ -2890,8 +2890,8 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { std::move(mock_sk_surface), framebuffer_info, [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - XCTAssertTrue( - flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); + XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, kDefaultViewId, + std::move(mock_surface))); // Disposing won't remove embedded views until the view is removed from the composition_order_ XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm index e0351b8ae74b0..7c34af1a1a843 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.mm +++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm @@ -73,9 +73,7 @@ int64_t window_view_id) { TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::SubmitFrame"); FML_CHECK(platform_views_controller_); - // TODO(dkwingsmt) - platform_views_controller_->SubmitFrame(context, ios_context_, kFlutterDefaultViewId, - std::move(frame)); + platform_views_controller_->SubmitFrame(context, ios_context_, window_view_id, std::move(frame)); TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::DidSubmitFrame"); } From 9bb4a01910d1ff2ee817a035dd18c900c1d0c309 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 8 May 2023 20:18:13 -0700 Subject: [PATCH 105/147] Remove print --- lib/ui/compositing/scene_builder.cc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/ui/compositing/scene_builder.cc b/lib/ui/compositing/scene_builder.cc index 439e004cc2f44..9f6d58de31ac9 100644 --- a/lib/ui/compositing/scene_builder.cc +++ b/lib/ui/compositing/scene_builder.cc @@ -214,20 +214,14 @@ void SceneBuilder::addPicture(double dx, double dy, Picture* picture, int hints) { - printf("SceneBuilder::addPicture start\n"); - fflush(stdout); if (!picture) { // Picture::dispose was called and it has been collected. - printf("SceneBuilder::addPicture ret 1\n"); - fflush(stdout); return; } // Explicitly check for display_list, since the picture object might have // been disposed but not collected yet, but the display list is null. if (picture->display_list()) { - printf("SceneBuilder::addPicture actual\n"); - fflush(stdout); auto layer = std::make_unique( SkPoint::Make(SafeNarrow(dx), SafeNarrow(dy)), picture->display_list(), !!(hints & 1), !!(hints & 2)); @@ -300,8 +294,6 @@ void SceneBuilder::AddLayer(std::shared_ptr layer) { FML_DCHECK(layer); if (!layer_stack_.empty()) { - printf("SceneBuilder::AddLayer actual\n"); - fflush(stdout); layer_stack_.back()->Add(std::move(layer)); } } From 4121d972b04daeaf385fb507d3f05024e5df068f Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 8 May 2023 21:06:38 -0700 Subject: [PATCH 106/147] Remove unused var --- shell/platform/darwin/ios/ios_external_view_embedder.mm | 2 -- 1 file changed, 2 deletions(-) diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm index 7c34af1a1a843..dde57241ee2a7 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.mm +++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm @@ -6,8 +6,6 @@ namespace flutter { -constexpr int64_t kFlutterDefaultViewId = 0ll; - IOSExternalViewEmbedder::IOSExternalViewEmbedder( const std::shared_ptr& platform_views_controller, std::shared_ptr context) From f9936440f4439a709a5bdfcfc14bcdb08ffb8ad4 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 8 May 2023 21:07:07 -0700 Subject: [PATCH 107/147] Add more view_id to config --- .../darwin/macos/framework/Source/FlutterCompositorTest.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterCompositorTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterCompositorTest.mm index d447f75ca05d4..e8903c1667c79 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterCompositorTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterCompositorTest.mm @@ -121,6 +121,7 @@ - (nullable FlutterView*)viewForId:(FlutterViewId)viewId { config.struct_size = sizeof(FlutterBackingStoreConfig); config.size.width = 800; config.size.height = 600; + config.view_id = 0; macos_compositor->CreateBackingStore(&config, &backing_store); FlutterLayer layers[] = {{ From 783e34e13a32354ed7627b63586e8ed52af46520 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 10 May 2023 14:38:46 -0700 Subject: [PATCH 108/147] Impl --- .../macos/framework/Source/FlutterEngine.mm | 9 +++ .../framework/Source/FlutterEngineTest.mm | 11 +-- .../framework/Source/FlutterEngine_Internal.h | 2 + .../Source/FlutterThreadSynchronizer.h | 12 +++- .../Source/FlutterThreadSynchronizer.mm | 69 ++++++++++++++++--- .../macos/framework/Source/FlutterView.h | 13 +--- .../macos/framework/Source/FlutterView.mm | 25 +++---- .../framework/Source/FlutterViewController.mm | 9 ++- .../macos/framework/Source/FlutterViewTest.mm | 7 +- 9 files changed, 117 insertions(+), 40 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 16e8405a6fcdd..4f09c1a1b95e6 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -382,6 +382,8 @@ @implementation FlutterEngine { // A method channel for miscellaneous platform functionality. FlutterMethodChannel* _platformChannel; + FlutterThreadSynchronizer* _threadSynchronizer; + int _nextViewId; } @@ -421,6 +423,7 @@ - (instancetype)initWithName:(NSString*)labelPrefix object:nil]; _platformViewController = [[FlutterPlatformViewController alloc] init]; + _threadSynchronizer = [[FlutterThreadSynchronizer alloc] init]; [self setUpPlatformViewChannel]; [self setUpAccessibilityChannel]; [self setUpNotificationCenterListeners]; @@ -905,6 +908,8 @@ - (void)shutDownEngine { } NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator]; + [_threadSynchronizer shutdown]; + _threadSynchronizer = nil; FlutterViewController* nextViewController; while ((nextViewController = [viewControllerEnumerator nextObject])) { [nextViewController.flutterView shutdown]; @@ -1072,6 +1077,10 @@ - (NSPasteboard*)pasteboard { return flutter::GetSwitchesFromEnvironment(); } +- (FlutterThreadSynchronizer*)threadSynchronizer { + return _threadSynchronizer; +} + #pragma mark - FlutterBinaryMessenger - (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message { diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm index d7aa813ca2fbf..8018c5f8d2bee 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm @@ -24,6 +24,8 @@ // CREATE_NATIVE_ENTRY and MOCK_ENGINE_PROC are leaky by design // NOLINTBEGIN(clang-analyzer-core.StackAddressEscape) +constexpr int64_t kDefaultViewId = 0ll; + @interface FlutterEngine (Test) /** * The FlutterCompositor object currently in use by the FlutterEngine. @@ -435,7 +437,7 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable result:^(id result){ }]; - [viewController.flutterView.threadSynchronizer blockUntilFrameAvailable]; + [engine.threadSynchronizer blockUntilFrameAvailable]; CALayer* rootLayer = viewController.flutterView.layer; @@ -626,9 +628,10 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable [threadSynchronizer shutdown]; std::thread rasterThread([&threadSynchronizer] { - [threadSynchronizer performCommit:CGSizeMake(100, 100) - notify:^{ - }]; + [threadSynchronizer performCommitForView:kDefaultViewId + size:CGSizeMake(100, 100) + notify:^{ + }]; }); rasterThread.join(); diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h b/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h index e291094eaa342..e5b185c492b03 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h @@ -107,6 +107,8 @@ typedef NS_ENUM(NSInteger, FlutterAppExitResponse) { */ @property(nonatomic, readonly) FlutterEngineTerminationHandler* terminationHandler; +@property(nonatomic, readonly) FlutterThreadSynchronizer* threadSynchronizer; + /** * Attach a view controller to the engine as its default controller. * diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h index 8eca9611f0f48..5cc399cb33279 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h @@ -15,7 +15,9 @@ * Called from platform thread. Blocks until commit with given size (or empty) * is requested. */ -- (void)beginResize:(CGSize)size notify:(nonnull dispatch_block_t)notify; +- (void)beginResizeForView:(int64_t)viewId + size:(CGSize)size + notify:(nonnull dispatch_block_t)notify; /** * Called from raster thread. Schedules the given block on platform thread @@ -26,7 +28,13 @@ * * The notify block is guaranteed to be called within a core animation transaction. */ -- (void)performCommit:(CGSize)size notify:(nonnull dispatch_block_t)notify; +- (void)performCommitForView:(int64_t)viewId + size:(CGSize)size + notify:(nonnull dispatch_block_t)notify; + +- (void)registerView:(int64_t)viewId; + +- (void)deregisterView:(int64_t)viewId; /** * Called when shutting down. Unblocks everything and prevents any further synchronization. diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm index cb442ea768ffb..82f65f932e61d 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm @@ -3,6 +3,7 @@ #import #include +#include #include #import "flutter/fml/logging.h" @@ -11,7 +12,7 @@ @interface FlutterThreadSynchronizer () { std::mutex _mutex; BOOL _shuttingDown; - CGSize _contentSize; + std::unordered_map _contentSizes; std::vector _scheduledBlocks; BOOL _beginResizeWaiting; @@ -20,10 +21,42 @@ @interface FlutterThreadSynchronizer () { std::condition_variable _condBlockBeginResize; } +/** + * Returns true if all existing views have a non-zero size. + * + * If there are no views, still returns true. + */ +- (BOOL)allViewsHaveFrame; + +/** + * Returns true if there are any views that have a non-zero size. + * + * If there are no views, returns false. + */ +- (BOOL)someViewsHaveFrame; + @end @implementation FlutterThreadSynchronizer +- (BOOL)allViewsHaveFrame { + for (auto const& [viewId, contentSize] : _contentSizes) { + if (CGSizeEqualToSize(contentSize, CGSizeZero)) { + return NO; + } + } + return YES; +} + +- (BOOL)someViewsHaveFrame { + for (auto const& [viewId, contentSize] : _contentSizes) { + if (!CGSizeEqualToSize(contentSize, CGSizeZero)) { + return YES; + } + } + return NO; +} + - (void)drain { FML_DCHECK([NSThread isMainThread]); @@ -41,7 +74,7 @@ - (void)blockUntilFrameAvailable { _beginResizeWaiting = YES; - while (CGSizeEqualToSize(_contentSize, CGSizeZero) && !_shuttingDown) { + while (![self someViewsHaveFrame] && !_shuttingDown) { _condBlockBeginResize.wait(lock); [self drain]; } @@ -49,10 +82,12 @@ - (void)blockUntilFrameAvailable { _beginResizeWaiting = NO; } -- (void)beginResize:(CGSize)size notify:(nonnull dispatch_block_t)notify { +- (void)beginResizeForView:(int64_t)viewId + size:(CGSize)size + notify:(nonnull dispatch_block_t)notify { std::unique_lock lock(_mutex); - if (CGSizeEqualToSize(_contentSize, CGSizeZero) || _shuttingDown) { + if (![self allViewsHaveFrame] || _shuttingDown) { // No blocking until framework produces at least one frame notify(); return; @@ -62,12 +97,18 @@ - (void)beginResize:(CGSize)size notify:(nonnull dispatch_block_t)notify { notify(); - _contentSize = CGSizeMake(-1, -1); + _contentSizes[viewId] = CGSizeMake(-1, -1); _beginResizeWaiting = YES; - while (!CGSizeEqualToSize(_contentSize, size) && // - !CGSizeEqualToSize(_contentSize, CGSizeZero) && !_shuttingDown) { + while (true) { + if (_shuttingDown) { + break; + } + const CGSize& contentSize = _contentSizes[viewId]; + if (CGSizeEqualToSize(contentSize, size) || CGSizeEqualToSize(contentSize, CGSizeZero)) { + break; + } _condBlockBeginResize.wait(lock); [self drain]; } @@ -75,7 +116,9 @@ - (void)beginResize:(CGSize)size notify:(nonnull dispatch_block_t)notify { _beginResizeWaiting = NO; } -- (void)performCommit:(CGSize)size notify:(nonnull dispatch_block_t)notify { +- (void)performCommitForView:(int64_t)viewId + size:(CGSize)size + notify:(nonnull dispatch_block_t)notify { fml::AutoResetWaitableEvent event; { std::unique_lock lock(_mutex); @@ -87,7 +130,7 @@ - (void)performCommit:(CGSize)size notify:(nonnull dispatch_block_t)notify { fml::AutoResetWaitableEvent& e = event; _scheduledBlocks.push_back(^{ notify(); - _contentSize = size; + _contentSizes[viewId] = size; e.Signal(); }); if (_beginResizeWaiting) { @@ -102,6 +145,14 @@ - (void)performCommit:(CGSize)size notify:(nonnull dispatch_block_t)notify { event.Wait(); } +- (void)registerView:(int64_t)viewId { + _contentSizes[viewId] = CGSizeZero; +} + +- (void)deregisterView:(int64_t)viewId { + _contentSizes.erase(viewId); +} + - (void)shutdown { std::unique_lock lock(_mutex); _shuttingDown = YES; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.h b/shell/platform/darwin/macos/framework/Source/FlutterView.h index f581079574bdc..fe2d55aac554d 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.h @@ -44,7 +44,8 @@ constexpr FlutterViewId kFlutterDefaultViewId = 0ll; - (nullable instancetype)initWithMTLDevice:(nonnull id)device commandQueue:(nonnull id)commandQueue reshapeListener:(nonnull id)reshapeListener - NS_DESIGNATED_INITIALIZER; + threadSynchronizer:(nonnull FlutterThreadSynchronizer*)threadSynchronizer + viewId:(int64_t)viewId NS_DESIGNATED_INITIALIZER; - (nullable instancetype)initWithFrame:(NSRect)frameRect pixelFormat:(nullable NSOpenGLPixelFormat*)format NS_UNAVAILABLE; @@ -74,13 +75,3 @@ constexpr FlutterViewId kFlutterDefaultViewId = 0ll; - (void)setBackgroundColor:(nonnull NSColor*)color; @end - -@interface FlutterView (FlutterViewPrivate) - -/** - * Returns FlutterThreadSynchronizer for this view. - * Used for FlutterEngineTest. - */ -- (nonnull FlutterThreadSynchronizer*)threadSynchronizer; - -@end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.mm b/shell/platform/darwin/macos/framework/Source/FlutterView.mm index c3f802d43ed9e..db20790f07b57 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.mm @@ -10,6 +10,7 @@ #import @interface FlutterView () { + int64_t _viewId; __weak id _reshapeListener; FlutterThreadSynchronizer* _threadSynchronizer; FlutterSurfaceManager* _surfaceManager; @@ -21,40 +22,41 @@ @implementation FlutterView - (instancetype)initWithMTLDevice:(id)device commandQueue:(id)commandQueue - reshapeListener:(id)reshapeListener { + reshapeListener:(id)reshapeListener + threadSynchronizer:(FlutterThreadSynchronizer*)threadSynchronizer + viewId:(int64_t)viewId { self = [super initWithFrame:NSZeroRect]; if (self) { [self setWantsLayer:YES]; [self setBackgroundColor:[NSColor blackColor]]; [self setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawDuringViewResize]; + _viewId = viewId; _reshapeListener = reshapeListener; - _threadSynchronizer = [[FlutterThreadSynchronizer alloc] init]; + _threadSynchronizer = threadSynchronizer; _surfaceManager = [[FlutterSurfaceManager alloc] initWithDevice:device commandQueue:commandQueue layer:self.layer delegate:self]; + [_threadSynchronizer registerView:viewId]; } return self; } - (void)onPresent:(CGSize)frameSize withBlock:(dispatch_block_t)block { - [_threadSynchronizer performCommit:frameSize notify:block]; + [_threadSynchronizer performCommitForView:_viewId size:frameSize notify:block]; } - (FlutterSurfaceManager*)surfaceManager { return _surfaceManager; } -- (FlutterThreadSynchronizer*)threadSynchronizer { - return _threadSynchronizer; -} - - (void)reshaped { CGSize scaledSize = [self convertSizeToBacking:self.bounds.size]; - [_threadSynchronizer beginResize:scaledSize - notify:^{ - [_reshapeListener viewDidReshape:self]; - }]; + [_threadSynchronizer beginResizeForView:_viewId + size:scaledSize + notify:^{ + [_reshapeListener viewDidReshape:self]; + }]; } - (void)setBackgroundColor:(NSColor*)color { @@ -113,7 +115,6 @@ - (BOOL)layer:(CALayer*)layer } - (void)shutdown { - [_threadSynchronizer shutdown]; } #pragma mark - NSAccessibility overrides diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index f6e9aa3eb957f..f7c082b694323 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -372,6 +372,10 @@ @implementation FlutterViewController { std::shared_ptr _bridge; FlutterViewId _id; + + // FlutterViewController does not actually uses the synchronizer, but only + // passes it to FlutterView. + __weak FlutterThreadSynchronizer* _weakViewThreadSynchronizer; } @synthesize viewId = _viewId; @@ -398,6 +402,7 @@ static void CommonInit(FlutterViewController* controller, FlutterEngine* engine) controller.engine, controller.viewId); controller->_mouseTrackingMode = FlutterMouseTrackingModeInKeyWindow; controller->_textInputPlugin = [[FlutterTextInputPlugin alloc] initWithViewController:controller]; + controller->_weakViewThreadSynchronizer = engine.threadSynchronizer; [controller initializeKeyboard]; [controller notifySemanticsEnabledChanged]; // macOS fires this message when changing IMEs. @@ -858,7 +863,9 @@ - (nonnull FlutterView*)createFlutterViewWithMTLDevice:(id)device commandQueue:(id)commandQueue { return [[FlutterView alloc] initWithMTLDevice:device commandQueue:commandQueue - reshapeListener:self]; + reshapeListener:self + threadSynchronizer:_weakViewThreadSynchronizer + viewId:_viewId]; } - (void)onKeyboardLayoutChanged { diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewTest.mm index 15ca078042fd8..7599b66e8b0ec 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewTest.mm @@ -8,6 +8,8 @@ #import "flutter/testing/testing.h" +constexpr int64_t kDefaultViewId = 0ll; + @interface TestReshapeListener : NSObject @end @@ -23,8 +25,11 @@ - (void)viewDidReshape:(nonnull NSView*)view { id device = MTLCreateSystemDefaultDevice(); id queue = [device newCommandQueue]; TestReshapeListener* listener = [[TestReshapeListener alloc] init]; + FlutterThreadSynchronizer* threadSynchronizer = [[FlutterThreadSynchronizer alloc] init]; FlutterView* view = [[FlutterView alloc] initWithMTLDevice:device commandQueue:queue - reshapeListener:listener]; + reshapeListener:listener + threadSynchronizer:threadSynchronizer + viewId:kDefaultViewId]; EXPECT_EQ([view layer:view.layer shouldInheritContentsScale:3.0 fromWindow:view.window], YES); } From f174bd44475ef55e984991df3e2f12e94d9cc5d4 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 16 May 2023 01:29:43 -0700 Subject: [PATCH 109/147] Fix 1 --- shell/platform/android/android_surface_gl_skia.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/android/android_surface_gl_skia.cc b/shell/platform/android/android_surface_gl_skia.cc index ce7b3c63d50ba..91e98c9836085 100644 --- a/shell/platform/android/android_surface_gl_skia.cc +++ b/shell/platform/android/android_surface_gl_skia.cc @@ -222,7 +222,7 @@ sk_sp AndroidSurfaceGLSkia::GetGLInterface() const { return GPUSurfaceGLDelegate::GetGLInterface(); } -std::unique_ptr AndroidSurfaceGLSkia::CreateSnapshotSurface() { +std::unique_ptr AndroidSurfaceGLSkia::CreateSnapshotStudio() { if (!onscreen_surface_ || !onscreen_surface_->IsValid()) { onscreen_surface_ = android_context_->CreatePbufferSurface(); } From ecbb1a9f4b33700e6e95bc317ce5452e5ea6407f Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 16 May 2023 01:36:16 -0700 Subject: [PATCH 110/147] Fix more --- shell/platform/android/android_surface_gl_impeller.cc | 8 ++++---- shell/platform/android/android_surface_gl_skia.h | 3 +++ shell/platform/android/android_surface_vulkan_impeller.cc | 3 ++- .../ios/framework/Source/FlutterPlatformViewsTest.mm | 3 ++- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/shell/platform/android/android_surface_gl_impeller.cc b/shell/platform/android/android_surface_gl_impeller.cc index 532fbb3bbb879..646a53ab0f7a3 100644 --- a/shell/platform/android/android_surface_gl_impeller.cc +++ b/shell/platform/android/android_surface_gl_impeller.cc @@ -36,10 +36,10 @@ bool AndroidSurfaceGLImpeller::IsValid() const { // |AndroidSurface| std::unique_ptr AndroidSurfaceGLImpeller::CreateGPUStudio( GrDirectContext* gr_context) { - auto studio = - std::make_unique(this, // delegate - impeller_context_ // context - ); + auto studio = std::make_unique( + this, // delegate + android_context_->GetImpellerContext() // context + ); if (!studio->IsValid()) { return nullptr; } diff --git a/shell/platform/android/android_surface_gl_skia.h b/shell/platform/android/android_surface_gl_skia.h index df6bb29861aea..98a6d84346f95 100644 --- a/shell/platform/android/android_surface_gl_skia.h +++ b/shell/platform/android/android_surface_gl_skia.h @@ -89,6 +89,9 @@ class AndroidSurfaceGLSkia final : public GPUSurfaceGLDelegate, std::unique_ptr onscreen_surface_; std::unique_ptr offscreen_surface_; + sk_sp UseExistingMainContextOrCreate( + GrDirectContext* gr_context); + FML_DISALLOW_COPY_AND_ASSIGN(AndroidSurfaceGLSkia); }; diff --git a/shell/platform/android/android_surface_vulkan_impeller.cc b/shell/platform/android/android_surface_vulkan_impeller.cc index 238e2c6e5a681..76dbcfad08d67 100644 --- a/shell/platform/android/android_surface_vulkan_impeller.cc +++ b/shell/platform/android/android_surface_vulkan_impeller.cc @@ -40,7 +40,8 @@ std::unique_ptr AndroidSurfaceVulkanImpeller::CreateGPUStudio( } std::unique_ptr studio = - std::make_unique(impeller_context_); + std::make_unique( + android_context_->GetImpellerContext()); if (!studio->IsValid()) { return nullptr; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index 00fb2e05bc85d..0891cbe184242 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -2955,7 +2955,8 @@ - (void)testOnlyPlatformViewsAreRemovedWhenReset { [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)); + flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, kDefaultViewId, + std::move(mock_surface)); UIView* someView = [[[UIView alloc] init] autorelease]; [mockFlutterView addSubview:someView]; From ea97d923968cbcee0ece402fc20d1e802a3fa1ba Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 16 May 2023 11:35:15 -0700 Subject: [PATCH 111/147] Fix compile --- impeller/renderer/backend/vulkan/command_encoder_vk.cc | 4 ++-- impeller/renderer/backend/vulkan/descriptor_pool_vk.cc | 2 +- shell/platform/android/android_surface_gl_skia.cc | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/impeller/renderer/backend/vulkan/command_encoder_vk.cc b/impeller/renderer/backend/vulkan/command_encoder_vk.cc index 7b234b470c1dc..4e5857ef18fd4 100644 --- a/impeller/renderer/backend/vulkan/command_encoder_vk.cc +++ b/impeller/renderer/backend/vulkan/command_encoder_vk.cc @@ -16,7 +16,7 @@ class TrackedObjectsVK { public: explicit TrackedObjectsVK(std::weak_ptr device_holder, const std::shared_ptr& pool) - : desc_pool_(device_holder) { + : desc_pool_(std::move(device_holder)) { if (!pool) { return; } @@ -96,7 +96,7 @@ class TrackedObjectsVK { }; CommandEncoderVK::CommandEncoderVK( - std::weak_ptr device_holder, + const std::weak_ptr& device_holder, const std::shared_ptr& queue, const std::shared_ptr& pool, std::shared_ptr fence_waiter) diff --git a/impeller/renderer/backend/vulkan/descriptor_pool_vk.cc b/impeller/renderer/backend/vulkan/descriptor_pool_vk.cc index 7d2a6c4c5eccb..8c58ddfff1cd0 100644 --- a/impeller/renderer/backend/vulkan/descriptor_pool_vk.cc +++ b/impeller/renderer/backend/vulkan/descriptor_pool_vk.cc @@ -10,7 +10,7 @@ namespace impeller { DescriptorPoolVK::DescriptorPoolVK( - std::weak_ptr device_holder) + const std::weak_ptr& device_holder) : device_holder_(device_holder) { FML_DCHECK(device_holder.lock()); } diff --git a/shell/platform/android/android_surface_gl_skia.cc b/shell/platform/android/android_surface_gl_skia.cc index 91e98c9836085..f4b7c34b97fc5 100644 --- a/shell/platform/android/android_surface_gl_skia.cc +++ b/shell/platform/android/android_surface_gl_skia.cc @@ -60,7 +60,7 @@ sk_sp AndroidSurfaceGLSkia::UseExistingMainContextOrCreate( main_skia_context = GPUStudioGLSkia::MakeGLContext(this); android_context_->SetMainSkiaContext(main_skia_context); } - FML_DCHECK(GLContextPtr()->GetMainSkiaContext() == main_skia_context); + FML_DCHECK(android_context_()->GetMainSkiaContext() == main_skia_context); return main_skia_context; } } From f757ab7faf40dd0a01493797723facfcd162f603 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 16 May 2023 11:55:53 -0700 Subject: [PATCH 112/147] Compile --- impeller/renderer/backend/vulkan/command_encoder_vk.h | 2 +- impeller/renderer/backend/vulkan/descriptor_pool_vk.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/impeller/renderer/backend/vulkan/command_encoder_vk.h b/impeller/renderer/backend/vulkan/command_encoder_vk.h index d160385472702..e7b5d4c74f270 100644 --- a/impeller/renderer/backend/vulkan/command_encoder_vk.h +++ b/impeller/renderer/backend/vulkan/command_encoder_vk.h @@ -76,7 +76,7 @@ class CommandEncoderVK { std::shared_ptr tracked_objects_; bool is_valid_ = false; - CommandEncoderVK(std::weak_ptr device_holder, + CommandEncoderVK(const std::weak_ptr& device_holder, const std::shared_ptr& queue, const std::shared_ptr& pool, std::shared_ptr fence_waiter); diff --git a/impeller/renderer/backend/vulkan/descriptor_pool_vk.h b/impeller/renderer/backend/vulkan/descriptor_pool_vk.h index 5fb05d5827cb9..72b2da38408c6 100644 --- a/impeller/renderer/backend/vulkan/descriptor_pool_vk.h +++ b/impeller/renderer/backend/vulkan/descriptor_pool_vk.h @@ -27,7 +27,8 @@ namespace impeller { /// class DescriptorPoolVK { public: - explicit DescriptorPoolVK(std::weak_ptr device_holder); + explicit DescriptorPoolVK( + const std::weak_ptr& device_holder); ~DescriptorPoolVK(); From cad24e3aaf841714ae394dbe0444d8408ba61ac3 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 16 May 2023 12:45:49 -0700 Subject: [PATCH 113/147] Fix compile --- impeller/renderer/backend/vulkan/command_encoder_vk.cc | 5 +++-- shell/platform/android/android_surface_gl_skia.cc | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/impeller/renderer/backend/vulkan/command_encoder_vk.cc b/impeller/renderer/backend/vulkan/command_encoder_vk.cc index 4e5857ef18fd4..979b6969df038 100644 --- a/impeller/renderer/backend/vulkan/command_encoder_vk.cc +++ b/impeller/renderer/backend/vulkan/command_encoder_vk.cc @@ -14,8 +14,9 @@ namespace impeller { class TrackedObjectsVK { public: - explicit TrackedObjectsVK(std::weak_ptr device_holder, - const std::shared_ptr& pool) + explicit TrackedObjectsVK( + const std::weak_ptr& device_holder, + const std::shared_ptr& pool) : desc_pool_(std::move(device_holder)) { if (!pool) { return; diff --git a/shell/platform/android/android_surface_gl_skia.cc b/shell/platform/android/android_surface_gl_skia.cc index f4b7c34b97fc5..abd44d205345a 100644 --- a/shell/platform/android/android_surface_gl_skia.cc +++ b/shell/platform/android/android_surface_gl_skia.cc @@ -60,7 +60,7 @@ sk_sp AndroidSurfaceGLSkia::UseExistingMainContextOrCreate( main_skia_context = GPUStudioGLSkia::MakeGLContext(this); android_context_->SetMainSkiaContext(main_skia_context); } - FML_DCHECK(android_context_()->GetMainSkiaContext() == main_skia_context); + FML_DCHECK(android_context_->GetMainSkiaContext() == main_skia_context); return main_skia_context; } } From da84c2f26b5fb9334633b617818acb1b92123e62 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 16 May 2023 13:27:29 -0700 Subject: [PATCH 114/147] Fix lint --- impeller/renderer/backend/vulkan/command_encoder_vk.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/renderer/backend/vulkan/command_encoder_vk.cc b/impeller/renderer/backend/vulkan/command_encoder_vk.cc index 979b6969df038..d90e5caa4b211 100644 --- a/impeller/renderer/backend/vulkan/command_encoder_vk.cc +++ b/impeller/renderer/backend/vulkan/command_encoder_vk.cc @@ -17,7 +17,7 @@ class TrackedObjectsVK { explicit TrackedObjectsVK( const std::weak_ptr& device_holder, const std::shared_ptr& pool) - : desc_pool_(std::move(device_holder)) { + : desc_pool_(device_holder) { if (!pool) { return; } From df41175f67a7225fc3e00ea77c6081ad9cd7125f Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 19 May 2023 00:05:11 -0700 Subject: [PATCH 115/147] Remove Surface::GetContext --- flow/surface.h | 2 -- shell/gpu/gpu_surface_gl_impeller.cc | 6 ------ shell/gpu/gpu_surface_gl_impeller.h | 3 --- shell/gpu/gpu_surface_gl_skia.cc | 5 ----- shell/gpu/gpu_surface_gl_skia.h | 3 --- shell/gpu/gpu_surface_metal_impeller.h | 3 --- shell/gpu/gpu_surface_metal_impeller.mm | 5 ----- shell/gpu/gpu_surface_metal_skia.h | 3 --- shell/gpu/gpu_surface_metal_skia.mm | 7 +------ shell/gpu/gpu_surface_software.cc | 6 ------ shell/gpu/gpu_surface_software.h | 3 --- shell/gpu/gpu_surface_vulkan.cc | 4 ---- shell/gpu/gpu_surface_vulkan.h | 3 --- shell/gpu/gpu_surface_vulkan_impeller.cc | 6 ------ shell/gpu/gpu_surface_vulkan_impeller.h | 3 --- .../external_view_embedder_unittests.cc | 2 -- 16 files changed, 1 insertion(+), 63 deletions(-) diff --git a/flow/surface.h b/flow/surface.h index 947a6b8603a10..6de182275a90e 100644 --- a/flow/surface.h +++ b/flow/surface.h @@ -40,8 +40,6 @@ class Surface { virtual SkMatrix GetRootTransformation() const = 0; - virtual GrDirectContext* GetContext() = 0; - /// Capture the `SurfaceData` currently present in the surface. /// /// Not guaranteed to work on all setups and not intended to be used in diff --git a/shell/gpu/gpu_surface_gl_impeller.cc b/shell/gpu/gpu_surface_gl_impeller.cc index e024a3f5fb439..e8a75f216dfab 100644 --- a/shell/gpu/gpu_surface_gl_impeller.cc +++ b/shell/gpu/gpu_surface_gl_impeller.cc @@ -134,10 +134,4 @@ SkMatrix GPUSurfaceGLImpeller::GetRootTransformation() const { return {}; } -// |Surface| -GrDirectContext* GPUSurfaceGLImpeller::GetContext() { - // Impeller != Skia. - return nullptr; -} - } // namespace flutter diff --git a/shell/gpu/gpu_surface_gl_impeller.h b/shell/gpu/gpu_surface_gl_impeller.h index aaec9cb13c57d..49a750d5de2a6 100644 --- a/shell/gpu/gpu_surface_gl_impeller.h +++ b/shell/gpu/gpu_surface_gl_impeller.h @@ -41,9 +41,6 @@ class GPUSurfaceGLImpeller final : public Surface { // |Surface| SkMatrix GetRootTransformation() const override; - // |Surface| - GrDirectContext* GetContext() override; - FML_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceGLImpeller); }; diff --git a/shell/gpu/gpu_surface_gl_skia.cc b/shell/gpu/gpu_surface_gl_skia.cc index b5f86d63882f0..2a66d7e8862a5 100644 --- a/shell/gpu/gpu_surface_gl_skia.cc +++ b/shell/gpu/gpu_surface_gl_skia.cc @@ -284,9 +284,4 @@ sk_sp GPUSurfaceGLSkia::AcquireRenderSurface( return onscreen_surface_; } -// |Surface| -GrDirectContext* GPUSurfaceGLSkia::GetContext() { - return context_.get(); -} - } // namespace flutter diff --git a/shell/gpu/gpu_surface_gl_skia.h b/shell/gpu/gpu_surface_gl_skia.h index 6367c8112584d..ba666f35a43cf 100644 --- a/shell/gpu/gpu_surface_gl_skia.h +++ b/shell/gpu/gpu_surface_gl_skia.h @@ -39,9 +39,6 @@ class GPUSurfaceGLSkia : public Surface { // |Surface| SkMatrix GetRootTransformation() const override; - // |Surface| - GrDirectContext* GetContext() override; - private: bool CreateOrUpdateSurfaces(const SkISize& size); diff --git a/shell/gpu/gpu_surface_metal_impeller.h b/shell/gpu/gpu_surface_metal_impeller.h index 89e77094fe32c..905acc850d36e 100644 --- a/shell/gpu/gpu_surface_metal_impeller.h +++ b/shell/gpu/gpu_surface_metal_impeller.h @@ -49,9 +49,6 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUSurfaceMetalImpeller : public Surface { // |Surface| SkMatrix GetRootTransformation() const override; - // |Surface| - GrDirectContext* GetContext() override; - FML_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceMetalImpeller); }; diff --git a/shell/gpu/gpu_surface_metal_impeller.mm b/shell/gpu/gpu_surface_metal_impeller.mm index 6ddfc68bab320..3c4d509298736 100644 --- a/shell/gpu/gpu_surface_metal_impeller.mm +++ b/shell/gpu/gpu_surface_metal_impeller.mm @@ -163,11 +163,6 @@ return {}; } -// |Surface| -GrDirectContext* GPUSurfaceMetalImpeller::GetContext() { - return nullptr; -} - Surface::SurfaceData GPUSurfaceMetalImpeller::GetSurfaceData() const { if (!(last_drawable_ && [last_drawable_ conformsToProtocol:@protocol(CAMetalDrawable)])) { return {}; diff --git a/shell/gpu/gpu_surface_metal_skia.h b/shell/gpu/gpu_surface_metal_skia.h index f828a4e9a9938..38920f02dbdc1 100644 --- a/shell/gpu/gpu_surface_metal_skia.h +++ b/shell/gpu/gpu_surface_metal_skia.h @@ -52,9 +52,6 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUSurfaceMetalSkia : public Surface { // |Surface| SkMatrix GetRootTransformation() const override; - // |Surface| - GrDirectContext* GetContext() override; - // |Surface| std::unique_ptr AcquireFrameFromCAMetalLayer( const SkISize& frame_info); diff --git a/shell/gpu/gpu_surface_metal_skia.mm b/shell/gpu/gpu_surface_metal_skia.mm index 6130265089e55..e5566b42a6f50 100644 --- a/shell/gpu/gpu_surface_metal_skia.mm +++ b/shell/gpu/gpu_surface_metal_skia.mm @@ -96,7 +96,7 @@ [](const SurfaceFrame& surface_frame, DlCanvas* canvas) { return true; }, frame_size); } - sksl_precompiler_->PrecompileKnownSkSLsIfNecessary(GetContext()); + sksl_precompiler_->PrecompileKnownSkSLsIfNecessary(context_.get()); switch (render_target_type_) { case MTLRenderTargetType::kCAMetalLayer: @@ -242,9 +242,4 @@ return {}; } -// |Surface| -GrDirectContext* GPUSurfaceMetalSkia::GetContext() { - return context_.get(); -} - } // namespace flutter diff --git a/shell/gpu/gpu_surface_software.cc b/shell/gpu/gpu_surface_software.cc index d61cf66dc93c3..e4a71ca5355f4 100644 --- a/shell/gpu/gpu_surface_software.cc +++ b/shell/gpu/gpu_surface_software.cc @@ -91,10 +91,4 @@ SkMatrix GPUSurfaceSoftware::GetRootTransformation() const { return matrix; } -// |Surface| -GrDirectContext* GPUSurfaceSoftware::GetContext() { - // There is no GrContext associated with a software surface. - return nullptr; -} - } // namespace flutter diff --git a/shell/gpu/gpu_surface_software.h b/shell/gpu/gpu_surface_software.h index 2c9a2fc73c373..0acd344a9d04d 100644 --- a/shell/gpu/gpu_surface_software.h +++ b/shell/gpu/gpu_surface_software.h @@ -29,9 +29,6 @@ class GPUSurfaceSoftware : public Surface { // |Surface| SkMatrix GetRootTransformation() const override; - // |Surface| - GrDirectContext* GetContext() override; - private: GPUSurfaceSoftwareDelegate* delegate_; // TODO(38466): Refactor GPU surface APIs take into account the fact that an diff --git a/shell/gpu/gpu_surface_vulkan.cc b/shell/gpu/gpu_surface_vulkan.cc index b07b986add019..5997f23a53875 100644 --- a/shell/gpu/gpu_surface_vulkan.cc +++ b/shell/gpu/gpu_surface_vulkan.cc @@ -94,10 +94,6 @@ SkMatrix GPUSurfaceVulkan::GetRootTransformation() const { return matrix; } -GrDirectContext* GPUSurfaceVulkan::GetContext() { - return skia_context_.get(); -} - sk_sp GPUSurfaceVulkan::CreateSurfaceFromVulkanImage( const VkImage image, const VkFormat format, diff --git a/shell/gpu/gpu_surface_vulkan.h b/shell/gpu/gpu_surface_vulkan.h index 7b32f7da9bc40..a7ef9df141981 100644 --- a/shell/gpu/gpu_surface_vulkan.h +++ b/shell/gpu/gpu_surface_vulkan.h @@ -46,9 +46,6 @@ class GPUSurfaceVulkan : public Surface { // |Surface| SkMatrix GetRootTransformation() const override; - // |Surface| - GrDirectContext* GetContext() override; - static SkColorType ColorTypeFromFormat(const VkFormat format); private: diff --git a/shell/gpu/gpu_surface_vulkan_impeller.cc b/shell/gpu/gpu_surface_vulkan_impeller.cc index 81ee597e94e1d..16aadcfa85817 100644 --- a/shell/gpu/gpu_surface_vulkan_impeller.cc +++ b/shell/gpu/gpu_surface_vulkan_impeller.cc @@ -104,10 +104,4 @@ SkMatrix GPUSurfaceVulkanImpeller::GetRootTransformation() const { return {}; } -// |Surface| -GrDirectContext* GPUSurfaceVulkanImpeller::GetContext() { - // Impeller != Skia. - return nullptr; -} - } // namespace flutter diff --git a/shell/gpu/gpu_surface_vulkan_impeller.h b/shell/gpu/gpu_surface_vulkan_impeller.h index d3421fa1d834b..5f6aee9f13530 100644 --- a/shell/gpu/gpu_surface_vulkan_impeller.h +++ b/shell/gpu/gpu_surface_vulkan_impeller.h @@ -38,9 +38,6 @@ class GPUSurfaceVulkanImpeller final : public Surface { // |Surface| SkMatrix GetRootTransformation() const override; - // |Surface| - GrDirectContext* GetContext() override; - FML_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceVulkanImpeller); }; 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 c3800020b79ec..1d9792f92b4f7 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 @@ -57,8 +57,6 @@ class SurfaceMock : public Surface { (override)); MOCK_METHOD(SkMatrix, GetRootTransformation, (), (const, override)); - - MOCK_METHOD(GrDirectContext*, GetContext, (), (override)); }; fml::RefPtr GetThreadMergerFromPlatformThread( From 6d33a118bb220ec964c4dedbf5db848564d4a08a Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 19 May 2023 14:04:59 -0700 Subject: [PATCH 116/147] Update shell_test_platform_view_vulkan.h --- shell/common/shell_test_platform_view_vulkan.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/shell/common/shell_test_platform_view_vulkan.h b/shell/common/shell_test_platform_view_vulkan.h index 54067aa11de76..8775f0ef4e8c1 100644 --- a/shell/common/shell_test_platform_view_vulkan.h +++ b/shell/common/shell_test_platform_view_vulkan.h @@ -35,8 +35,6 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { ~OffScreenContext(); - sk_sp GetContext() { return context_; } - private: fml::RefPtr vk_; std::unique_ptr application_; From 6eef03009819e2bb304e8c32db2a40c7546527e0 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 19 May 2023 14:11:18 -0700 Subject: [PATCH 117/147] Compile --- shell/common/shell_test_platform_view_vulkan.cc | 6 +----- shell/common/shell_test_platform_view_vulkan.h | 5 ++--- .../darwin/macos/framework/Source/FlutterEngineTest.mm | 4 ++-- shell/platform/fuchsia/flutter/surface.cc | 5 ----- shell/platform/fuchsia/flutter/surface.h | 3 --- 5 files changed, 5 insertions(+), 18 deletions(-) diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index 60bfd7ba49697..ee8132f4d2283 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -204,7 +204,7 @@ ShellTestPlatformViewVulkan::OffScreenSurface::OffScreenSurface( ShellTestPlatformViewVulkan::OffScreenSurface::~OffScreenSurface() {} bool ShellTestPlatformViewVulkan::OffScreenSurface::IsValid() { - return GetContext(); + return offscreen_context_->GetContext(); } std::unique_ptr @@ -229,10 +229,6 @@ ShellTestPlatformViewVulkan::OffScreenSurface::AcquireFrame( /*frame_size=*/SkISize::Make(800, 600)); } -GrDirectContext* ShellTestPlatformViewVulkan::OffScreenSurface::GetContext() { - return offscreen_context_->GetContext().get(); -} - SkMatrix ShellTestPlatformViewVulkan::OffScreenSurface::GetRootTransformation() const { SkMatrix matrix; diff --git a/shell/common/shell_test_platform_view_vulkan.h b/shell/common/shell_test_platform_view_vulkan.h index 8775f0ef4e8c1..6ffa904dd1afc 100644 --- a/shell/common/shell_test_platform_view_vulkan.h +++ b/shell/common/shell_test_platform_view_vulkan.h @@ -35,6 +35,8 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { ~OffScreenContext(); + sk_sp GetContext() { return context_; } + private: fml::RefPtr vk_; std::unique_ptr application_; @@ -91,9 +93,6 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { // |Surface| SkMatrix GetRootTransformation() const override; - // |Surface| - GrDirectContext* GetContext() override; - private: std::shared_ptr shell_test_external_view_embedder_; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm index 450d5499de259..c1e9ca2412170 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm @@ -830,8 +830,8 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable FlutterEngine* engine = GetFlutterEngine(); auto original_set_viewport_metrics = engine.embedderAPI.SendWindowMetricsEvent; engine.embedderAPI.SendWindowMetricsEvent = MOCK_ENGINE_PROC( - SendWindowMetricsEvent, - ([&updated, &original_set_viewport_metrics](auto engine, auto* window_metrics) { + SendWindowMetricsEvent, ([&updated, &original_set_viewport_metrics]( + auto engine, int64_t view_id, auto* window_metrics) { updated = YES; return original_set_viewport_metrics(engine, window_metrics); })); diff --git a/shell/platform/fuchsia/flutter/surface.cc b/shell/platform/fuchsia/flutter/surface.cc index 13ff5f7c8a110..c0d8136b01404 100644 --- a/shell/platform/fuchsia/flutter/surface.cc +++ b/shell/platform/fuchsia/flutter/surface.cc @@ -40,11 +40,6 @@ std::unique_ptr Surface::AcquireFrame( size); } -// |flutter::Surface| -GrDirectContext* Surface::GetContext() { - return gr_context_; -} - // |flutter::Surface| SkMatrix Surface::GetRootTransformation() const { // This backend does not support delegating to the underlying platform to diff --git a/shell/platform/fuchsia/flutter/surface.h b/shell/platform/fuchsia/flutter/surface.h index b3e6621db5f09..a2eea7b28de86 100644 --- a/shell/platform/fuchsia/flutter/surface.h +++ b/shell/platform/fuchsia/flutter/surface.h @@ -34,9 +34,6 @@ class Surface final : public flutter::Surface { int64_t view_id, const SkISize& size) override; - // |flutter::Surface| - GrDirectContext* GetContext() override; - // |flutter::Surface| SkMatrix GetRootTransformation() const override; From 9b8ec84b50d9236a0398235959b3e9e80d97b1f2 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 19 May 2023 15:18:32 -0700 Subject: [PATCH 118/147] Compile --- shell/common/shell_test_platform_view_vulkan.cc | 6 +++++- shell/common/shell_test_platform_view_vulkan.h | 2 ++ .../darwin/macos/framework/Source/FlutterEngineTest.mm | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index ee8132f4d2283..0c10083be8abd 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -204,7 +204,7 @@ ShellTestPlatformViewVulkan::OffScreenSurface::OffScreenSurface( ShellTestPlatformViewVulkan::OffScreenSurface::~OffScreenSurface() {} bool ShellTestPlatformViewVulkan::OffScreenSurface::IsValid() { - return offscreen_context_->GetContext(); + return GetContext(); } std::unique_ptr @@ -229,6 +229,10 @@ ShellTestPlatformViewVulkan::OffScreenSurface::AcquireFrame( /*frame_size=*/SkISize::Make(800, 600)); } +GrDirectContext* ShellTestPlatformViewVulkan::OffScreenSurface::GetContext() { + return offscreen_context_->GetContext(); +} + SkMatrix ShellTestPlatformViewVulkan::OffScreenSurface::GetRootTransformation() const { SkMatrix matrix; diff --git a/shell/common/shell_test_platform_view_vulkan.h b/shell/common/shell_test_platform_view_vulkan.h index 6ffa904dd1afc..a2c762259eb58 100644 --- a/shell/common/shell_test_platform_view_vulkan.h +++ b/shell/common/shell_test_platform_view_vulkan.h @@ -94,6 +94,8 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { SkMatrix GetRootTransformation() const override; private: + GrDirectContext* GetContext(); + std::shared_ptr shell_test_external_view_embedder_; std::shared_ptr offscreen_context_; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm index c1e9ca2412170..992704f2aba23 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm @@ -833,7 +833,7 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable SendWindowMetricsEvent, ([&updated, &original_set_viewport_metrics]( auto engine, int64_t view_id, auto* window_metrics) { updated = YES; - return original_set_viewport_metrics(engine, window_metrics); + return original_set_viewport_metrics(engine, view_id, window_metrics); })); EXPECT_TRUE([engine runWithEntrypoint:@"main"]); From 9e592956b75167d3e71fb0e293c43c3941986fa0 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 19 May 2023 15:26:18 -0700 Subject: [PATCH 119/147] Compile --- shell/common/shell_test_platform_view_vulkan.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index 0c10083be8abd..60bfd7ba49697 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -230,7 +230,7 @@ ShellTestPlatformViewVulkan::OffScreenSurface::AcquireFrame( } GrDirectContext* ShellTestPlatformViewVulkan::OffScreenSurface::GetContext() { - return offscreen_context_->GetContext(); + return offscreen_context_->GetContext().get(); } SkMatrix ShellTestPlatformViewVulkan::OffScreenSurface::GetRootTransformation() From 931cb91bb93a3e8b4ee7d606065be903212bd947 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Sat, 20 May 2023 22:17:17 -0700 Subject: [PATCH 120/147] Fix fuchsia compile --- shell/platform/fuchsia/flutter/surface.cc | 4 +--- shell/platform/fuchsia/flutter/surface.h | 4 ---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/shell/platform/fuchsia/flutter/surface.cc b/shell/platform/fuchsia/flutter/surface.cc index c0d8136b01404..5b8e1d29623fe 100644 --- a/shell/platform/fuchsia/flutter/surface.cc +++ b/shell/platform/fuchsia/flutter/surface.cc @@ -16,9 +16,7 @@ namespace flutter_runner { Surface::Surface(std::string debug_label, std::shared_ptr view_embedder, GrDirectContext* gr_context) - : debug_label_(std::move(debug_label)), - view_embedder_(view_embedder), - gr_context_(gr_context) {} + : debug_label_(std::move(debug_label)) {} Surface::~Surface() = default; diff --git a/shell/platform/fuchsia/flutter/surface.h b/shell/platform/fuchsia/flutter/surface.h index a2eea7b28de86..acfaff43659c0 100644 --- a/shell/platform/fuchsia/flutter/surface.h +++ b/shell/platform/fuchsia/flutter/surface.h @@ -22,10 +22,6 @@ class Surface final : public flutter::Surface { ~Surface() override; private: - const std::string debug_label_; - std::shared_ptr view_embedder_; - GrDirectContext* gr_context_; - // |flutter::Surface| bool IsValid() override; From 70d4fc68a47e01d139f495335c6f0390a338a1d3 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Sun, 21 May 2023 02:08:28 -0700 Subject: [PATCH 121/147] Lint --- impeller/renderer/backend/vulkan/pipeline_vk.cc | 2 +- impeller/renderer/backend/vulkan/shader_library_vk.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/impeller/renderer/backend/vulkan/pipeline_vk.cc b/impeller/renderer/backend/vulkan/pipeline_vk.cc index bc971cd7695a9..c1ca1e3084765 100644 --- a/impeller/renderer/backend/vulkan/pipeline_vk.cc +++ b/impeller/renderer/backend/vulkan/pipeline_vk.cc @@ -14,7 +14,7 @@ PipelineVK::PipelineVK(std::weak_ptr device_holder, vk::UniquePipelineLayout layout, vk::UniqueDescriptorSetLayout descriptor_set_layout) : Pipeline(std::move(library), desc), - device_holder_(device_holder), + device_holder_(std::move(device_holder)), pipeline_(std::move(pipeline)), render_pass_(std::move(render_pass)), layout_(std::move(layout)), diff --git a/impeller/renderer/backend/vulkan/shader_library_vk.cc b/impeller/renderer/backend/vulkan/shader_library_vk.cc index 3988e156a6e51..ea92c36258b94 100644 --- a/impeller/renderer/backend/vulkan/shader_library_vk.cc +++ b/impeller/renderer/backend/vulkan/shader_library_vk.cc @@ -55,7 +55,7 @@ static std::string VKShaderNameToShaderKeyName(const std::string& name, ShaderLibraryVK::ShaderLibraryVK( std::weak_ptr device_holder, const std::vector>& shader_libraries_data) - : device_holder_(device_holder) { + : device_holder_(std::move(device_holder)) { TRACE_EVENT0("impeller", "CreateShaderLibrary"); bool success = true; auto iterator = [&](auto type, // From 3bcd96a552a85fd351ce787b7f6037de31809c16 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Sun, 21 May 2023 21:19:11 -0700 Subject: [PATCH 122/147] Move back debug_label_ --- shell/platform/fuchsia/flutter/surface.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shell/platform/fuchsia/flutter/surface.h b/shell/platform/fuchsia/flutter/surface.h index acfaff43659c0..e6876392c7499 100644 --- a/shell/platform/fuchsia/flutter/surface.h +++ b/shell/platform/fuchsia/flutter/surface.h @@ -22,6 +22,8 @@ class Surface final : public flutter::Surface { ~Surface() override; private: + const std::string debug_label_; + // |flutter::Surface| bool IsValid() override; From b9226c9ae7efebd80bbb52765a31c057956d917e Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 22 May 2023 15:18:09 -0700 Subject: [PATCH 123/147] Basic test --- ci/licenses_golden/licenses_flutter | 4 ++++ shell/platform/darwin/macos/BUILD.gn | 1 + .../Source/FlutterThreadSynchronizer.h | 13 ++++++++++ .../Source/FlutterThreadSynchronizer.mm | 24 +++++++++++++++++-- 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index c673eb20437be..efbcf647cab2a 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -2720,6 +2720,9 @@ ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTex ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObjectTest.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextureRegistrar.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextureRegistrar.mm + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterUmbrellaImportTests.m + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterView.mm + ../../../flutter/LICENSE @@ -5362,6 +5365,7 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextu FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextureRegistrar.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterUmbrellaImportTests.m FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterView.mm diff --git a/shell/platform/darwin/macos/BUILD.gn b/shell/platform/darwin/macos/BUILD.gn index edebb4ebfd3b1..16ffb24cd3f19 100644 --- a/shell/platform/darwin/macos/BUILD.gn +++ b/shell/platform/darwin/macos/BUILD.gn @@ -182,6 +182,7 @@ executable("flutter_desktop_darwin_unittests") { "framework/Source/FlutterSurfaceManagerTest.mm", "framework/Source/FlutterTextInputPluginTest.mm", "framework/Source/FlutterTextInputSemanticsObjectTest.mm", + "framework/Source/FlutterThreadSynchronizerTest.mm", "framework/Source/FlutterViewControllerTest.mm", "framework/Source/FlutterViewControllerTestUtils.h", "framework/Source/FlutterViewControllerTestUtils.mm", diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h index 5cc399cb33279..4f0e1f7ad14ef 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h @@ -1,3 +1,7 @@ +// 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. + #import /** @@ -5,6 +9,8 @@ */ @interface FlutterThreadSynchronizer : NSObject +- (nullable instancetype)init; + /** * Blocks current thread until there is frame available. * Used in FlutterEngineTest. @@ -42,3 +48,10 @@ - (void)shutdown; @end + +@interface FlutterThreadSynchronizer (Test) + +- (nullable instancetype)initWithMainQueue:(nonnull dispatch_queue_t)queue + mainThread:(nonnull NSThread*)thread; + +@end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm index 82f65f932e61d..43ba3853ff8fe 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm @@ -1,3 +1,7 @@ +// 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. + #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h" #import @@ -10,6 +14,8 @@ #import "flutter/fml/synchronization/waitable_event.h" @interface FlutterThreadSynchronizer () { + dispatch_queue_t _queue; + __weak NSThread* _queueThread; std::mutex _mutex; BOOL _shuttingDown; std::unordered_map _contentSizes; @@ -39,6 +45,20 @@ - (BOOL)someViewsHaveFrame; @implementation FlutterThreadSynchronizer +- (instancetype)init { + return [self initWithMainQueue:dispatch_get_main_queue() mainThread:[NSThread mainThread]]; +} + +- (instancetype)initWithMainQueue:(dispatch_queue_t)queue + mainThread:(NSThread*)thread { + self = [super init]; + if (self != nil) { + _queue = queue; + _queueThread = thread; + } + return self; +} + - (BOOL)allViewsHaveFrame { for (auto const& [viewId, contentSize] : _contentSizes) { if (CGSizeEqualToSize(contentSize, CGSizeZero)) { @@ -58,7 +78,7 @@ - (BOOL)someViewsHaveFrame { } - (void)drain { - FML_DCHECK([NSThread isMainThread]); + FML_DCHECK([NSThread currentThread] == _queueThread); [CATransaction begin]; [CATransaction setDisableActions:YES]; @@ -136,7 +156,7 @@ - (void)performCommitForView:(int64_t)viewId if (_beginResizeWaiting) { _condBlockBeginResize.notify_all(); } else { - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_async(_queue, ^{ std::unique_lock lock(_mutex); [self drain]; }); From 57e159f9bde5557a87a1cbe2316ca3c9e2165161 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 22 May 2023 17:31:37 -0700 Subject: [PATCH 124/147] Use set specific to check main --- .../Source/FlutterThreadSynchronizer.h | 3 +-- .../Source/FlutterThreadSynchronizer.mm | 22 ++++++++++++++----- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h index 4f0e1f7ad14ef..6c424bf5d9fb8 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h @@ -51,7 +51,6 @@ @interface FlutterThreadSynchronizer (Test) -- (nullable instancetype)initWithMainQueue:(nonnull dispatch_queue_t)queue - mainThread:(nonnull NSThread*)thread; +- (nullable instancetype)initWithMainQueue:(nonnull dispatch_queue_t)queue; @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm index 43ba3853ff8fe..4bdc672e79f5e 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm @@ -13,9 +13,10 @@ #import "flutter/fml/logging.h" #import "flutter/fml/synchronization/waitable_event.h" +static constexpr intptr_t _kMainQueueContext = 0x1234ABCD; + @interface FlutterThreadSynchronizer () { dispatch_queue_t _queue; - __weak NSThread* _queueThread; std::mutex _mutex; BOOL _shuttingDown; std::unordered_map _contentSizes; @@ -41,24 +42,29 @@ - (BOOL)allViewsHaveFrame; */ - (BOOL)someViewsHaveFrame; +- (const void*)mainQueueKey; + @end @implementation FlutterThreadSynchronizer - (instancetype)init { - return [self initWithMainQueue:dispatch_get_main_queue() mainThread:[NSThread mainThread]]; + return [self initWithMainQueue:dispatch_get_main_queue()]; } -- (instancetype)initWithMainQueue:(dispatch_queue_t)queue - mainThread:(NSThread*)thread { +- (instancetype)initWithMainQueue:(dispatch_queue_t)queue { self = [super init]; if (self != nil) { _queue = queue; - _queueThread = thread; + dispatch_queue_set_specific(_queue, [self mainQueueKey], (void*)_kMainQueueContext, NULL); } return self; } +- (void)dealloc { + dispatch_queue_set_specific(_queue, [self mainQueueKey], NULL, NULL); +} + - (BOOL)allViewsHaveFrame { for (auto const& [viewId, contentSize] : _contentSizes) { if (CGSizeEqualToSize(contentSize, CGSizeZero)) { @@ -78,7 +84,7 @@ - (BOOL)someViewsHaveFrame { } - (void)drain { - FML_DCHECK([NSThread currentThread] == _queueThread); + FML_DCHECK(dispatch_get_specific([self mainQueueKey]) == (void*)_kMainQueueContext); [CATransaction begin]; [CATransaction setDisableActions:YES]; @@ -180,4 +186,8 @@ - (void)shutdown { [self drain]; } +- (const void*)mainQueueKey { + return (__bridge const void*)self; +} + @end From 85b1262719917475f0b04632c0f0d3605783688c Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 22 May 2023 17:50:49 -0700 Subject: [PATCH 125/147] Use set specific to check main --- .../framework/Source/FlutterThreadSynchronizer.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h index 6c424bf5d9fb8..3141b7b001cb6 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h @@ -12,13 +12,7 @@ - (nullable instancetype)init; /** - * Blocks current thread until there is frame available. - * Used in FlutterEngineTest. - */ -- (void)blockUntilFrameAvailable; - -/** - * Called from platform thread. Blocks until commit with given size (or empty) + * Called from platform thread. Blocks until a commit with given size (or empty) * is requested. */ - (void)beginResizeForView:(int64_t)viewId @@ -53,4 +47,10 @@ - (nullable instancetype)initWithMainQueue:(nonnull dispatch_queue_t)queue; +/** + * Blocks current thread until there is frame available. + * Used in FlutterEngineTest. + */ +- (void)blockUntilFrameAvailable; + @end From 23c3851c788ce2efd9640bb373f3d30acad3e246 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 23 May 2023 01:13:50 -0700 Subject: [PATCH 126/147] FinishResizingOnlyWhenCommittingMatchingSize --- .../Source/FlutterThreadSynchronizer.h | 2 + .../Source/FlutterThreadSynchronizer.mm | 22 +-- .../Source/FlutterThreadSynchronizerTest.mm | 161 ++++++++++++++++++ 3 files changed, 170 insertions(+), 15 deletions(-) create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h index 3141b7b001cb6..6c9279e8d0e85 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h @@ -47,6 +47,8 @@ - (nullable instancetype)initWithMainQueue:(nonnull dispatch_queue_t)queue; +- (BOOL)isWaitingWhenMutexIsAvailable; + /** * Blocks current thread until there is frame available. * Used in FlutterEngineTest. diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm index 4bdc672e79f5e..82229bbd5fa5f 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm @@ -13,10 +13,8 @@ #import "flutter/fml/logging.h" #import "flutter/fml/synchronization/waitable_event.h" -static constexpr intptr_t _kMainQueueContext = 0x1234ABCD; - @interface FlutterThreadSynchronizer () { - dispatch_queue_t _queue; + dispatch_queue_t _mainQueue; std::mutex _mutex; BOOL _shuttingDown; std::unordered_map _contentSizes; @@ -42,8 +40,6 @@ - (BOOL)allViewsHaveFrame; */ - (BOOL)someViewsHaveFrame; -- (const void*)mainQueueKey; - @end @implementation FlutterThreadSynchronizer @@ -55,16 +51,11 @@ - (instancetype)init { - (instancetype)initWithMainQueue:(dispatch_queue_t)queue { self = [super init]; if (self != nil) { - _queue = queue; - dispatch_queue_set_specific(_queue, [self mainQueueKey], (void*)_kMainQueueContext, NULL); + _mainQueue = queue; } return self; } -- (void)dealloc { - dispatch_queue_set_specific(_queue, [self mainQueueKey], NULL, NULL); -} - - (BOOL)allViewsHaveFrame { for (auto const& [viewId, contentSize] : _contentSizes) { if (CGSizeEqualToSize(contentSize, CGSizeZero)) { @@ -84,7 +75,7 @@ - (BOOL)someViewsHaveFrame { } - (void)drain { - FML_DCHECK(dispatch_get_specific([self mainQueueKey]) == (void*)_kMainQueueContext); + dispatch_assert_queue(_mainQueue); [CATransaction begin]; [CATransaction setDisableActions:YES]; @@ -162,7 +153,7 @@ - (void)performCommitForView:(int64_t)viewId if (_beginResizeWaiting) { _condBlockBeginResize.notify_all(); } else { - dispatch_async(_queue, ^{ + dispatch_async(_mainQueue, ^{ std::unique_lock lock(_mutex); [self drain]; }); @@ -186,8 +177,9 @@ - (void)shutdown { [self drain]; } -- (const void*)mainQueueKey { - return (__bridge const void*)self; +- (BOOL)isWaitingWhenMutexIsAvailable { + std::unique_lock lock(_mutex); + return _beginResizeWaiting; } @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm new file mode 100644 index 0000000000000..0f886c77ab3b7 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm @@ -0,0 +1,161 @@ +// 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. + +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h" + +#import +#import "flutter/testing/testing.h" +#import "flutter/fml/synchronization/waitable_event.h" + +namespace flutter::testing { + +namespace { + +} // namespace + +} // namespace flutter::testing + +@interface FlutterThreadSynchronizerTestScaffold : NSObject + +@property(nonatomic, readonly, nonnull) FlutterThreadSynchronizer* synchronizer; + +- (nullable instancetype)init; +- (void)dispatchMainTask:(nonnull void(^)())task; +- (void)dispatchRenderTask:(nonnull void(^)())task; +- (void)joinMain; +- (void)joinRender; +@end + +@implementation FlutterThreadSynchronizerTestScaffold { + dispatch_queue_t _mainQueue; + std::shared_ptr _mainLatch; + + dispatch_queue_t _renderQueue; + std::shared_ptr _renderLatch; + + FlutterThreadSynchronizer* _synchronizer; +} + +@synthesize synchronizer = _synchronizer; + +- (nullable instancetype)init { + self = [super init]; + if (self != nil) { + _mainQueue = dispatch_queue_create("MAIN", DISPATCH_QUEUE_SERIAL); + _renderQueue = dispatch_queue_create("RENDER", DISPATCH_QUEUE_SERIAL); + _synchronizer = [[FlutterThreadSynchronizer alloc] initWithMainQueue:_mainQueue]; + } + return self; +} + +- (void)dispatchMainTask:(nonnull void(^)())task { + dispatch_async(_mainQueue, task); +} + +- (void)dispatchRenderTask:(nonnull void(^)())task { + dispatch_async(_renderQueue, task); +} + +- (void)joinMain { + fml::AutoResetWaitableEvent latch; + fml::AutoResetWaitableEvent* pLatch = &latch; + dispatch_async(_mainQueue, ^{ pLatch->Signal(); }); + latch.Wait(); +} + +- (void)joinRender { + fml::AutoResetWaitableEvent latch; + fml::AutoResetWaitableEvent* pLatch = &latch; + dispatch_async(_renderQueue, ^{ pLatch->Signal(); }); + latch.Wait(); +} + +@end + +TEST(FlutterThreadSynchronizerTest, FinishResizingImmediatelyWhenSizeMatches) { + FlutterThreadSynchronizerTestScaffold* scaffold = [[FlutterThreadSynchronizerTestScaffold alloc] init]; + FlutterThreadSynchronizer* synchronizer = scaffold.synchronizer; + + [synchronizer registerView:1]; + + // Initial resize: does not block until the first frame. + __block int notifiedResize = 0; + [scaffold dispatchMainTask:^{ + [synchronizer beginResizeForView:1 size:CGSize{5, 5} notify:^{ + notifiedResize += 1; + }]; + }]; + EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); + [scaffold joinMain]; + EXPECT_EQ(notifiedResize, 1); + + // Still does not block. + [scaffold dispatchMainTask:^{ + [synchronizer beginResizeForView:1 size:CGSize{7, 7} notify:^{ + notifiedResize += 1; + }]; + }]; + EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); + [scaffold joinMain]; + EXPECT_EQ(notifiedResize, 2); + + // First frame + __block int notifiedCommit = 0; + [scaffold dispatchRenderTask:^{ + [synchronizer performCommitForView:1 size:CGSize{7, 7} notify:^{ + notifiedCommit += 1; + }]; + }]; + EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); + [scaffold joinRender]; + EXPECT_EQ(notifiedCommit, 1); +} + +TEST(FlutterThreadSynchronizerTest, FinishResizingOnlyWhenCommittingMatchingSize) { + FlutterThreadSynchronizerTestScaffold* scaffold = [[FlutterThreadSynchronizerTestScaffold alloc] init]; + FlutterThreadSynchronizer* synchronizer = scaffold.synchronizer; + fml::AutoResetWaitableEvent begunResizingLatch; + __block fml::AutoResetWaitableEvent* begunResizing = &begunResizingLatch; + + [synchronizer registerView:1]; + + // Initial resize: does not block until the first frame. + [scaffold dispatchMainTask:^{ + [synchronizer beginResizeForView:1 size:CGSize{5, 5} notify:^{}]; + }]; + [scaffold joinMain]; + EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); + + // First frame. + [scaffold dispatchRenderTask:^{ + [synchronizer performCommitForView:1 size:CGSize{5, 5} notify:^{}]; + }]; + [scaffold joinRender]; + EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); + + // Resize to (7, 7): blocks since there's a frame until the next frame. + [scaffold dispatchMainTask:^{ + [synchronizer beginResizeForView:1 size:CGSize{7, 7} notify:^{ + begunResizing->Signal(); + }]; + }]; + begunResizing->Wait(); + EXPECT_TRUE([synchronizer isWaitingWhenMutexIsAvailable]); + + // Render with old size. + [scaffold dispatchRenderTask:^{ + [synchronizer performCommitForView:1 size:CGSize{5, 5} notify:^{}]; + }]; + [scaffold joinRender]; + EXPECT_TRUE([synchronizer isWaitingWhenMutexIsAvailable]); + + // Render with new size. + [scaffold dispatchRenderTask:^{ + [synchronizer performCommitForView:1 size:CGSize{7, 7} notify:^{}]; + }]; + [scaffold joinRender]; + EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); + + [scaffold joinMain]; +} From b3cbb05c4ac492aa621272e7ca12c83f76808436 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 23 May 2023 14:30:30 -0700 Subject: [PATCH 127/147] FinishResizingWhenShuttingDown --- .../Source/FlutterThreadSynchronizer.h | 8 +- .../Source/FlutterThreadSynchronizer.mm | 3 + .../Source/FlutterThreadSynchronizerTest.mm | 143 ++++++++++++++---- 3 files changed, 123 insertions(+), 31 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h index 6c9279e8d0e85..74653956354ed 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h @@ -37,7 +37,9 @@ - (void)deregisterView:(int64_t)viewId; /** - * Called when shutting down. Unblocks everything and prevents any further synchronization. + * Called from platform thread when shutting down. + * + * Prevents any further synchronization and no longer blocks any threads. */ - (void)shutdown; @@ -47,6 +49,10 @@ - (nullable instancetype)initWithMainQueue:(nonnull dispatch_queue_t)queue; +/** + * Blocks current thread until the mutex is available, then return whether the + * synchronizer is waiting for a correct commit during resizing. + */ - (BOOL)isWaitingWhenMutexIsAvailable; /** diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm index 82229bbd5fa5f..a0561e4d30e8e 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm @@ -102,6 +102,7 @@ - (void)blockUntilFrameAvailable { - (void)beginResizeForView:(int64_t)viewId size:(CGSize)size notify:(nonnull dispatch_block_t)notify { + dispatch_assert_queue(_mainQueue); std::unique_lock lock(_mutex); if (![self allViewsHaveFrame] || _shuttingDown) { @@ -136,6 +137,7 @@ - (void)beginResizeForView:(int64_t)viewId - (void)performCommitForView:(int64_t)viewId size:(CGSize)size notify:(nonnull dispatch_block_t)notify { + dispatch_assert_queue_not(_mainQueue); fml::AutoResetWaitableEvent event; { std::unique_lock lock(_mutex); @@ -171,6 +173,7 @@ - (void)deregisterView:(int64_t)viewId { } - (void)shutdown { + dispatch_assert_queue(_mainQueue); std::unique_lock lock(_mutex); _shuttingDown = YES; _condBlockBeginResize.notify_all(); diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm index 0f886c77ab3b7..ef97b62a3785a 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm @@ -5,14 +5,12 @@ #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h" #import -#import "flutter/testing/testing.h" #import "flutter/fml/synchronization/waitable_event.h" +#import "flutter/testing/testing.h" namespace flutter::testing { -namespace { - -} // namespace +namespace {} // namespace } // namespace flutter::testing @@ -21,8 +19,8 @@ @interface FlutterThreadSynchronizerTestScaffold : NSObject @property(nonatomic, readonly, nonnull) FlutterThreadSynchronizer* synchronizer; - (nullable instancetype)init; -- (void)dispatchMainTask:(nonnull void(^)())task; -- (void)dispatchRenderTask:(nonnull void(^)())task; +- (void)dispatchMainTask:(nonnull void (^)())task; +- (void)dispatchRenderTask:(nonnull void (^)())task; - (void)joinMain; - (void)joinRender; @end @@ -49,32 +47,37 @@ - (nullable instancetype)init { return self; } -- (void)dispatchMainTask:(nonnull void(^)())task { +- (void)dispatchMainTask:(nonnull void (^)())task { dispatch_async(_mainQueue, task); } -- (void)dispatchRenderTask:(nonnull void(^)())task { +- (void)dispatchRenderTask:(nonnull void (^)())task { dispatch_async(_renderQueue, task); } - (void)joinMain { fml::AutoResetWaitableEvent latch; fml::AutoResetWaitableEvent* pLatch = &latch; - dispatch_async(_mainQueue, ^{ pLatch->Signal(); }); + dispatch_async(_mainQueue, ^{ + pLatch->Signal(); + }); latch.Wait(); } - (void)joinRender { fml::AutoResetWaitableEvent latch; fml::AutoResetWaitableEvent* pLatch = &latch; - dispatch_async(_renderQueue, ^{ pLatch->Signal(); }); + dispatch_async(_renderQueue, ^{ + pLatch->Signal(); + }); latch.Wait(); } @end TEST(FlutterThreadSynchronizerTest, FinishResizingImmediatelyWhenSizeMatches) { - FlutterThreadSynchronizerTestScaffold* scaffold = [[FlutterThreadSynchronizerTestScaffold alloc] init]; + FlutterThreadSynchronizerTestScaffold* scaffold = + [[FlutterThreadSynchronizerTestScaffold alloc] init]; FlutterThreadSynchronizer* synchronizer = scaffold.synchronizer; [synchronizer registerView:1]; @@ -82,9 +85,11 @@ - (void)joinRender { // Initial resize: does not block until the first frame. __block int notifiedResize = 0; [scaffold dispatchMainTask:^{ - [synchronizer beginResizeForView:1 size:CGSize{5, 5} notify:^{ - notifiedResize += 1; - }]; + [synchronizer beginResizeForView:1 + size:CGSize{5, 5} + notify:^{ + notifiedResize += 1; + }]; }]; EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); [scaffold joinMain]; @@ -92,9 +97,11 @@ - (void)joinRender { // Still does not block. [scaffold dispatchMainTask:^{ - [synchronizer beginResizeForView:1 size:CGSize{7, 7} notify:^{ - notifiedResize += 1; - }]; + [synchronizer beginResizeForView:1 + size:CGSize{7, 7} + notify:^{ + notifiedResize += 1; + }]; }]; EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); [scaffold joinMain]; @@ -103,9 +110,11 @@ - (void)joinRender { // First frame __block int notifiedCommit = 0; [scaffold dispatchRenderTask:^{ - [synchronizer performCommitForView:1 size:CGSize{7, 7} notify:^{ - notifiedCommit += 1; - }]; + [synchronizer performCommitForView:1 + size:CGSize{7, 7} + notify:^{ + notifiedCommit += 1; + }]; }]; EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); [scaffold joinRender]; @@ -113,49 +122,123 @@ - (void)joinRender { } TEST(FlutterThreadSynchronizerTest, FinishResizingOnlyWhenCommittingMatchingSize) { - FlutterThreadSynchronizerTestScaffold* scaffold = [[FlutterThreadSynchronizerTestScaffold alloc] init]; + FlutterThreadSynchronizerTestScaffold* scaffold = + [[FlutterThreadSynchronizerTestScaffold alloc] init]; FlutterThreadSynchronizer* synchronizer = scaffold.synchronizer; + // A latch to ensure that a beginResizeForView: call has at least executed + // something, so that the isWaitingWhenMutexIsAvailable: call correctly stops + // at either when beginResizeForView: finishes or waits half way. fml::AutoResetWaitableEvent begunResizingLatch; - __block fml::AutoResetWaitableEvent* begunResizing = &begunResizingLatch; + fml::AutoResetWaitableEvent* begunResizing = &begunResizingLatch; [synchronizer registerView:1]; // Initial resize: does not block until the first frame. [scaffold dispatchMainTask:^{ - [synchronizer beginResizeForView:1 size:CGSize{5, 5} notify:^{}]; + [synchronizer beginResizeForView:1 + size:CGSize{5, 5} + notify:^{ + }]; }]; [scaffold joinMain]; EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); // First frame. [scaffold dispatchRenderTask:^{ - [synchronizer performCommitForView:1 size:CGSize{5, 5} notify:^{}]; + [synchronizer performCommitForView:1 + size:CGSize{5, 5} + notify:^{ + }]; }]; [scaffold joinRender]; EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); - // Resize to (7, 7): blocks since there's a frame until the next frame. + // Resize to (7, 7): blocks until the next frame. [scaffold dispatchMainTask:^{ - [synchronizer beginResizeForView:1 size:CGSize{7, 7} notify:^{ - begunResizing->Signal(); - }]; + [synchronizer beginResizeForView:1 + size:CGSize{7, 7} + notify:^{ + begunResizing->Signal(); + }]; }]; begunResizing->Wait(); EXPECT_TRUE([synchronizer isWaitingWhenMutexIsAvailable]); // Render with old size. [scaffold dispatchRenderTask:^{ - [synchronizer performCommitForView:1 size:CGSize{5, 5} notify:^{}]; + [synchronizer performCommitForView:1 + size:CGSize{5, 5} + notify:^{ + }]; }]; [scaffold joinRender]; EXPECT_TRUE([synchronizer isWaitingWhenMutexIsAvailable]); // Render with new size. [scaffold dispatchRenderTask:^{ - [synchronizer performCommitForView:1 size:CGSize{7, 7} notify:^{}]; + [synchronizer performCommitForView:1 + size:CGSize{7, 7} + notify:^{ + }]; }]; [scaffold joinRender]; EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); [scaffold joinMain]; } + +TEST(FlutterThreadSynchronizerTest, FinishResizingWhenShuttingDown) { + FlutterThreadSynchronizerTestScaffold* scaffold = + [[FlutterThreadSynchronizerTestScaffold alloc] init]; + FlutterThreadSynchronizer* synchronizer = scaffold.synchronizer; + fml::AutoResetWaitableEvent begunResizingLatch; + fml::AutoResetWaitableEvent* begunResizing = &begunResizingLatch; + + [synchronizer registerView:1]; + + // Initial resize + [scaffold dispatchMainTask:^{ + [synchronizer beginResizeForView:1 + size:CGSize{5, 5} + notify:^{ + }]; + }]; + [scaffold joinMain]; + EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); + + // Push a frame. + [scaffold dispatchRenderTask:^{ + [synchronizer performCommitForView:1 + size:CGSize{5, 5} + notify:^{ + }]; + }]; + [scaffold joinRender]; + EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); + + [scaffold dispatchMainTask:^{ + [synchronizer shutdown]; + }]; + + // Resize to (7, 7). Should not block any frames since it has shut down. + [scaffold dispatchMainTask:^{ + [synchronizer beginResizeForView:1 + size:CGSize{7, 7} + notify:^{ + begunResizing->Signal(); + }]; + }]; + begunResizing->Wait(); + EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); + [scaffold joinMain]; + + // All further calls should be unblocking. + [scaffold dispatchRenderTask:^{ + [synchronizer performCommitForView:1 + size:CGSize{9, 9} + notify:^{ + }]; + }]; + [scaffold joinRender]; + EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); +} From dc05a5a20715708f30ec6b25e3a20f811e6584d1 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 23 May 2023 16:33:20 -0700 Subject: [PATCH 128/147] multi view tests --- .../Source/FlutterThreadSynchronizer.h | 12 ++ .../Source/FlutterThreadSynchronizerTest.mm | 150 +++++++++++++++++- 2 files changed, 159 insertions(+), 3 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h index 74653956354ed..1cd4ee64dfa49 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h @@ -52,6 +52,18 @@ /** * Blocks current thread until the mutex is available, then return whether the * synchronizer is waiting for a correct commit during resizing. + * + * After calling an operation of the thread synchronizer, call this method, + * and when it returns, the thread synchronizer can be at one of the following 3 + * states: + * + * 1. The operation has not started at all (with a return value FALSE.) + * 2. The operation has ended (with a return value FALSE.) + * 3. beginResizeForView: is in progress, waiting (with a return value TRUE.) + * + * By eliminating the 1st case (such as using the notify callback), we can use + * this return value to decide whether the synchronizer is in case 2 or case 3, + * that is whether the resizing is blocked by a mismatching commit. */ - (BOOL)isWaitingWhenMutexIsAvailable; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm index ef97b62a3785a..4e27103f88091 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm @@ -75,7 +75,7 @@ - (void)joinRender { @end -TEST(FlutterThreadSynchronizerTest, FinishResizingImmediatelyWhenSizeMatches) { +TEST(FlutterThreadSynchronizerTest, RegularCommit) { FlutterThreadSynchronizerTestScaffold* scaffold = [[FlutterThreadSynchronizerTestScaffold alloc] init]; FlutterThreadSynchronizer* synchronizer = scaffold.synchronizer; @@ -121,7 +121,7 @@ - (void)joinRender { EXPECT_EQ(notifiedCommit, 1); } -TEST(FlutterThreadSynchronizerTest, FinishResizingOnlyWhenCommittingMatchingSize) { +TEST(FlutterThreadSynchronizerTest, ResizingBlocksRenderingUntilSizeMatches) { FlutterThreadSynchronizerTestScaffold* scaffold = [[FlutterThreadSynchronizerTestScaffold alloc] init]; FlutterThreadSynchronizer* synchronizer = scaffold.synchronizer; @@ -187,7 +187,7 @@ - (void)joinRender { [scaffold joinMain]; } -TEST(FlutterThreadSynchronizerTest, FinishResizingWhenShuttingDown) { +TEST(FlutterThreadSynchronizerTest, ShutdownMakesEverythingNonBlocking) { FlutterThreadSynchronizerTestScaffold* scaffold = [[FlutterThreadSynchronizerTestScaffold alloc] init]; FlutterThreadSynchronizer* synchronizer = scaffold.synchronizer; @@ -242,3 +242,147 @@ - (void)joinRender { [scaffold joinRender]; EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); } + +TEST(FlutterThreadSynchronizerTest, RegularCommitForMultipleViews) { + FlutterThreadSynchronizerTestScaffold* scaffold = + [[FlutterThreadSynchronizerTestScaffold alloc] init]; + FlutterThreadSynchronizer* synchronizer = scaffold.synchronizer; + fml::AutoResetWaitableEvent begunResizingLatch; + fml::AutoResetWaitableEvent* begunResizing = &begunResizingLatch; + + [synchronizer registerView:1]; + [synchronizer registerView:2]; + + // Initial resize: does not block until the first frame. + [scaffold dispatchMainTask:^{ + [synchronizer beginResizeForView:1 + size:CGSize{5, 5} + notify:^{ + }]; + [synchronizer beginResizeForView:2 + size:CGSize{15, 15} + notify:^{ + begunResizing->Signal(); + }]; + }]; + begunResizing->Wait(); + EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); + [scaffold joinMain]; + + // Still does not block. + [scaffold dispatchMainTask:^{ + [synchronizer beginResizeForView:1 + size:CGSize{7, 7} + notify:^{ + begunResizing->Signal(); + }]; + }]; + begunResizing->Signal(); + EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); + [scaffold joinMain]; + + // First frame + [scaffold dispatchRenderTask:^{ + [synchronizer performCommitForView:1 + size:CGSize{7, 7} + notify:^{ + }]; + [synchronizer performCommitForView:2 + size:CGSize{15, 15} + notify:^{ + }]; + }]; + [scaffold joinRender]; + EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); +} + +TEST(FlutterThreadSynchronizerTest, ResizingForMultipleViews) { + FlutterThreadSynchronizerTestScaffold* scaffold = + [[FlutterThreadSynchronizerTestScaffold alloc] init]; + FlutterThreadSynchronizer* synchronizer = scaffold.synchronizer; + fml::AutoResetWaitableEvent begunResizingLatch; + fml::AutoResetWaitableEvent* begunResizing = &begunResizingLatch; + + [synchronizer registerView:1]; + [synchronizer registerView:2]; + + // Initial resize: does not block until the first frame. + [scaffold dispatchMainTask:^{ + [synchronizer beginResizeForView:1 + size:CGSize{5, 5} + notify:^{ + }]; + [synchronizer beginResizeForView:2 + size:CGSize{15, 15} + notify:^{ + }]; + }]; + [scaffold joinMain]; + EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); + + // First frame. + [scaffold dispatchRenderTask:^{ + [synchronizer performCommitForView:1 + size:CGSize{5, 5} + notify:^{ + }]; + [synchronizer performCommitForView:2 + size:CGSize{15, 15} + notify:^{ + }]; + }]; + [scaffold joinRender]; + EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); + + // Resize view 2 to (17, 17): blocks until the next frame. + [scaffold dispatchMainTask:^{ + [synchronizer beginResizeForView:2 + size:CGSize{17, 17} + notify:^{ + begunResizing->Signal(); + }]; + }]; + begunResizing->Wait(); + EXPECT_TRUE([synchronizer isWaitingWhenMutexIsAvailable]); + + // Render view 1 with the size. Still blocking. + [scaffold dispatchRenderTask:^{ + [synchronizer performCommitForView:1 + size:CGSize{5, 5} + notify:^{ + }]; + }]; + [scaffold joinRender]; + EXPECT_TRUE([synchronizer isWaitingWhenMutexIsAvailable]); + + // Render view 2 with the old size. Still blocking. + [scaffold dispatchRenderTask:^{ + [synchronizer performCommitForView:1 + size:CGSize{15, 15} + notify:^{ + }]; + }]; + [scaffold joinRender]; + EXPECT_TRUE([synchronizer isWaitingWhenMutexIsAvailable]); + + // Render view 1 with the size. + [scaffold dispatchRenderTask:^{ + [synchronizer performCommitForView:1 + size:CGSize{5, 5} + notify:^{ + }]; + }]; + [scaffold joinRender]; + EXPECT_TRUE([synchronizer isWaitingWhenMutexIsAvailable]); + + // Render view 2 with the new size. Unblocks. + [scaffold dispatchRenderTask:^{ + [synchronizer performCommitForView:2 + size:CGSize{17, 17} + notify:^{ + }]; + }]; + [scaffold joinRender]; + [scaffold joinMain]; + EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]); +} From 98b4013fc988adc4f04c8c64238541e904aacd7e Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 23 May 2023 17:13:49 -0700 Subject: [PATCH 129/147] Docs and locs --- .../Source/FlutterThreadSynchronizer.h | 30 ++++++++++++++++--- .../Source/FlutterThreadSynchronizer.mm | 4 +++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h index 1cd4ee64dfa49..b1a2570159282 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h @@ -6,14 +6,20 @@ /** * Takes care of synchronization between raster and platform thread. + * + * All methods of this class must be called from the platform thread, + * except for performCommitForView:size:notify:. */ @interface FlutterThreadSynchronizer : NSObject +/** + * Creates a FlutterThreadSynchronizer that uses the OS main thread as the + * platform thread. + */ - (nullable instancetype)init; /** - * Called from platform thread. Blocks until a commit with given size (or empty) - * is requested. + * Blocks until all views have a commit with their given sizes (or empty) is requested. */ - (void)beginResizeForView:(int64_t)viewId size:(CGSize)size @@ -32,12 +38,24 @@ size:(CGSize)size notify:(nonnull dispatch_block_t)notify; +/** + * Requests the synchronizer to track another view. + * + * A view must be registered before calling begineResizeForView: or + * performCommitForView:. It is typically done when the view controller is + * created. + */ - (void)registerView:(int64_t)viewId; +/** + * Requests the synchronizer to no longer track a view. + * + * It is typically done when the view controller is created. + */ - (void)deregisterView:(int64_t)viewId; /** - * Called from platform thread when shutting down. + * Called when the engine shuts down. * * Prevents any further synchronization and no longer blocks any threads. */ @@ -45,8 +63,12 @@ @end -@interface FlutterThreadSynchronizer (Test) +@interface FlutterThreadSynchronizer (TestUtils) +/** + * Creates a FlutterThreadSynchronizer that uses the specified queue as the + * platform thread. + */ - (nullable instancetype)initWithMainQueue:(nonnull dispatch_queue_t)queue; /** diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm index a0561e4d30e8e..75a4ff5037210 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm @@ -165,10 +165,14 @@ - (void)performCommitForView:(int64_t)viewId } - (void)registerView:(int64_t)viewId { + dispatch_assert_queue(_mainQueue); + std::unique_lock lock(_mutex); _contentSizes[viewId] = CGSizeZero; } - (void)deregisterView:(int64_t)viewId { + dispatch_assert_queue(_mainQueue); + std::unique_lock lock(_mutex); _contentSizes.erase(viewId); } From 31b54305dc0be59144537fdaed7c7fc781036d4c Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 23 May 2023 17:27:12 -0700 Subject: [PATCH 130/147] Use better way, and fix tests --- .../macos/framework/Source/FlutterEngine.mm | 4 ++-- .../framework/Source/FlutterEngineTest.mm | 3 ++- .../framework/Source/FlutterEngine_Internal.h | 6 ++++-- .../Source/FlutterThreadSynchronizerTest.mm | 19 +++++++------------ .../framework/Source/FlutterViewController.mm | 6 ++++-- .../Source/FlutterViewController_Internal.h | 9 ++++++--- 6 files changed, 25 insertions(+), 22 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 41269b6e94dd7..ad8d3e870ce20 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -592,7 +592,7 @@ - (void)registerViewController:(FlutterViewController*)controller forId:(Flutter NSAssert(![controller attached], @"The incoming view controller is already attached to an engine."); NSAssert([_viewControllers objectForKey:@(viewId)] == nil, @"The requested view ID is occupied."); - [controller attachToEngine:self withId:viewId]; + [controller setUpWithEngine:self viewId:viewId threadSynchronizer:_threadSynchronizer]; NSAssert(controller.viewId == viewId, @"Failed to assign view ID."); [_viewControllers setObject:controller forKey:@(viewId)]; } @@ -1122,7 +1122,7 @@ - (NSPasteboard*)pasteboard { return flutter::GetSwitchesFromEnvironment(); } -- (FlutterThreadSynchronizer*)threadSynchronizer { +- (FlutterThreadSynchronizer*)testThreadSynchronizer { return _threadSynchronizer; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm index 5b3e111e89086..cf81a8ad798c2 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm @@ -34,6 +34,7 @@ @interface FlutterEngine (Test) * May be nil if the compositor has not been initialized yet. */ @property(nonatomic, readonly, nullable) flutter::FlutterCompositor* macOSCompositor; + @end @interface TestPlatformViewFactory : NSObject @@ -440,7 +441,7 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable result:^(id result){ }]; - [engine.threadSynchronizer blockUntilFrameAvailable]; + [engine.testThreadSynchronizer blockUntilFrameAvailable]; CALayer* rootLayer = viewController.flutterView.layer; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h b/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h index ba11b19851478..be973222ca95d 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h @@ -108,8 +108,6 @@ typedef NS_ENUM(NSInteger, FlutterAppExitResponse) { */ @property(nonatomic, readonly) FlutterEngineTerminationHandler* terminationHandler; -@property(nonatomic, readonly) FlutterThreadSynchronizer* threadSynchronizer; - /** * Attach a view controller to the engine as its default controller. * @@ -197,4 +195,8 @@ typedef NS_ENUM(NSInteger, FlutterAppExitResponse) { @end +@interface FlutterEngine (Tests) +- (nonnull FlutterThreadSynchronizer*)testThreadSynchronizer; +@end + NS_ASSUME_NONNULL_END diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm index 4e27103f88091..2541f8675576d 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm @@ -80,11 +80,10 @@ - (void)joinRender { [[FlutterThreadSynchronizerTestScaffold alloc] init]; FlutterThreadSynchronizer* synchronizer = scaffold.synchronizer; - [synchronizer registerView:1]; - // Initial resize: does not block until the first frame. __block int notifiedResize = 0; [scaffold dispatchMainTask:^{ + [synchronizer registerView:1]; [synchronizer beginResizeForView:1 size:CGSize{5, 5} notify:^{ @@ -131,10 +130,9 @@ - (void)joinRender { fml::AutoResetWaitableEvent begunResizingLatch; fml::AutoResetWaitableEvent* begunResizing = &begunResizingLatch; - [synchronizer registerView:1]; - // Initial resize: does not block until the first frame. [scaffold dispatchMainTask:^{ + [synchronizer registerView:1]; [synchronizer beginResizeForView:1 size:CGSize{5, 5} notify:^{ @@ -194,10 +192,9 @@ - (void)joinRender { fml::AutoResetWaitableEvent begunResizingLatch; fml::AutoResetWaitableEvent* begunResizing = &begunResizingLatch; - [synchronizer registerView:1]; - // Initial resize [scaffold dispatchMainTask:^{ + [synchronizer registerView:1]; [synchronizer beginResizeForView:1 size:CGSize{5, 5} notify:^{ @@ -250,11 +247,10 @@ - (void)joinRender { fml::AutoResetWaitableEvent begunResizingLatch; fml::AutoResetWaitableEvent* begunResizing = &begunResizingLatch; - [synchronizer registerView:1]; - [synchronizer registerView:2]; - // Initial resize: does not block until the first frame. [scaffold dispatchMainTask:^{ + [synchronizer registerView:1]; + [synchronizer registerView:2]; [synchronizer beginResizeForView:1 size:CGSize{5, 5} notify:^{ @@ -303,11 +299,10 @@ - (void)joinRender { fml::AutoResetWaitableEvent begunResizingLatch; fml::AutoResetWaitableEvent* begunResizing = &begunResizingLatch; - [synchronizer registerView:1]; - [synchronizer registerView:2]; - // Initial resize: does not block until the first frame. [scaffold dispatchMainTask:^{ + [synchronizer registerView:1]; + [synchronizer registerView:2]; [synchronizer beginResizeForView:1 size:CGSize{5, 5} notify:^{ diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index f7c082b694323..007a7559945f6 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -402,7 +402,6 @@ static void CommonInit(FlutterViewController* controller, FlutterEngine* engine) controller.engine, controller.viewId); controller->_mouseTrackingMode = FlutterMouseTrackingModeInKeyWindow; controller->_textInputPlugin = [[FlutterTextInputPlugin alloc] initWithViewController:controller]; - controller->_weakViewThreadSynchronizer = engine.threadSynchronizer; [controller initializeKeyboard]; [controller notifySemanticsEnabledChanged]; // macOS fires this message when changing IMEs. @@ -546,10 +545,13 @@ - (void)notifySemanticsEnabledChanged { return _bridge; } -- (void)attachToEngine:(nonnull FlutterEngine*)engine withId:(FlutterViewId)viewId { +- (void)setUpWithEngine:(FlutterEngine*)engine + viewId:(FlutterViewId)viewId + threadSynchronizer:(FlutterThreadSynchronizer*)threadSynchronizer { NSAssert(_engine == nil, @"Already attached to an engine %@.", _engine); _engine = engine; _viewId = viewId; + _weakViewThreadSynchronizer = threadSynchronizer; } - (void)detachFromEngine { diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h b/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h index 62801f9bce739..4ffeffac4bb17 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h @@ -39,11 +39,14 @@ - (BOOL)isDispatchingKeyEvent:(nonnull NSEvent*)event; /** - * Set the `engine` and `id` of this controller. + * Set up the controller with `engine` and `id`, and other engine-level classes. * - * This method is called by FlutterEngine. + * This method is called by FlutterEngine. A view controller must be set up + * before being used, and must be set up only once until detachFromEngine:. */ -- (void)attachToEngine:(nonnull FlutterEngine*)engine withId:(FlutterViewId)viewId; +- (void)setUpWithEngine:(nonnull FlutterEngine*)engine + viewId:(FlutterViewId)viewId + threadSynchronizer:(nonnull FlutterThreadSynchronizer*)threadSynchronizer; /** * Reset the `engine` and `id` of this controller. From 9cb1f69b2e1b638ed99b70e680bf4baa66ed6ebb Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 25 May 2023 13:31:22 -0700 Subject: [PATCH 131/147] Make FVC reg/dereg --- .../macos/framework/Source/FlutterThreadSynchronizer.h | 2 +- .../darwin/macos/framework/Source/FlutterView.mm | 1 - .../macos/framework/Source/FlutterViewController.mm | 9 ++++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h index b1a2570159282..8d8d248bdefdb 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h @@ -50,7 +50,7 @@ /** * Requests the synchronizer to no longer track a view. * - * It is typically done when the view controller is created. + * It is typically done when the view controller is destroyed. */ - (void)deregisterView:(int64_t)viewId; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.mm b/shell/platform/darwin/macos/framework/Source/FlutterView.mm index db20790f07b57..2c30a7457dc8a 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.mm @@ -37,7 +37,6 @@ - (instancetype)initWithMTLDevice:(id)device commandQueue:commandQueue layer:self.layer delegate:self]; - [_threadSynchronizer registerView:viewId]; } return self; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index 007a7559945f6..af56e910904fc 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -375,7 +375,7 @@ @implementation FlutterViewController { // FlutterViewController does not actually uses the synchronizer, but only // passes it to FlutterView. - __weak FlutterThreadSynchronizer* _weakViewThreadSynchronizer; + FlutterThreadSynchronizer* _threadSynchronizer; } @synthesize viewId = _viewId; @@ -551,11 +551,14 @@ - (void)setUpWithEngine:(FlutterEngine*)engine NSAssert(_engine == nil, @"Already attached to an engine %@.", _engine); _engine = engine; _viewId = viewId; - _weakViewThreadSynchronizer = threadSynchronizer; + _threadSynchronizer = threadSynchronizer; + [_threadSynchronizer registerView:_viewId]; } - (void)detachFromEngine { NSAssert(_engine != nil, @"Not attached to any engine."); + [_threadSynchronizer deregisterView:_viewId]; + _threadSynchronizer = nil; _engine = nil; } @@ -866,7 +869,7 @@ - (nonnull FlutterView*)createFlutterViewWithMTLDevice:(id)device return [[FlutterView alloc] initWithMTLDevice:device commandQueue:commandQueue reshapeListener:self - threadSynchronizer:_weakViewThreadSynchronizer + threadSynchronizer:_threadSynchronizer viewId:_viewId]; } From da1530460b463cd4c9ef8f82a5a729a549921f51 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 25 May 2023 13:33:03 -0700 Subject: [PATCH 132/147] Remove FV:shutdown --- .../platform/darwin/macos/framework/Source/FlutterEngine.mm | 5 ----- shell/platform/darwin/macos/framework/Source/FlutterView.h | 6 ------ shell/platform/darwin/macos/framework/Source/FlutterView.mm | 2 -- 3 files changed, 13 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index ad8d3e870ce20..d4f194cfca01e 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -931,13 +931,8 @@ - (void)shutDownEngine { return; } - NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator]; [_threadSynchronizer shutdown]; _threadSynchronizer = nil; - FlutterViewController* nextViewController; - while ((nextViewController = [viewControllerEnumerator nextObject])) { - [nextViewController.flutterView shutdown]; - } FlutterEngineResult result = _embedderAPI.Deinitialize(_engine); if (result != kSuccess) { diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.h b/shell/platform/darwin/macos/framework/Source/FlutterView.h index fe2d55aac554d..0c468d4c58d08 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.h @@ -59,12 +59,6 @@ constexpr FlutterViewId kFlutterDefaultViewId = 0ll; */ @property(readonly, nonatomic, nonnull) FlutterSurfaceManager* surfaceManager; -/** - * Must be called when shutting down. Unblocks raster thread and prevents any further - * synchronization. - */ -- (void)shutdown; - /** * By default, the `FlutterSurfaceManager` creates two layers to manage Flutter * content, the content layer and containing layer. To set the native background diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.mm b/shell/platform/darwin/macos/framework/Source/FlutterView.mm index 2c30a7457dc8a..89bbdb9153828 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.mm @@ -113,8 +113,6 @@ - (BOOL)layer:(CALayer*)layer return YES; } -- (void)shutdown { -} #pragma mark - NSAccessibility overrides - (BOOL)isAccessibilityElement { From 0a9a5847e00ea2b646728ea98c8331164217f026 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 26 May 2023 14:09:00 -0700 Subject: [PATCH 133/147] Short variant --- lib/ui/compositing/scene_builder_unittests.cc | 4 +- lib/ui/hooks_unittests.cc | 2 +- lib/ui/painting/image_dispose_unittests.cc | 2 +- lib/ui/painting/image_encoding_unittests.cc | 4 +- lib/ui/painting/paint_unittests.cc | 2 +- lib/ui/painting/path_unittests.cc | 6 +- .../painting/single_frame_codec_unittests.cc | 2 +- .../semantics_update_builder_unittests.cc | 2 +- .../platform_configuration_unittests.cc | 12 +- ...rm_message_response_dart_port_unittests.cc | 2 +- ...latform_message_response_dart_unittests.cc | 2 +- shell/common/input_events_unittests.cc | 15 +- shell/common/shell_test.cc | 60 ++--- shell/common/shell_test.h | 33 ++- shell/common/shell_test_platform_view.h | 2 +- shell/common/shell_unittests.cc | 238 +++++++++--------- .../ios/platform_message_handler_ios_test.mm | 4 +- 17 files changed, 203 insertions(+), 189 deletions(-) diff --git a/lib/ui/compositing/scene_builder_unittests.cc b/lib/ui/compositing/scene_builder_unittests.cc index 702d83bf61417..d83e15350d64c 100644 --- a/lib/ui/compositing/scene_builder_unittests.cc +++ b/lib/ui/compositing/scene_builder_unittests.cc @@ -82,7 +82,7 @@ TEST_F(ShellTest, SceneBuilderBuildAndSceneDisposeReleasesLayerStack) { AddNativeCallback("ValidateSceneHasNoLayers", CREATE_NATIVE_ENTRY(validate_scene_has_no_layers)); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto configuration = RunConfiguration::InferFromSettings(settings); @@ -141,7 +141,7 @@ TEST_F(ShellTest, EngineLayerDisposeReleasesReference) { AddNativeCallback("ValidateEngineLayerDispose", CREATE_NATIVE_ENTRY(validate_engine_layer_dispose)); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto configuration = RunConfiguration::InferFromSettings(settings); diff --git a/lib/ui/hooks_unittests.cc b/lib/ui/hooks_unittests.cc index 8fc69f42dcd7d..c386c462f9487 100644 --- a/lib/ui/hooks_unittests.cc +++ b/lib/ui/hooks_unittests.cc @@ -35,7 +35,7 @@ TEST_F(HooksTest, HooksUnitTests) { auto message_latch = std::make_shared(); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto call_hook = [](Dart_NativeArguments args) { diff --git a/lib/ui/painting/image_dispose_unittests.cc b/lib/ui/painting/image_dispose_unittests.cc index 0f8bb6d027062..2d1a648bdf6bc 100644 --- a/lib/ui/painting/image_dispose_unittests.cc +++ b/lib/ui/painting/image_dispose_unittests.cc @@ -69,7 +69,7 @@ TEST_F(ImageDisposeTest, ImageReleasedAfterFrameAndDisposePictureAndLayer) { CREATE_NATIVE_ENTRY(native_capture_image_and_picture)); AddNativeCallback("Finish", CREATE_NATIVE_ENTRY(native_finish)); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); diff --git a/lib/ui/painting/image_encoding_unittests.cc b/lib/ui/painting/image_encoding_unittests.cc index 8a111167fc64d..833dfc11856f8 100644 --- a/lib/ui/painting/image_encoding_unittests.cc +++ b/lib/ui/painting/image_encoding_unittests.cc @@ -106,7 +106,7 @@ TEST_F(ShellTest, EncodeImageGivesExternalTypedData) { AddNativeCallback("ValidateExternal", CREATE_NATIVE_ENTRY(nativeValidateExternal)); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto configuration = RunConfiguration::InferFromSettings(settings); @@ -170,7 +170,7 @@ TEST_F(ShellTest, EncodeImageAccessesSyncSwitch) { AddNativeCallback("EncodeImage", CREATE_NATIVE_ENTRY(native_encode_image)); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto configuration = RunConfiguration::InferFromSettings(settings); diff --git a/lib/ui/painting/paint_unittests.cc b/lib/ui/painting/paint_unittests.cc index 1324568cb2347..1986cc8d78701 100644 --- a/lib/ui/painting/paint_unittests.cc +++ b/lib/ui/painting/paint_unittests.cc @@ -36,7 +36,7 @@ TEST_F(ShellTest, ConvertPaintToDlPaint) { AddNativeCallback("ConvertPaintToDlPaint", CREATE_NATIVE_ENTRY(nativeToDlPaint)); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto configuration = RunConfiguration::InferFromSettings(settings); diff --git a/lib/ui/painting/path_unittests.cc b/lib/ui/painting/path_unittests.cc index f02b72ff5a511..e9f39b3ba73c8 100644 --- a/lib/ui/painting/path_unittests.cc +++ b/lib/ui/painting/path_unittests.cc @@ -50,7 +50,7 @@ TEST_F(ShellTest, PathVolatilityOldPathsBecomeNonVolatile) { AddNativeCallback("ValidatePath", CREATE_NATIVE_ENTRY(native_validate_path)); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto configuration = RunConfiguration::InferFromSettings(settings); @@ -108,7 +108,7 @@ TEST_F(ShellTest, PathVolatilityGCRemovesPathFromTracker) { AddNativeCallback("ValidatePath", CREATE_NATIVE_ENTRY(native_validate_path)); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto configuration = RunConfiguration::InferFromSettings(settings); @@ -164,7 +164,7 @@ TEST_F(ShellTest, DeterministicRenderingDisablesPathVolatility) { AddNativeCallback("ValidatePath", CREATE_NATIVE_ENTRY(native_validate_path)); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto configuration = RunConfiguration::InferFromSettings(settings); diff --git a/lib/ui/painting/single_frame_codec_unittests.cc b/lib/ui/painting/single_frame_codec_unittests.cc index d9a76af262ed5..2cbb2254442a4 100644 --- a/lib/ui/painting/single_frame_codec_unittests.cc +++ b/lib/ui/painting/single_frame_codec_unittests.cc @@ -41,7 +41,7 @@ TEST_F(ShellTest, SingleFrameCodecAccuratelyReportsSize) { AddNativeCallback("ValidateCodec", CREATE_NATIVE_ENTRY(validate_codec)); AddNativeCallback("Finish", CREATE_NATIVE_ENTRY(finish)); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto configuration = RunConfiguration::InferFromSettings(settings); diff --git a/lib/ui/semantics/semantics_update_builder_unittests.cc b/lib/ui/semantics/semantics_update_builder_unittests.cc index 004d7f8f78296..197314fbd3160 100644 --- a/lib/ui/semantics/semantics_update_builder_unittests.cc +++ b/lib/ui/semantics/semantics_update_builder_unittests.cc @@ -74,7 +74,7 @@ TEST_F(SemanticsUpdateBuilderTest, CanHandleAttributedStrings) { AddNativeCallback("SemanticsUpdate", CREATE_NATIVE_ENTRY(nativeSemanticsUpdate)); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto configuration = RunConfiguration::InferFromSettings(settings); diff --git a/lib/ui/window/platform_configuration_unittests.cc b/lib/ui/window/platform_configuration_unittests.cc index e89a5cc7d381c..64648093b7d75 100644 --- a/lib/ui/window/platform_configuration_unittests.cc +++ b/lib/ui/window/platform_configuration_unittests.cc @@ -49,7 +49,7 @@ TEST_F(ShellTest, PlatformConfigurationInitialization) { AddNativeCallback("ValidateConfiguration", CREATE_NATIVE_ENTRY(nativeValidateConfiguration)); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto run_configuration = RunConfiguration::InferFromSettings(settings); @@ -99,7 +99,7 @@ TEST_F(ShellTest, PlatformConfigurationWindowMetricsUpdate) { AddNativeCallback("ValidateConfiguration", CREATE_NATIVE_ENTRY(nativeValidateConfiguration)); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto run_configuration = RunConfiguration::InferFromSettings(settings); @@ -137,7 +137,7 @@ TEST_F(ShellTest, PlatformConfigurationOnErrorHandlesError) { CreateNewThread() // io ); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto run_configuration = RunConfiguration::InferFromSettings(settings); @@ -187,7 +187,7 @@ TEST_F(ShellTest, PlatformConfigurationOnErrorDoesNotHandleError) { CreateNewThread() // io ); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto run_configuration = RunConfiguration::InferFromSettings(settings); @@ -238,7 +238,7 @@ TEST_F(ShellTest, PlatformConfigurationOnErrorThrows) { CreateNewThread() // io ); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto run_configuration = RunConfiguration::InferFromSettings(settings); @@ -286,7 +286,7 @@ TEST_F(ShellTest, PlatformConfigurationSetDartPerformanceMode) { CreateNewThread() // io ); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto run_configuration = RunConfiguration::InferFromSettings(settings); diff --git a/lib/ui/window/platform_message_response_dart_port_unittests.cc b/lib/ui/window/platform_message_response_dart_port_unittests.cc index 7dda83049d926..32422be053bfc 100644 --- a/lib/ui/window/platform_message_response_dart_port_unittests.cc +++ b/lib/ui/window/platform_message_response_dart_port_unittests.cc @@ -53,7 +53,7 @@ TEST_F(ShellTest, PlatformMessageResponseDartPort) { Settings settings = CreateSettingsForFixture(); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto configuration = RunConfiguration::InferFromSettings(settings); diff --git a/lib/ui/window/platform_message_response_dart_unittests.cc b/lib/ui/window/platform_message_response_dart_unittests.cc index a9a29198bcf4d..d2f0863719c94 100644 --- a/lib/ui/window/platform_message_response_dart_unittests.cc +++ b/lib/ui/window/platform_message_response_dart_unittests.cc @@ -52,7 +52,7 @@ TEST_F(ShellTest, PlatformMessageResponseDart) { Settings settings = CreateSettingsForFixture(); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto configuration = RunConfiguration::InferFromSettings(settings); diff --git a/shell/common/input_events_unittests.cc b/shell/common/input_events_unittests.cc index 3f117c006c65f..f84cddbfbe5d2 100644 --- a/shell/common/input_events_unittests.cc +++ b/shell/common/input_events_unittests.cc @@ -54,7 +54,10 @@ static void TestSimulatedInputEvents( bool restart_engine = false) { ///// Begin constructing shell /////////////////////////////////////////////// auto settings = fixture->CreateSettingsForFixture(); - std::unique_ptr shell = fixture->CreateShell(settings, true); + std::unique_ptr shell = fixture->CreateShell({ + .settings = settings, + .simulate_vsync = true, + }); auto configuration = RunConfiguration::InferFromSettings(settings); configuration.SetEntrypoint("onPointerDataPacketMain"); @@ -300,7 +303,10 @@ TEST_F(ShellTest, HandlesActualIphoneXsInputEvents) { TEST_F(ShellTest, CanCorrectlyPipePointerPacket) { // Sets up shell with test fixture. auto settings = CreateSettingsForFixture(); - std::unique_ptr shell = CreateShell(settings, true); + std::unique_ptr shell = CreateShell({ + .settings = settings, + .simulate_vsync = true, + }); auto configuration = RunConfiguration::InferFromSettings(settings); configuration.SetEntrypoint("onPointerDataPacketMain"); @@ -361,7 +367,10 @@ TEST_F(ShellTest, CanCorrectlyPipePointerPacket) { TEST_F(ShellTest, CanCorrectlySynthesizePointerPacket) { // Sets up shell with test fixture. auto settings = CreateSettingsForFixture(); - std::unique_ptr shell = CreateShell(settings, true); + std::unique_ptr shell = CreateShell({ + .settings = settings, + .simulate_vsync = true, + }); auto configuration = RunConfiguration::InferFromSettings(settings); configuration.SetEntrypoint("onPointerDataPacketMain"); diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 8be076930b11b..7467d79491907 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -321,23 +321,21 @@ fml::TimePoint ShellTest::GetLatestFrameTargetTime(Shell* shell) const { } std::unique_ptr ShellTest::CreateShell(const Settings& settings, - bool simulate_vsync) { - return CreateShell(settings, GetTaskRunnersForFixture(), simulate_vsync); + const TaskRunners* task_runners) { + return CreateShell({ + .settings = settings, + .task_runners = task_runners, + }); } -std::unique_ptr ShellTest::CreateShell( - const Settings& settings, - TaskRunners task_runners, - bool simulate_vsync, - const std::shared_ptr& - shell_test_external_view_embedder, - bool is_gpu_disabled, - ShellTestPlatformView::BackendType rendering_backend, - Shell::CreateCallback platform_view_create_callback) { +std::unique_ptr ShellTest::CreateShell(const Config& config) { const auto vsync_clock = std::make_shared(); + TaskRunners task_runners = config.task_runners != nullptr + ? *config.task_runners + : GetTaskRunnersForFixture(); CreateVsyncWaiter create_vsync_waiter = [&]() { - if (simulate_vsync) { + if (config.simulate_vsync) { return static_cast>( std::make_unique(task_runners, vsync_clock)); } else { @@ -346,23 +344,27 @@ std::unique_ptr ShellTest::CreateShell( } }; + Shell::CreateCallback platform_view_create_callback = + std::move(config.platform_view_create_callback); if (!platform_view_create_callback) { - platform_view_create_callback = [vsync_clock, // - &create_vsync_waiter, // - shell_test_external_view_embedder, // - rendering_backend // + platform_view_create_callback = + [vsync_clock, // + &create_vsync_waiter, // + shell_test_external_view_embedder = + config.shell_test_external_view_embedder, // + rendering_backend = config.rendering_backend // ](Shell& shell) { - return ShellTestPlatformView::Create( - shell, // - shell.GetTaskRunners(), // - vsync_clock, // - create_vsync_waiter, // - rendering_backend, // - shell_test_external_view_embedder, // - shell.GetConcurrentWorkerTaskRunner(), // - shell.GetIsGpuDisabledSyncSwitch() // - ); - }; + return ShellTestPlatformView::Create( + shell, // + shell.GetTaskRunners(), // + vsync_clock, // + create_vsync_waiter, // + rendering_backend, // + shell_test_external_view_embedder, // + shell.GetConcurrentWorkerTaskRunner(), // + shell.GetIsGpuDisabledSyncSwitch() // + ); + }; } Shell::CreateCallback rasterizer_create_callback = @@ -370,10 +372,10 @@ std::unique_ptr ShellTest::CreateShell( return Shell::Create(flutter::PlatformData(), // task_runners, // - settings, // + config.settings, // platform_view_create_callback, // rasterizer_create_callback, // - is_gpu_disabled // + config.is_gpu_disabled // ); } diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index b9e95cc590e8b..c748e93ff3438 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -31,22 +31,31 @@ namespace testing { class ShellTest : public FixtureTest { public: + struct Config { + // Required. + const Settings& settings; + // Defaults to &GetTaskRunnersForFixture(). + const TaskRunners* task_runners; + // Defaults to false. + bool simulate_vsync; + // Defaults to nullptr. + std::shared_ptr + shell_test_external_view_embedder; + // Defaults to false. + bool is_gpu_disabled; + // Defaults to kDefaultBackend. + ShellTestPlatformView::BackendType rendering_backend; + // Defaults to calling ShellTestPlatformView::Create with the provided + // arguments. + Shell::CreateCallback platform_view_create_callback; + }; + ShellTest(); Settings CreateSettingsForFixture() override; std::unique_ptr CreateShell(const Settings& settings, - bool simulate_vsync = false); - std::unique_ptr CreateShell( - const Settings& settings, - TaskRunners task_runners, - bool simulate_vsync = false, - const std::shared_ptr& - shell_test_external_view_embedder = nullptr, - bool is_gpu_disabled = false, - ShellTestPlatformView::BackendType rendering_backend = - ShellTestPlatformView::BackendType::kDefaultBackend, - Shell::CreateCallback platform_view_create_callback = - nullptr); + const TaskRunners* task_runners = nullptr); + std::unique_ptr CreateShell(const Config& config); void DestroyShell(std::unique_ptr shell); void DestroyShell(std::unique_ptr shell, const TaskRunners& task_runners); diff --git a/shell/common/shell_test_platform_view.h b/shell/common/shell_test_platform_view.h index ebb96c87dd23e..3dd518bdaa27a 100644 --- a/shell/common/shell_test_platform_view.h +++ b/shell/common/shell_test_platform_view.h @@ -15,10 +15,10 @@ namespace testing { class ShellTestPlatformView : public PlatformView { public: enum class BackendType { + kDefaultBackend = 0, // Default value kGLBackend, kVulkanBackend, kMetalBackend, - kDefaultBackend, }; static std::unique_ptr Create( diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 2edc3b173d82b..81b52d03082d1 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -330,7 +330,7 @@ TEST_F(ShellTest, InitializeWithInvalidThreads) { ASSERT_FALSE(DartVMRef::IsInstanceRunning()); Settings settings = CreateSettingsForFixture(); TaskRunners task_runners("test", nullptr, nullptr, nullptr, nullptr); - auto shell = CreateShell(settings, task_runners); + auto shell = CreateShell(settings, &task_runners); ASSERT_FALSE(shell); ASSERT_FALSE(DartVMRef::IsInstanceRunning()); } @@ -348,7 +348,7 @@ TEST_F(ShellTest, InitializeWithDifferentThreads) { thread_host.raster_thread->GetTaskRunner(), thread_host.ui_thread->GetTaskRunner(), thread_host.io_thread->GetTaskRunner()); - auto shell = CreateShell(settings, task_runners); + auto shell = CreateShell(settings, &task_runners); ASSERT_TRUE(ValidateShell(shell.get())); ASSERT_TRUE(DartVMRef::IsInstanceRunning()); DestroyShell(std::move(shell), task_runners); @@ -363,7 +363,7 @@ TEST_F(ShellTest, InitializeWithSingleThread) { auto task_runner = thread_host.platform_thread->GetTaskRunner(); TaskRunners task_runners("test", task_runner, task_runner, task_runner, task_runner); - auto shell = CreateShell(settings, task_runners); + auto shell = CreateShell(settings, &task_runners); ASSERT_TRUE(DartVMRef::IsInstanceRunning()); ASSERT_TRUE(ValidateShell(shell.get())); DestroyShell(std::move(shell), task_runners); @@ -377,7 +377,7 @@ TEST_F(ShellTest, InitializeWithSingleThreadWhichIsTheCallingThread) { auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); TaskRunners task_runners("test", task_runner, task_runner, task_runner, task_runner); - auto shell = CreateShell(settings, task_runners); + auto shell = CreateShell(settings, &task_runners); ASSERT_TRUE(ValidateShell(shell.get())); ASSERT_TRUE(DartVMRef::IsInstanceRunning()); DestroyShell(std::move(shell), task_runners); @@ -428,9 +428,11 @@ TEST_F(ShellTest, InitializeWithDisabledGpu) { auto task_runner = thread_host.platform_thread->GetTaskRunner(); TaskRunners task_runners("test", task_runner, task_runner, task_runner, task_runner); - auto shell = CreateShell(settings, task_runners, /*simulate_vsync=*/false, - /*shell_test_external_view_embedder=*/nullptr, - /*is_gpu_disabled=*/true); + auto shell = CreateShell({ + .settings = settings, + .task_runners = &task_runners, + .is_gpu_disabled = true, + }); ASSERT_TRUE(DartVMRef::IsInstanceRunning()); ASSERT_TRUE(ValidateShell(shell.get())); @@ -456,7 +458,7 @@ TEST_F(ShellTest, InitializeWithGPUAndPlatformThreadsTheSame) { thread_host.ui_thread->GetTaskRunner(), // ui thread_host.io_thread->GetTaskRunner() // io ); - auto shell = CreateShell(settings, task_runners); + auto shell = CreateShell(settings, &task_runners); ASSERT_TRUE(DartVMRef::IsInstanceRunning()); ASSERT_TRUE(ValidateShell(shell.get())); DestroyShell(std::move(shell), task_runners); @@ -794,8 +796,10 @@ TEST_F(ShellTest, ExternalEmbedderNoThreadMerger) { }; auto external_view_embedder = std::make_shared( end_frame_callback, PostPrerollResult::kResubmitFrame, false); - auto shell = CreateShell(settings, GetTaskRunnersForFixture(), false, - external_view_embedder); + auto shell = CreateShell({ + .settings = settings, + .shell_test_external_view_embedder = external_view_embedder, + }); // Create the surface needed by rasterizer PlatformViewNotifyCreated(shell.get()); @@ -851,8 +855,10 @@ TEST_F(ShellTest, PushBackdropFilterToVisitedPlatformViews) { external_view_embedder = std::make_shared( end_frame_callback, PostPrerollResult::kResubmitFrame, false); - auto shell = CreateShell(settings, GetTaskRunnersForFixture(), false, - external_view_embedder); + auto shell = CreateShell({ + .settings = settings, + .shell_test_external_view_embedder = external_view_embedder, + }); // Create the surface needed by rasterizer PlatformViewNotifyCreated(shell.get()); @@ -920,8 +926,10 @@ TEST_F(ShellTest, }; auto external_view_embedder = std::make_shared( end_frame_callback, PostPrerollResult::kResubmitFrame, true); - auto shell = CreateShell(settings, GetTaskRunnersForFixture(), false, - external_view_embedder); + auto shell = CreateShell({ + .settings = settings, + .shell_test_external_view_embedder = external_view_embedder, + }); // Create the surface needed by rasterizer PlatformViewNotifyCreated(shell.get()); @@ -961,8 +969,10 @@ TEST_F(ShellTest, OnPlatformViewDestroyDisablesThreadMerger) { auto external_view_embedder = std::make_shared( end_frame_callback, PostPrerollResult::kSuccess, true); - auto shell = CreateShell(settings, GetTaskRunnersForFixture(), false, - external_view_embedder); + auto shell = CreateShell({ + .settings = settings, + .shell_test_external_view_embedder = external_view_embedder, + }); // Create the surface needed by rasterizer PlatformViewNotifyCreated(shell.get()); @@ -1023,8 +1033,10 @@ TEST_F(ShellTest, OnPlatformViewDestroyAfterMergingThreads) { // Set resubmit once to trigger thread merging. external_view_embedder->UpdatePostPrerollResult( PostPrerollResult::kResubmitFrame); - auto shell = CreateShell(settings, GetTaskRunnersForFixture(), false, - external_view_embedder); + auto shell = CreateShell({ + .settings = settings, + .shell_test_external_view_embedder = external_view_embedder, + }); // Create the surface needed by rasterizer PlatformViewNotifyCreated(shell.get()); @@ -1087,8 +1099,10 @@ TEST_F(ShellTest, OnPlatformViewDestroyWhenThreadsAreMerging) { auto external_view_embedder = std::make_shared( end_frame_callback, PostPrerollResult::kSuccess, true); - auto shell = CreateShell(settings, GetTaskRunnersForFixture(), false, - external_view_embedder); + auto shell = CreateShell({ + .settings = settings, + .shell_test_external_view_embedder = external_view_embedder, + }); // Create the surface needed by rasterizer PlatformViewNotifyCreated(shell.get()); @@ -1150,8 +1164,10 @@ TEST_F(ShellTest, }; auto external_view_embedder = std::make_shared( end_frame_callback, PostPrerollResult::kSuccess, true); - auto shell = CreateShell(settings, GetTaskRunnersForFixture(), false, - external_view_embedder); + auto shell = CreateShell({ + .settings = settings, + .shell_test_external_view_embedder = external_view_embedder, + }); // Create the surface needed by rasterizer PlatformViewNotifyCreated(shell.get()); @@ -1189,8 +1205,7 @@ TEST_F(ShellTest, TEST_F(ShellTest, OnPlatformViewDestroyWithoutRasterThreadMerger) { auto settings = CreateSettingsForFixture(); - auto shell = - CreateShell(settings, GetTaskRunnersForFixture(), false, nullptr); + auto shell = CreateShell(settings); // Create the surface needed by rasterizer PlatformViewNotifyCreated(shell.get()); @@ -1250,8 +1265,11 @@ TEST_F(ShellTest, OnPlatformViewDestroyWithStaticThreadMerging) { thread_host.ui_thread->GetTaskRunner(), // ui thread_host.io_thread->GetTaskRunner() // io ); - auto shell = - CreateShell(settings, task_runners, false, external_view_embedder); + auto shell = CreateShell({ + .settings = settings, + .task_runners = &task_runners, + .shell_test_external_view_embedder = external_view_embedder, + }); // Create the surface needed by rasterizer PlatformViewNotifyCreated(shell.get()); @@ -1291,8 +1309,10 @@ TEST_F(ShellTest, GetUsedThisFrameShouldBeSetBeforeEndFrame) { }; external_view_embedder = std::make_shared( end_frame_callback, PostPrerollResult::kSuccess, true); - auto shell = CreateShell(settings, GetTaskRunnersForFixture(), false, - external_view_embedder); + auto shell = CreateShell({ + .settings = settings, + .shell_test_external_view_embedder = external_view_embedder, + }); // Create the surface needed by rasterizer PlatformViewNotifyCreated(shell.get()); @@ -1342,8 +1362,10 @@ TEST_F(ShellTest, DISABLED_SkipAndSubmitFrame) { external_view_embedder = std::make_shared( end_frame_callback, PostPrerollResult::kSkipAndRetryFrame, true); - auto shell = CreateShell(settings, GetTaskRunnersForFixture(), false, - external_view_embedder); + auto shell = CreateShell({ + .settings = settings, + .shell_test_external_view_embedder = external_view_embedder, + }); PlatformViewNotifyCreated(shell.get()); @@ -1507,7 +1529,7 @@ TEST_F(ShellTest, WaitForFirstFrameInlined) { auto task_runner = CreateNewThread(); TaskRunners task_runners("test", task_runner, task_runner, task_runner, task_runner); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); // Create the surface needed by rasterizer PlatformViewNotifyCreated(shell.get()); @@ -1568,15 +1590,11 @@ TEST_F(ShellTest, MultipleFluttersSetResourceCacheBytes) { return result; }; - auto shell = CreateShell( - /*settings=*/settings, - /*task_runners=*/task_runners, - /*simulate_vsync=*/false, - /*shell_test_external_view_embedder=*/nullptr, - /*is_gpu_disabled=*/false, - /*rendering_backend=*/ - ShellTestPlatformView::BackendType::kDefaultBackend, - /*platform_view_create_callback=*/platform_view_create_callback); + auto shell = CreateShell({ + .settings = settings, + .task_runners = &task_runners, + .platform_view_create_callback = platform_view_create_callback, + }); // Create the surface needed by rasterizer PlatformViewNotifyCreated(shell.get()); @@ -1672,7 +1690,7 @@ TEST_F(ShellTest, SetResourceCacheSize) { auto task_runner = CreateNewThread(); TaskRunners task_runners("test", task_runner, task_runner, task_runner, task_runner); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); // Create the surface needed by rasterizer PlatformViewNotifyCreated(shell.get()); @@ -1734,7 +1752,7 @@ TEST_F(ShellTest, SetResourceCacheSizeEarly) { auto task_runner = CreateNewThread(); TaskRunners task_runners("test", task_runner, task_runner, task_runner, task_runner); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { @@ -1761,7 +1779,7 @@ TEST_F(ShellTest, SetResourceCacheSizeNotifiesDart) { auto task_runner = CreateNewThread(); TaskRunners task_runners("test", task_runner, task_runner, task_runner, task_runner); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { @@ -1800,7 +1818,7 @@ TEST_F(ShellTest, CanCreateImagefromDecompressedBytes) { TaskRunners task_runners("test", task_runner, task_runner, task_runner, task_runner); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); // Create the surface needed by rasterizer PlatformViewNotifyCreated(shell.get()); @@ -1869,7 +1887,7 @@ TEST_F(ShellTest, TextureFrameMarkedAvailableAndUnregister) { auto task_runner = CreateNewThread(); TaskRunners task_runners("test", task_runner, task_runner, task_runner, task_runner); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(ValidateShell(shell.get())); PlatformViewNotifyCreated(shell.get()); @@ -1923,7 +1941,7 @@ TEST_F(ShellTest, IsolateCanAccessPersistentIsolateData) { message_latch.Signal(); })); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto configuration = RunConfiguration::InferFromSettings(settings); @@ -1950,7 +1968,7 @@ TEST_F(ShellTest, CanScheduleFrameFromPlatform) { CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { check_latch.Signal(); })); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto configuration = RunConfiguration::InferFromSettings(settings); @@ -1984,7 +2002,7 @@ TEST_F(ShellTest, SecondaryVsyncCallbackShouldBeCalledAfterVsyncCallback) { is_on_begin_frame_called = true; count_down_latch.CountDown(); })); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(shell->IsSetup()); auto configuration = RunConfiguration::InferFromSettings(settings); @@ -2334,7 +2352,7 @@ TEST_F(ShellTest, RasterizerScreenshot) { auto task_runner = CreateNewThread(); TaskRunners task_runners("test", task_runner, task_runner, task_runner, task_runner); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(ValidateShell(shell.get())); PlatformViewNotifyCreated(shell.get()); @@ -2364,7 +2382,7 @@ TEST_F(ShellTest, RasterizerMakeRasterSnapshot) { auto task_runner = CreateNewThread(); TaskRunners task_runners("test", task_runner, task_runner, task_runner, task_runner); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); ASSERT_TRUE(ValidateShell(shell.get())); PlatformViewNotifyCreated(shell.get()); @@ -2618,8 +2636,10 @@ TEST_F(ShellTest, DISABLED_DiscardLayerTreeOnResize) { }; auto external_view_embedder = std::make_shared( std::move(end_frame_callback), PostPrerollResult::kSuccess, false); - std::unique_ptr shell = CreateShell( - settings, GetTaskRunnersForFixture(), false, external_view_embedder); + std::unique_ptr shell = CreateShell({ + .settings = settings, + .shell_test_external_view_embedder = external_view_embedder, + }); // Create the surface needed by rasterizer PlatformViewNotifyCreated(shell.get()); @@ -2691,8 +2711,10 @@ TEST_F(ShellTest, DISABLED_DiscardResubmittedLayerTreeOnResize) { external_view_embedder = std::make_shared( std::move(end_frame_callback), PostPrerollResult::kResubmitFrame, true); - std::unique_ptr shell = CreateShell( - settings, GetTaskRunnersForFixture(), false, external_view_embedder); + std::unique_ptr shell = CreateShell({ + .settings = settings, + .shell_test_external_view_embedder = external_view_embedder, + }); // Create the surface needed by rasterizer PlatformViewNotifyCreated(shell.get()); @@ -2776,7 +2798,7 @@ TEST_F(ShellTest, IgnoresInvalidMetrics) { AddNativeCallback("ReportMetrics", CREATE_NATIVE_ENTRY(native_report_device_pixel_ratio)); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); auto configuration = RunConfiguration::InferFromSettings(settings); configuration.SetEntrypoint("reportMetrics"); @@ -3377,7 +3399,7 @@ TEST_F(ShellTest, UpdateAssetResolverByTypeReplaces) { auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); TaskRunners task_runners("test", task_runner, task_runner, task_runner, task_runner); - auto shell = CreateShell(settings, task_runners); + auto shell = CreateShell(settings, &task_runners); ASSERT_TRUE(DartVMRef::IsInstanceRunning()); ASSERT_TRUE(ValidateShell(shell.get())); @@ -3422,7 +3444,7 @@ TEST_F(ShellTest, UpdateAssetResolverByTypeAppends) { auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); TaskRunners task_runners("test", task_runner, task_runner, task_runner, task_runner); - auto shell = CreateShell(settings, task_runners); + auto shell = CreateShell(settings, &task_runners); ASSERT_TRUE(DartVMRef::IsInstanceRunning()); ASSERT_TRUE(ValidateShell(shell.get())); @@ -3463,7 +3485,7 @@ TEST_F(ShellTest, UpdateAssetResolverByTypeNull) { auto task_runner = thread_host.platform_thread->GetTaskRunner(); TaskRunners task_runners("test", task_runner, task_runner, task_runner, task_runner); - auto shell = CreateShell(settings, task_runners); + auto shell = CreateShell(settings, &task_runners); ASSERT_TRUE(DartVMRef::IsInstanceRunning()); ASSERT_TRUE(ValidateShell(shell.get())); @@ -3500,7 +3522,7 @@ TEST_F(ShellTest, UpdateAssetResolverByTypeDoesNotReplaceMismatchType) { auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); TaskRunners task_runners("test", task_runner, task_runner, task_runner, task_runner); - auto shell = CreateShell(settings, task_runners); + auto shell = CreateShell(settings, &task_runners); ASSERT_TRUE(DartVMRef::IsInstanceRunning()); ASSERT_TRUE(ValidateShell(shell.get())); @@ -3545,14 +3567,10 @@ TEST_F(ShellTest, CanCreateShellsWithGLBackend) { GTEST_SKIP(); #endif // !SHELL_ENABLE_GL auto settings = CreateSettingsForFixture(); - std::unique_ptr shell = - CreateShell(settings, // - GetTaskRunnersForFixture(), // - false, // - nullptr, // - false, // - ShellTestPlatformView::BackendType::kGLBackend // - ); + std::unique_ptr shell = CreateShell({ + .settings = settings, + .rendering_backend = ShellTestPlatformView::BackendType::kGLBackend, + }); ASSERT_NE(shell, nullptr); ASSERT_TRUE(shell->IsSetup()); auto configuration = RunConfiguration::InferFromSettings(settings); @@ -3569,14 +3587,10 @@ TEST_F(ShellTest, CanCreateShellsWithVulkanBackend) { GTEST_SKIP(); #endif // !SHELL_ENABLE_VULKAN auto settings = CreateSettingsForFixture(); - std::unique_ptr shell = - CreateShell(settings, // - GetTaskRunnersForFixture(), // - false, // - nullptr, // - false, // - ShellTestPlatformView::BackendType::kVulkanBackend // - ); + std::unique_ptr shell = CreateShell({ + .settings = settings, + .rendering_backend = ShellTestPlatformView::BackendType::kVulkanBackend, + }); ASSERT_NE(shell, nullptr); ASSERT_TRUE(shell->IsSetup()); auto configuration = RunConfiguration::InferFromSettings(settings); @@ -3593,14 +3607,10 @@ TEST_F(ShellTest, CanCreateShellsWithMetalBackend) { GTEST_SKIP(); #endif // !SHELL_ENABLE_METAL auto settings = CreateSettingsForFixture(); - std::unique_ptr shell = - CreateShell(settings, // - GetTaskRunnersForFixture(), // - false, // - nullptr, // - false, // - ShellTestPlatformView::BackendType::kMetalBackend // - ); + std::unique_ptr shell = CreateShell({ + .settings = settings, + .rendering_backend = ShellTestPlatformView::BackendType::kMetalBackend, + }); ASSERT_NE(shell, nullptr); ASSERT_TRUE(shell->IsSetup()); auto configuration = RunConfiguration::InferFromSettings(settings); @@ -3764,15 +3774,11 @@ TEST_F(ShellTest, UsesPlatformMessageHandler) { .WillOnce(Return(platform_message_handler)); return result; }; - auto shell = CreateShell( - /*settings=*/settings, - /*task_runners=*/task_runners, - /*simulate_vsync=*/false, - /*shell_test_external_view_embedder=*/nullptr, - /*is_gpu_disabled=*/false, - /*rendering_backend=*/ - ShellTestPlatformView::BackendType::kDefaultBackend, - /*platform_view_create_callback=*/platform_view_create_callback); + auto shell = CreateShell({ + .settings = settings, + .task_runners = &task_runners, + .platform_view_create_callback = platform_view_create_callback, + }); EXPECT_EQ(platform_message_handler, shell->GetPlatformMessageHandler()); PostSync(task_runners.GetUITaskRunner(), [&shell]() { @@ -3864,7 +3870,7 @@ TEST_F(ShellTest, ImmutableBufferLoadsAssetOnBackgroundThread) { auto task_runner = CreateNewThread(); TaskRunners task_runners("test", task_runner, task_runner, task_runner, task_runner); - std::unique_ptr shell = CreateShell(settings, task_runners); + std::unique_ptr shell = CreateShell(settings, &task_runners); fml::CountDownLatch latch(1); AddNativeCallback("NotifyNative", @@ -3898,14 +3904,10 @@ TEST_F(ShellTest, PictureToImageSync) { GTEST_SKIP(); #endif // !SHELL_ENABLE_GL auto settings = CreateSettingsForFixture(); - std::unique_ptr shell = - CreateShell(settings, // - GetTaskRunnersForFixture(), // - false, // - nullptr, // - false, // - ShellTestPlatformView::BackendType::kGLBackend // - ); + std::unique_ptr shell = CreateShell({ + .settings = settings, + .rendering_backend = ShellTestPlatformView::BackendType::kGLBackend, + }); AddNativeCallback("NativeOnBeforeToImageSync", CREATE_NATIVE_ENTRY([&](auto args) { @@ -3941,14 +3943,10 @@ TEST_F(ShellTest, PictureToImageSyncImpellerNoSurface) { #endif // !SHELL_ENABLE_METAL auto settings = CreateSettingsForFixture(); settings.enable_impeller = true; - std::unique_ptr shell = - CreateShell(settings, // - GetTaskRunnersForFixture(), // - false, // - nullptr, // - false, // - ShellTestPlatformView::BackendType::kMetalBackend // - ); + std::unique_ptr shell = CreateShell({ + .settings = settings, + .rendering_backend = ShellTestPlatformView::BackendType::kMetalBackend, + }); AddNativeCallback("NativeOnBeforeToImageSync", CREATE_NATIVE_ENTRY([&](auto args) { @@ -3993,14 +3991,11 @@ TEST_F(ShellTest, PictureToImageSyncWithTrampledContext) { task_runner); auto settings = CreateSettingsForFixture(); - std::unique_ptr shell = - CreateShell(settings, // - task_runners, // - false, // - nullptr, // - false, // - ShellTestPlatformView::BackendType::kGLBackend // - ); + std::unique_ptr shell = CreateShell({ + .settings = settings, + .task_runners = &task_runners, + .rendering_backend = ShellTestPlatformView::BackendType::kGLBackend, + }); AddNativeCallback( "NativeOnBeforeToImageSync", CREATE_NATIVE_ENTRY([&](auto args) { @@ -4035,8 +4030,7 @@ TEST_F(ShellTest, PictureToImageSyncWithTrampledContext) { TEST_F(ShellTest, PluginUtilitiesCallbackHandleErrorHandling) { auto settings = CreateSettingsForFixture(); - std::unique_ptr shell = - CreateShell(settings, GetTaskRunnersForFixture()); + std::unique_ptr shell = CreateShell(settings); fml::AutoResetWaitableEvent latch; bool test_passed; @@ -4074,7 +4068,7 @@ TEST_F(ShellTest, NotifyIdleRejectsPastAndNearFuture) { thread_host.raster_thread->GetTaskRunner(), thread_host.ui_thread->GetTaskRunner(), thread_host.io_thread->GetTaskRunner()); - auto shell = CreateShell(settings, task_runners); + auto shell = CreateShell(settings, &task_runners); ASSERT_TRUE(DartVMRef::IsInstanceRunning()); ASSERT_TRUE(ValidateShell(shell.get())); @@ -4119,7 +4113,7 @@ TEST_F(ShellTest, NotifyIdleNotCalledInLatencyMode) { thread_host.raster_thread->GetTaskRunner(), thread_host.ui_thread->GetTaskRunner(), thread_host.io_thread->GetTaskRunner()); - auto shell = CreateShell(settings, task_runners); + auto shell = CreateShell(settings, &task_runners); ASSERT_TRUE(DartVMRef::IsInstanceRunning()); ASSERT_TRUE(ValidateShell(shell.get())); @@ -4162,7 +4156,7 @@ TEST_F(ShellTest, NotifyDestroyed) { thread_host.raster_thread->GetTaskRunner(), thread_host.ui_thread->GetTaskRunner(), thread_host.io_thread->GetTaskRunner()); - auto shell = CreateShell(settings, task_runners); + auto shell = CreateShell(settings, &task_runners); ASSERT_TRUE(DartVMRef::IsInstanceRunning()); ASSERT_TRUE(ValidateShell(shell.get())); diff --git a/shell/platform/darwin/ios/platform_message_handler_ios_test.mm b/shell/platform/darwin/ios/platform_message_handler_ios_test.mm index f1af3bef93ab0..92164577651af 100644 --- a/shell/platform/darwin/ios/platform_message_handler_ios_test.mm +++ b/shell/platform/darwin/ios/platform_message_handler_ios_test.mm @@ -44,8 +44,8 @@ @interface PlatformMessageHandlerIosTest : XCTestCase @implementation PlatformMessageHandlerIosTest - (void)testCreate { - flutter::TaskRunners task_runners("test", GetCurrentTaskRunner(), CreateNewThread("raster"), - CreateNewThread("ui"), CreateNewThread("io")); + TaskRunners task_runners("test", GetCurrentTaskRunner(), CreateNewThread("raster"), + CreateNewThread("ui"), CreateNewThread("io")); auto handler = std::make_unique(task_runners.GetPlatformTaskRunner()); XCTAssertTrue(handler); } From 1841648adfe3adbaa3ac7639ea464e4d6b3ecbf9 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 26 May 2023 21:51:15 -0700 Subject: [PATCH 134/147] Move support_thread_merging to PlatformView --- flow/embedded_views.cc | 4 -- flow/embedded_views.h | 7 ---- shell/common/animator_unittests.cc | 2 +- shell/common/platform_view.cc | 4 ++ shell/common/platform_view.h | 5 +++ shell/common/rasterizer.cc | 20 +++++----- shell/common/rasterizer.h | 2 +- shell/common/rasterizer_unittests.cc | 32 ++++++++-------- shell/common/shell.cc | 3 +- shell/common/shell_test.cc | 7 ++-- shell/common/shell_test.h | 1 + .../shell_test_external_view_embedder.cc | 8 +--- .../shell_test_external_view_embedder.h | 7 +--- shell/common/shell_test_platform_view.cc | 9 +++-- shell/common/shell_test_platform_view.h | 4 +- shell/common/shell_test_platform_view_gl.cc | 11 +++++- shell/common/shell_test_platform_view_gl.h | 8 +++- shell/common/shell_test_platform_view_metal.h | 8 +++- .../common/shell_test_platform_view_metal.mm | 11 +++++- .../common/shell_test_platform_view_vulkan.cc | 11 +++++- .../common/shell_test_platform_view_vulkan.h | 4 +- shell/common/shell_unittests.cc | 38 ++++++++++++------- .../external_view_embedder.cc | 5 --- .../external_view_embedder.h | 2 - .../external_view_embedder_unittests.cc | 16 ++++---- .../platform/android/platform_view_android.cc | 5 +++ .../platform/android/platform_view_android.h | 3 ++ .../darwin/ios/ios_external_view_embedder.h | 3 -- .../darwin/ios/ios_external_view_embedder.mm | 5 --- shell/platform/darwin/ios/platform_view_ios.h | 3 ++ .../platform/darwin/ios/platform_view_ios.mm | 5 +++ 31 files changed, 144 insertions(+), 109 deletions(-) diff --git a/flow/embedded_views.cc b/flow/embedded_views.cc index b89ed606fcc14..2065ecd8747c3 100644 --- a/flow/embedded_views.cc +++ b/flow/embedded_views.cc @@ -111,10 +111,6 @@ const std::vector>::const_iterator MutatorsStack::End() return vector_.end(); }; -bool ExternalViewEmbedder::SupportsDynamicThreadMerging() { - return false; -} - void ExternalViewEmbedder::Teardown() {} } // namespace flutter diff --git a/flow/embedded_views.h b/flow/embedded_views.h index c9ee05430d0ab..37743843eeabf 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -433,13 +433,6 @@ class ExternalViewEmbedder { bool should_resubmit_frame, fml::RefPtr raster_thread_merger) {} - // Whether the embedder should support dynamic thread merging. - // - // Returning `true` results a |RasterThreadMerger| instance to be created. - // * See also |BegineFrame| and |EndFrame| for getting the - // |RasterThreadMerger| instance. - virtual bool SupportsDynamicThreadMerging(); - // Called when the rasterizer is being torn down. // This method provides a way to release resources associated with the current // embedder. diff --git a/shell/common/animator_unittests.cc b/shell/common/animator_unittests.cc index 77698532a2305..130f344ca40de 100644 --- a/shell/common/animator_unittests.cc +++ b/shell/common/animator_unittests.cc @@ -85,7 +85,7 @@ TEST_F(ShellTest, VSyncTargetTime) { shell, shell.GetTaskRunners(), vsync_clock, create_vsync_waiter, ShellTestPlatformView::BackendType::kDefaultBackend, nullptr, shell.GetConcurrentWorkerTaskRunner(), - shell.GetIsGpuDisabledSyncSwitch()); + shell.GetIsGpuDisabledSyncSwitch(), false); }, [](Shell& shell) { return std::make_unique(shell); }); ASSERT_TRUE(DartVMRef::IsInstanceRunning()); diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index cff0b1c797643..e5d52d82ebe30 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -173,6 +173,10 @@ PlatformView::CreateExternalViewEmbedder() { return nullptr; } +bool PlatformView::SupportsDynamicThreadMerging() { + return false; +} + void PlatformView::SetNextFrameCallback(const fml::closure& closure) { if (!closure) { return; diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 3c12332c1d276..287ba91330ad4 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -683,8 +683,13 @@ class PlatformView { ComputePlatformResolvedLocales( const std::vector& supported_locale_data); + /// @brief Returns the external view embedder for the implicit view. + /// Those of non-implicit views should be set by + /// Shell::AddRenderSurface. virtual std::shared_ptr CreateExternalViewEmbedder(); + virtual bool SupportsDynamicThreadMerging(); + //-------------------------------------------------------------------------- /// @brief Invoked when the dart VM requests that a deferred library /// be loaded. Notifies the engine that the deferred library diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 28a294a279f68..f73a226827f5c 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -59,7 +59,8 @@ void Rasterizer::SetImpellerContext( impeller_context_ = std::move(impeller_context); } -void Rasterizer::Setup(std::unique_ptr studio) { +void Rasterizer::Setup(std::unique_ptr studio, + bool support_thread_merging) { studio_ = std::move(studio); if (max_cache_bytes_.has_value()) { @@ -72,9 +73,7 @@ void Rasterizer::Setup(std::unique_ptr studio) { compositor_context_->OnGrContextCreated(); } - if (external_view_embedder_ && - external_view_embedder_->SupportsDynamicThreadMerging() && - !raster_thread_merger_) { + if (support_thread_merging && !raster_thread_merger_) { const auto platform_id = delegate_.GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId(); const auto gpu_id = @@ -193,10 +192,10 @@ int Rasterizer::DrawLastLayerTree( } int success_count = 0; bool should_resubmit_frame = false; - for (auto& record_pair : surfaces_) { - Surface* surface = record_pair.second.surface.get(); - flutter::LayerTree* layer_tree = record_pair.second.last_tree.get(); - float device_pixel_ratio = record_pair.second.last_pixel_ratio; + for (auto& [view_id, surface_record] : surfaces_) { + Surface* surface = surface_record.surface.get(); + flutter::LayerTree* layer_tree = surface_record.last_tree.get(); + float device_pixel_ratio = surface_record.last_pixel_ratio; if (!surface || !layer_tree) { continue; } @@ -205,7 +204,7 @@ int Rasterizer::DrawLastLayerTree( } RasterStatus raster_status = DrawToSurface(*frame_timings_recorder, layer_tree, device_pixel_ratio, - &record_pair.second); + &surface_record); if (enable_leaf_layer_tracing) { layer_tree->enable_leaf_layer_tracing(false); } @@ -449,8 +448,7 @@ Rasterizer::DoDrawResult Rasterizer::DoDraw( return DoDrawResult{ .raster_status = raster_status, .resubmitted_layer_tree_item = std::make_unique( - view_id, - std::move(layer_tree), + view_id, std::move(layer_tree), frame_timings_recorder->CloneUntil( FrameTimingsRecorder::State::kBuildEnd), device_pixel_ratio), diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 7a3ea2bc880da..65442b44f9a2e 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -171,7 +171,7 @@ class Rasterizer final : public SnapshotDelegate, /// /// @param[in] studio The on-screen render studio. /// - void Setup(std::unique_ptr studio); + void Setup(std::unique_ptr studio, bool support_thread_merging); //---------------------------------------------------------------------------- /// @brief Releases the previously set up on-screen render surface and diff --git a/shell/common/rasterizer_unittests.cc b/shell/common/rasterizer_unittests.cc index 2d131f64a247f..7f3ce48010a99 100644 --- a/shell/common/rasterizer_unittests.cc +++ b/shell/common/rasterizer_unittests.cc @@ -136,7 +136,7 @@ TEST(RasterizerTest, drawEmptyPipeline) { auto surface = std::make_unique>(); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - rasterizer->Setup(std::move(studio)); + rasterizer->Setup(std::move(studio), false); rasterizer->AddSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { @@ -199,7 +199,7 @@ TEST(RasterizerTest, nullptr))) .Times(1); - rasterizer->Setup(std::move(studio)); + rasterizer->Setup(std::move(studio), false); rasterizer->AddSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { @@ -268,7 +268,7 @@ TEST( /*raster_thread_merger=*/_)) .Times(1); - rasterizer->Setup(std::move(studio)); + rasterizer->Setup(std::move(studio), false); rasterizer->AddSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { @@ -343,7 +343,7 @@ TEST( /*raster_thread_merger=*/_)) .Times(1); - rasterizer->Setup(std::move(studio)); + rasterizer->Setup(std::move(studio), false); rasterizer->AddSurface(kDefaultViewId, std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); @@ -420,7 +420,7 @@ TEST(RasterizerTest, /*raster_thread_merger=*/_)) .Times(2); - rasterizer->Setup(std::move(studio)); + rasterizer->Setup(std::move(studio), false); rasterizer->AddSurface(kDefaultViewId, std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); @@ -514,7 +514,7 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNotUsedThisFrame) { std::shared_ptr> external_view_embedder = std::make_shared>(); rasterizer->SetExternalViewEmbedder(external_view_embedder); - rasterizer->Setup(std::move(studio)); + rasterizer->Setup(std::move(studio), false); rasterizer->AddSurface(kDefaultViewId, std::move(surface)); EXPECT_CALL(*external_view_embedder, @@ -574,7 +574,7 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenPipelineIsEmpty) { std::shared_ptr> external_view_embedder = std::make_shared>(); rasterizer->SetExternalViewEmbedder(external_view_embedder); - rasterizer->Setup(std::move(studio)); + rasterizer->Setup(std::move(studio), false); rasterizer->AddSurface(kDefaultViewId, std::move(surface)); EXPECT_CALL( @@ -634,7 +634,7 @@ TEST(RasterizerTest, EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - rasterizer->Setup(std::move(studio)); + rasterizer->Setup(std::move(studio), false); rasterizer->AddSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { @@ -694,7 +694,7 @@ TEST( EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - rasterizer->Setup(std::move(studio)); + rasterizer->Setup(std::move(studio), false); rasterizer->AddSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { @@ -754,7 +754,7 @@ TEST( EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - rasterizer->Setup(std::move(studio)); + rasterizer->Setup(std::move(studio), false); rasterizer->AddSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { @@ -813,7 +813,7 @@ TEST( EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - rasterizer->Setup(std::move(studio)); + rasterizer->Setup(std::move(studio), false); rasterizer->AddSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { @@ -871,7 +871,7 @@ TEST( EXPECT_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - rasterizer->Setup(std::move(studio)); + rasterizer->Setup(std::move(studio), false); rasterizer->AddSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { @@ -953,7 +953,7 @@ TEST(RasterizerTest, }); thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - rasterizer->Setup(std::move(studio)); + rasterizer->Setup(std::move(studio), false); rasterizer->AddSurface(kDefaultViewId, std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); for (int i = 0; i < 2; i++) { @@ -1009,7 +1009,7 @@ TEST(RasterizerTest, TeardownFreesResourceCache) { }); EXPECT_CALL(*studio, GetContext()).WillRepeatedly(Return(context.get())); - rasterizer->Setup(std::move(studio)); + rasterizer->Setup(std::move(studio), false); rasterizer->AddSurface(kDefaultViewId, std::move(surface)); EXPECT_EQ(context->getResourceCacheLimit(), 0ul); @@ -1128,7 +1128,7 @@ TEST(RasterizerTest, presentationTimeSetWhenVsyncTargetInFuture) { [] { return std::make_unique(true); })); thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - rasterizer->Setup(std::move(studio)); + rasterizer->Setup(std::move(studio), false); rasterizer->AddSurface(kDefaultViewId, std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); for (int i = 0; i < 2; i++) { @@ -1211,7 +1211,7 @@ TEST(RasterizerTest, presentationTimeNotSetWhenVsyncTargetInPast) { [] { return std::make_unique(true); })); thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - rasterizer->Setup(std::move(studio)); + rasterizer->Setup(std::move(studio), false); rasterizer->AddSurface(kDefaultViewId, std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 42f2cc20d08d7..199ee8dac331e 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -804,7 +804,8 @@ void Shell::OnPlatformViewCreated() { // Enables the thread merger which may be used by the external view // embedder. rasterizer->EnableThreadMergerIfNeeded(); - rasterizer->Setup(std::move(studio)); + rasterizer->Setup(std::move(studio), + platform_view->SupportsDynamicThreadMerging()); rasterizer->AddSurface(kFlutterDefaultViewId, std::move(surface)); } diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 91f4e18c2e7ba..e5d06e119aeb0 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -355,8 +355,8 @@ std::unique_ptr ShellTest::CreateShell(const Config& config) { &create_vsync_waiter, // shell_test_external_view_embedder = config.shell_test_external_view_embedder, // - rendering_backend = config.rendering_backend // - ](Shell& shell) { + rendering_backend = config.rendering_backend, // + support_thread_merging = config.support_thread_merging](Shell& shell) { return ShellTestPlatformView::Create( shell, // shell.GetTaskRunners(), // @@ -365,7 +365,8 @@ std::unique_ptr ShellTest::CreateShell(const Config& config) { rendering_backend, // shell_test_external_view_embedder, // shell.GetConcurrentWorkerTaskRunner(), // - shell.GetIsGpuDisabledSyncSwitch() // + shell.GetIsGpuDisabledSyncSwitch(), // + support_thread_merging // ); }; } diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index c748e93ff3438..158a245474e2c 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -48,6 +48,7 @@ class ShellTest : public FixtureTest { // Defaults to calling ShellTestPlatformView::Create with the provided // arguments. Shell::CreateCallback platform_view_create_callback; + bool support_thread_merging; }; ShellTest(); diff --git a/shell/common/shell_test_external_view_embedder.cc b/shell/common/shell_test_external_view_embedder.cc index 475de0cd501da..f9ece8067b3c3 100644 --- a/shell/common/shell_test_external_view_embedder.cc +++ b/shell/common/shell_test_external_view_embedder.cc @@ -8,11 +8,9 @@ namespace flutter { ShellTestExternalViewEmbedder::ShellTestExternalViewEmbedder( const EndFrameCallBack& end_frame_call_back, - PostPrerollResult post_preroll_result, - bool support_thread_merging) + PostPrerollResult post_preroll_result) : end_frame_call_back_(end_frame_call_back), post_preroll_result_(post_preroll_result), - support_thread_merging_(support_thread_merging), submitted_frame_count_(0) {} void ShellTestExternalViewEmbedder::UpdatePostPrerollResult( @@ -118,8 +116,4 @@ DlCanvas* ShellTestExternalViewEmbedder::GetRootCanvas() { return nullptr; } -bool ShellTestExternalViewEmbedder::SupportsDynamicThreadMerging() { - return support_thread_merging_; -} - } // namespace flutter diff --git a/shell/common/shell_test_external_view_embedder.h b/shell/common/shell_test_external_view_embedder.h index 4aacfc6f7c401..cd5c7481b40bf 100644 --- a/shell/common/shell_test_external_view_embedder.h +++ b/shell/common/shell_test_external_view_embedder.h @@ -20,8 +20,7 @@ class ShellTestExternalViewEmbedder final : public ExternalViewEmbedder { std::function)>; ShellTestExternalViewEmbedder(const EndFrameCallBack& end_frame_call_back, - PostPrerollResult post_preroll_result, - bool support_thread_merging); + PostPrerollResult post_preroll_result); ~ShellTestExternalViewEmbedder() = default; @@ -86,14 +85,10 @@ class ShellTestExternalViewEmbedder final : public ExternalViewEmbedder { // |ExternalViewEmbedder| DlCanvas* GetRootCanvas() override; - // |ExternalViewEmbedder| - bool SupportsDynamicThreadMerging() override; - const EndFrameCallBack end_frame_call_back_; PostPrerollResult post_preroll_result_; - bool support_thread_merging_; SkISize frame_size_; std::map> slices_; std::map mutators_stacks_; diff --git a/shell/common/shell_test_platform_view.cc b/shell/common/shell_test_platform_view.cc index a683d372d8d0a..9ccdd0e912124 100644 --- a/shell/common/shell_test_platform_view.cc +++ b/shell/common/shell_test_platform_view.cc @@ -26,7 +26,8 @@ std::unique_ptr ShellTestPlatformView::Create( const std::shared_ptr& shell_test_external_view_embedder, const std::shared_ptr& worker_task_runner, - const std::shared_ptr& is_gpu_disabled_sync_switch) { + const std::shared_ptr& is_gpu_disabled_sync_switch, + bool support_thread_merging) { // TODO(gw280): https://github.com/flutter/flutter/issues/50298 // Make this fully runtime configurable switch (backend) { @@ -35,20 +36,20 @@ std::unique_ptr ShellTestPlatformView::Create( case BackendType::kGLBackend: return std::make_unique( delegate, task_runners, vsync_clock, create_vsync_waiter, - shell_test_external_view_embedder); + shell_test_external_view_embedder, support_thread_merging); #endif // SHELL_ENABLE_GL #ifdef SHELL_ENABLE_VULKAN case BackendType::kVulkanBackend: return std::make_unique( delegate, task_runners, vsync_clock, create_vsync_waiter, - shell_test_external_view_embedder); + shell_test_external_view_embedder, support_thread_merging); #endif // SHELL_ENABLE_VULKAN #ifdef SHELL_ENABLE_METAL case BackendType::kMetalBackend: return std::make_unique( delegate, task_runners, vsync_clock, create_vsync_waiter, shell_test_external_view_embedder, worker_task_runner, - is_gpu_disabled_sync_switch); + is_gpu_disabled_sync_switch, support_thread_merging); #endif // SHELL_ENABLE_METAL default: diff --git a/shell/common/shell_test_platform_view.h b/shell/common/shell_test_platform_view.h index 3dd518bdaa27a..8b46417af9505 100644 --- a/shell/common/shell_test_platform_view.h +++ b/shell/common/shell_test_platform_view.h @@ -30,8 +30,8 @@ class ShellTestPlatformView : public PlatformView { const std::shared_ptr& shell_test_external_view_embedder, const std::shared_ptr& worker_task_runner, - const std::shared_ptr& - is_gpu_disabled_sync_switch); + const std::shared_ptr& is_gpu_disabled_sync_switch, + bool support_thread_merging); 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 1e36400d96939..ae437e989360b 100644 --- a/shell/common/shell_test_platform_view_gl.cc +++ b/shell/common/shell_test_platform_view_gl.cc @@ -18,13 +18,15 @@ ShellTestPlatformViewGL::ShellTestPlatformViewGL( std::shared_ptr vsync_clock, CreateVsyncWaiter create_vsync_waiter, std::shared_ptr - shell_test_external_view_embedder) + shell_test_external_view_embedder, + bool support_thread_merging) : ShellTestPlatformView(delegate, task_runners), gl_surface_(SkISize::Make(800, 600)), create_vsync_waiter_(std::move(create_vsync_waiter)), vsync_clock_(std::move(vsync_clock)), shell_test_external_view_embedder_( - std::move(shell_test_external_view_embedder)) {} + std::move(shell_test_external_view_embedder), ), + support_thread_merging_(support_thread_merging) {} ShellTestPlatformViewGL::~ShellTestPlatformViewGL() = default; @@ -60,6 +62,11 @@ ShellTestPlatformViewGL::CreateExternalViewEmbedder() { return shell_test_external_view_embedder_; } +// |PlatformView| +bool ShellTestPlatformViewGL::SupportsDynamicThreadMerging() { + return support_thread_merging_; +} + // |PlatformView| PointerDataDispatcherMaker ShellTestPlatformViewGL::GetDispatcherMaker() { return [](DefaultPointerDataDispatcher::Delegate& delegate) { diff --git a/shell/common/shell_test_platform_view_gl.h b/shell/common/shell_test_platform_view_gl.h index 635bba96efa03..ed4c489579b0d 100644 --- a/shell/common/shell_test_platform_view_gl.h +++ b/shell/common/shell_test_platform_view_gl.h @@ -21,7 +21,8 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView, std::shared_ptr vsync_clock, CreateVsyncWaiter create_vsync_waiter, std::shared_ptr - shell_test_external_view_embedder); + shell_test_external_view_embedder, + bool support_thread_merging); // |ShellTestPlatformView| virtual ~ShellTestPlatformViewGL() override; @@ -41,6 +42,8 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView, sk_sp main_context_; + bool support_thread_merging_; + // |PlatformView| std::unique_ptr CreateRenderingStudio() override; @@ -50,6 +53,9 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView, // |PlatformView| std::shared_ptr CreateExternalViewEmbedder() override; + // |PlatformView| + bool SupportsDynamicThreadMerging() override; + // |PlatformView| std::unique_ptr CreateVSyncWaiter() override; diff --git a/shell/common/shell_test_platform_view_metal.h b/shell/common/shell_test_platform_view_metal.h index 32c6d1dc50751..c07258783f59c 100644 --- a/shell/common/shell_test_platform_view_metal.h +++ b/shell/common/shell_test_platform_view_metal.h @@ -25,8 +25,8 @@ class ShellTestPlatformViewMetal final : public ShellTestPlatformView, std::shared_ptr shell_test_external_view_embedder, const std::shared_ptr& worker_task_runner, - const std::shared_ptr& - is_gpu_disabled_sync_switch); + const std::shared_ptr& is_gpu_disabled_sync_switch, + bool support_thread_merging); // |ShellTestPlatformView| virtual ~ShellTestPlatformViewMetal() override; @@ -38,6 +38,7 @@ class ShellTestPlatformViewMetal final : public ShellTestPlatformView, const std::shared_ptr vsync_clock_; const std::shared_ptr shell_test_external_view_embedder_; + bool support_thread_merging_; // |ShellTestPlatformView| virtual void SimulateVSync() override; @@ -48,6 +49,9 @@ class ShellTestPlatformViewMetal final : public ShellTestPlatformView, // |PlatformView| std::shared_ptr CreateExternalViewEmbedder() override; + // |PlatformView| + bool SupportsDynamicThreadMerging() override; + // |PlatformView| PointerDataDispatcherMaker GetDispatcherMaker() override; diff --git a/shell/common/shell_test_platform_view_metal.mm b/shell/common/shell_test_platform_view_metal.mm index 48ded17a43af3..9d82156f6f26c 100644 --- a/shell/common/shell_test_platform_view_metal.mm +++ b/shell/common/shell_test_platform_view_metal.mm @@ -77,7 +77,8 @@ GPUMTLTextureInfo offscreen_texture_info() const { CreateVsyncWaiter create_vsync_waiter, std::shared_ptr shell_test_external_view_embedder, const std::shared_ptr& worker_task_runner, - const std::shared_ptr& is_gpu_disabled_sync_switch) + const std::shared_ptr& is_gpu_disabled_sync_switch, + bool support_thread_merging) : ShellTestPlatformView(delegate, task_runners), GPUSurfaceMetalDelegate(MTLRenderTargetType::kMTLTexture), metal_context_(std::make_unique(GetSettings().enable_impeller, @@ -85,7 +86,8 @@ GPUMTLTextureInfo offscreen_texture_info() const { is_gpu_disabled_sync_switch)), create_vsync_waiter_(std::move(create_vsync_waiter)), vsync_clock_(std::move(vsync_clock)), - shell_test_external_view_embedder_(std::move(shell_test_external_view_embedder)) { + shell_test_external_view_embedder_(std::move(shell_test_external_view_embedder)), + support_thread_merging_(support_thread_merging) { sksl_precompiler_ = std::make_shared(); if (GetSettings().enable_impeller) { FML_CHECK([metal_context_->impeller_context() context] != nil); @@ -110,6 +112,11 @@ GPUMTLTextureInfo offscreen_texture_info() const { return shell_test_external_view_embedder_; } +// |PlatformView| +bool ShellTestPlatformViewMetal::SupportsDynamicThreadMerging() { + return support_thread_merging_; +} + // |PlatformView| PointerDataDispatcherMaker ShellTestPlatformViewMetal::GetDispatcherMaker() { return [](DefaultPointerDataDispatcher::Delegate& delegate) { diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index 60bfd7ba49697..cd89d06b8955d 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -35,13 +35,15 @@ ShellTestPlatformViewVulkan::ShellTestPlatformViewVulkan( std::shared_ptr vsync_clock, CreateVsyncWaiter create_vsync_waiter, std::shared_ptr - shell_test_external_view_embedder) + shell_test_external_view_embedder, + bool support_thread_merging) : ShellTestPlatformView(delegate, task_runners), create_vsync_waiter_(std::move(create_vsync_waiter)), vsync_clock_(std::move(vsync_clock)), proc_table_(fml::MakeRefCounted(VULKAN_SO_PATH)), shell_test_external_view_embedder_( - std::move(shell_test_external_view_embedder)) {} + std::move(shell_test_external_view_embedder)), + support_thread_merging_(support_thread_merging) {} ShellTestPlatformViewVulkan::~ShellTestPlatformViewVulkan() = default; @@ -77,6 +79,11 @@ ShellTestPlatformViewVulkan::CreateExternalViewEmbedder() { return shell_test_external_view_embedder_; } +// |PlatformView| +bool ShellTestPlatformViewVulkan::SupportsDynamicThreadMerging() { + return support_thread_merging_; +} + // |PlatformView| PointerDataDispatcherMaker ShellTestPlatformViewVulkan::GetDispatcherMaker() { return [](DefaultPointerDataDispatcher::Delegate& delegate) { diff --git a/shell/common/shell_test_platform_view_vulkan.h b/shell/common/shell_test_platform_view_vulkan.h index a2c762259eb58..fcc3a4bdeaa47 100644 --- a/shell/common/shell_test_platform_view_vulkan.h +++ b/shell/common/shell_test_platform_view_vulkan.h @@ -22,7 +22,8 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { std::shared_ptr vsync_clock, CreateVsyncWaiter create_vsync_waiter, std::shared_ptr - shell_test_external_view_embedder); + shell_test_external_view_embedder, + bool support_thread_merging); ~ShellTestPlatformViewVulkan() override; @@ -43,6 +44,7 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { std::unique_ptr logical_device_; sk_sp memory_allocator_; sk_sp context_; + bool support_thread_merging_; bool CreateSkiaGrContext(); bool CreateSkiaBackendContext(GrVkBackendContext* context); diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 2e1260761cbbc..c21a3f50e68ea 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -421,9 +421,11 @@ TEST_F(ShellTest, return static_cast>( std::make_unique(task_runners)); }, - ShellTestPlatformView::BackendType::kDefaultBackend, nullptr, + ShellTestPlatformView::BackendType::kDefaultBackend, + /*external_view_embedder=*/nullptr, shell.GetConcurrentWorkerTaskRunner(), - shell.GetIsGpuDisabledSyncSwitch()); + shell.GetIsGpuDisabledSyncSwitch(), + /*support_thread_merging=*/false); }, [](Shell& shell) { return std::make_unique(shell); }); ASSERT_TRUE(ValidateShell(shell.get())); @@ -807,7 +809,7 @@ TEST_F(ShellTest, ExternalEmbedderNoThreadMerger) { end_frame_latch.Signal(); }; auto external_view_embedder = std::make_shared( - end_frame_callback, PostPrerollResult::kResubmitFrame, false); + end_frame_callback, PostPrerollResult::kResubmitFrame); auto shell = CreateShell({ .settings = settings, .shell_test_external_view_embedder = external_view_embedder, @@ -866,7 +868,7 @@ TEST_F(ShellTest, PushBackdropFilterToVisitedPlatformViews) { }; external_view_embedder = std::make_shared( - end_frame_callback, PostPrerollResult::kResubmitFrame, false); + end_frame_callback, PostPrerollResult::kResubmitFrame); auto shell = CreateShell({ .settings = settings, .shell_test_external_view_embedder = external_view_embedder, @@ -937,10 +939,11 @@ TEST_F(ShellTest, end_frame_latch.Signal(); }; auto external_view_embedder = std::make_shared( - end_frame_callback, PostPrerollResult::kResubmitFrame, true); + end_frame_callback, PostPrerollResult::kResubmitFrame); auto shell = CreateShell({ .settings = settings, .shell_test_external_view_embedder = external_view_embedder, + .support_thread_merging = true, }); // Create the surface needed by rasterizer @@ -979,11 +982,12 @@ TEST_F(ShellTest, OnPlatformViewDestroyDisablesThreadMerger) { raster_thread_merger = std::move(thread_merger); }; auto external_view_embedder = std::make_shared( - end_frame_callback, PostPrerollResult::kSuccess, true); + end_frame_callback, PostPrerollResult::kSuccess); auto shell = CreateShell({ .settings = settings, .shell_test_external_view_embedder = external_view_embedder, + .support_thread_merging = true, }); // Create the surface needed by rasterizer @@ -1041,13 +1045,14 @@ TEST_F(ShellTest, OnPlatformViewDestroyAfterMergingThreads) { end_frame_latch.Signal(); }; external_view_embedder = std::make_shared( - end_frame_callback, PostPrerollResult::kSuccess, true); + end_frame_callback, PostPrerollResult::kSuccess); // Set resubmit once to trigger thread merging. external_view_embedder->UpdatePostPrerollResult( PostPrerollResult::kResubmitFrame); auto shell = CreateShell({ .settings = settings, .shell_test_external_view_embedder = external_view_embedder, + .support_thread_merging = true, }); // Create the surface needed by rasterizer @@ -1109,11 +1114,12 @@ TEST_F(ShellTest, OnPlatformViewDestroyWhenThreadsAreMerging) { // can later check if the rasterizer is tore down using // |ValidateDestroyPlatformView| auto external_view_embedder = std::make_shared( - end_frame_callback, PostPrerollResult::kSuccess, true); + end_frame_callback, PostPrerollResult::kSuccess); auto shell = CreateShell({ .settings = settings, .shell_test_external_view_embedder = external_view_embedder, + .support_thread_merging = true, }); // Create the surface needed by rasterizer @@ -1175,10 +1181,11 @@ TEST_F(ShellTest, end_frame_latch.Signal(); }; auto external_view_embedder = std::make_shared( - end_frame_callback, PostPrerollResult::kSuccess, true); + end_frame_callback, PostPrerollResult::kSuccess); auto shell = CreateShell({ .settings = settings, .shell_test_external_view_embedder = external_view_embedder, + .support_thread_merging = true, }); // Create the surface needed by rasterizer @@ -1266,7 +1273,7 @@ TEST_F(ShellTest, OnPlatformViewDestroyWithStaticThreadMerging) { end_frame_latch.Signal(); }; auto external_view_embedder = std::make_shared( - end_frame_callback, PostPrerollResult::kSuccess, true); + end_frame_callback, PostPrerollResult::kSuccess); ThreadHost thread_host( "io.flutter.test." + GetCurrentTestName() + ".", ThreadHost::Type::Platform | ThreadHost::Type::IO | ThreadHost::Type::UI); @@ -1281,6 +1288,7 @@ TEST_F(ShellTest, OnPlatformViewDestroyWithStaticThreadMerging) { .settings = settings, .task_runners = &task_runners, .shell_test_external_view_embedder = external_view_embedder, + .support_thread_merging = true, }); // Create the surface needed by rasterizer @@ -1320,10 +1328,11 @@ TEST_F(ShellTest, GetUsedThisFrameShouldBeSetBeforeEndFrame) { end_frame_latch.Signal(); }; external_view_embedder = std::make_shared( - end_frame_callback, PostPrerollResult::kSuccess, true); + end_frame_callback, PostPrerollResult::kSuccess); auto shell = CreateShell({ .settings = settings, .shell_test_external_view_embedder = external_view_embedder, + .support_thread_merging = true, }); // Create the surface needed by rasterizer @@ -1372,11 +1381,12 @@ TEST_F(ShellTest, DISABLED_SkipAndSubmitFrame) { end_frame_latch.Signal(); }; external_view_embedder = std::make_shared( - end_frame_callback, PostPrerollResult::kSkipAndRetryFrame, true); + end_frame_callback, PostPrerollResult::kSkipAndRetryFrame); auto shell = CreateShell({ .settings = settings, .shell_test_external_view_embedder = external_view_embedder, + .support_thread_merging = true, }); PlatformViewNotifyCreated(shell.get()); @@ -2658,7 +2668,7 @@ TEST_F(ShellTest, DISABLED_DiscardLayerTreeOnResize) { end_frame_latch.Signal(); }; auto external_view_embedder = std::make_shared( - std::move(end_frame_callback), PostPrerollResult::kSuccess, false); + std::move(end_frame_callback), PostPrerollResult::kSuccess); std::unique_ptr shell = CreateShell({ .settings = settings, .shell_test_external_view_embedder = external_view_embedder, @@ -2733,7 +2743,7 @@ TEST_F(ShellTest, DISABLED_DiscardResubmittedLayerTreeOnResize) { }; external_view_embedder = std::make_shared( - std::move(end_frame_callback), PostPrerollResult::kResubmitFrame, true); + std::move(end_frame_callback), PostPrerollResult::kResubmitFrame); std::unique_ptr shell = CreateShell({ .settings = settings, 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 5d2692e3d89aa..cf846608196ef 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder.cc @@ -299,11 +299,6 @@ void AndroidExternalViewEmbedder::EndFrame( } } -// |ExternalViewEmbedder| -bool AndroidExternalViewEmbedder::SupportsDynamicThreadMerging() { - return true; -} - // |ExternalViewEmbedder| void AndroidExternalViewEmbedder::Teardown() { DestroySurfaces(); 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 2038b4190478c..7cd72ca3d8173 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.h +++ b/shell/platform/android/external_view_embedder/external_view_embedder.h @@ -71,8 +71,6 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder { bool should_resubmit_frame, fml::RefPtr raster_thread_merger) override; - bool SupportsDynamicThreadMerging() override; - void Teardown() override; // Gets the rect based on the device pixel ratio of a platform view displayed 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 0c932d21c397a..ac3148eba8770 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 @@ -929,13 +929,15 @@ TEST(AndroidExternalViewEmbedder, DoesNotDestroyOverlayLayersOnSizeChange) { GetThreadMergerFromRasterThread(&platform_thread)); } -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()); -} +// TODO(dkwingsmt): Migrate to a unit test for PlatformViewAndroid, which +// however doesn't have unit tests at all... +// 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(); diff --git a/shell/platform/android/platform_view_android.cc b/shell/platform/android/platform_view_android.cc index 6ed55925bfd7c..fd680263a652a 100644 --- a/shell/platform/android/platform_view_android.cc +++ b/shell/platform/android/platform_view_android.cc @@ -320,6 +320,11 @@ PlatformViewAndroid::CreateExternalViewEmbedder() { *android_context_, jni_facade_, surface_factory_, task_runners_); } +// |PlatformView| +bool PlatformViewAndroid::SupportsDynamicThreadMerging() { + return true; +} + // |PlatformView| std::unique_ptr PlatformViewAndroid::CreateSnapshotSurfaceProducer() { diff --git a/shell/platform/android/platform_view_android.h b/shell/platform/android/platform_view_android.h index cedd4086b1268..25c5786dc71b2 100644 --- a/shell/platform/android/platform_view_android.h +++ b/shell/platform/android/platform_view_android.h @@ -151,6 +151,9 @@ class PlatformViewAndroid final : public PlatformView { // |PlatformView| std::shared_ptr CreateExternalViewEmbedder() override; + // |PlatformView| + bool SupportsDynamicThreadMerging() override; + // |PlatformView| std::unique_ptr CreateSnapshotSurfaceProducer() override; diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.h b/shell/platform/darwin/ios/ios_external_view_embedder.h index 3b94df3888f2e..1192dba92d02e 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.h +++ b/shell/platform/darwin/ios/ios_external_view_embedder.h @@ -59,9 +59,6 @@ class IOSExternalViewEmbedder : public ExternalViewEmbedder { bool should_resubmit_frame, fml::RefPtr raster_thread_merger) override; - // |ExternalViewEmbedder| - bool SupportsDynamicThreadMerging() override; - // |ExternalViewEmbedder| void PushFilterToVisitedPlatformViews( std::shared_ptr filter, diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm index dde57241ee2a7..98713d914e1a7 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.mm +++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm @@ -82,11 +82,6 @@ platform_views_controller_->EndFrame(should_resubmit_frame, raster_thread_merger); } -// |ExternalViewEmbedder| -bool IOSExternalViewEmbedder::SupportsDynamicThreadMerging() { - return true; -} - // |ExternalViewEmbedder| void IOSExternalViewEmbedder::PushFilterToVisitedPlatformViews( std::shared_ptr filter, diff --git a/shell/platform/darwin/ios/platform_view_ios.h b/shell/platform/darwin/ios/platform_view_ios.h index 43f2b5d2962cd..510d51deba038 100644 --- a/shell/platform/darwin/ios/platform_view_ios.h +++ b/shell/platform/darwin/ios/platform_view_ios.h @@ -159,6 +159,9 @@ class PlatformViewIOS final : public PlatformView { // |PlatformView| std::shared_ptr CreateExternalViewEmbedder() override; + // |PlatformView| + bool SupportsDynamicThreadMerging() override; + // |PlatformView| sk_sp CreateResourceContext() const override; diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index eacdf25e3d00a..2bcef0a1b5677 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -170,6 +170,11 @@ new PlatformMessageHandlerIos(task_runners.GetPlatformTaskRunner())) {} return std::make_shared(platform_views_controller_, ios_context_); } +// |PlatformView| +bool PlatformViewIOS::SupportsDynamicThreadMerging() { + return true; +} + // |PlatformView| sk_sp PlatformViewIOS::CreateResourceContext() const { return ios_context_->CreateResourceContext(); From b93035129ec9b4d79c63ffd4d8f2f450ce3430ca Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 26 May 2023 22:16:56 -0700 Subject: [PATCH 135/147] Fix raster test and fuchsia compile --- shell/common/rasterizer_unittests.cc | 13 +++---------- shell/common/shell_test_platform_view_gl.cc | 2 +- shell/common/shell_test_platform_view_vulkan.h | 3 ++- .../flutter/flatland_external_view_embedder.h | 3 --- .../fuchsia/flutter/gfx_external_view_embedder.h | 3 --- 5 files changed, 6 insertions(+), 18 deletions(-) diff --git a/shell/common/rasterizer_unittests.cc b/shell/common/rasterizer_unittests.cc index 7f3ce48010a99..6708125c84004 100644 --- a/shell/common/rasterizer_unittests.cc +++ b/shell/common/rasterizer_unittests.cc @@ -91,7 +91,6 @@ class MockExternalViewEmbedder : public ExternalViewEmbedder { MOCK_METHOD2(EndFrame, void(bool should_resubmit_frame, fml::RefPtr raster_thread_merger)); - MOCK_METHOD0(SupportsDynamicThreadMerging, bool()); }; } // namespace @@ -244,8 +243,6 @@ TEST( std::shared_ptr> external_view_embedder = std::make_shared>(); rasterizer->SetExternalViewEmbedder(external_view_embedder); - EXPECT_CALL(*external_view_embedder, SupportsDynamicThreadMerging) - .WillRepeatedly(Return(true)); SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; auto surface_frame = std::make_unique( @@ -268,7 +265,7 @@ TEST( /*raster_thread_merger=*/_)) .Times(1); - rasterizer->Setup(std::move(studio), false); + rasterizer->Setup(std::move(studio), /*support_thread_merging=*/true); rasterizer->AddSurface(kDefaultViewId, std::move(surface)); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { @@ -330,8 +327,6 @@ TEST( .WillOnce(Return(ByMove(std::move(surface_frame)))); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - EXPECT_CALL(*external_view_embedder, SupportsDynamicThreadMerging) - .WillRepeatedly(Return(true)); EXPECT_CALL(*external_view_embedder, BeginFrame(/*frame_size=*/SkISize(), /*context=*/nullptr, @@ -343,7 +338,7 @@ TEST( /*raster_thread_merger=*/_)) .Times(1); - rasterizer->Setup(std::move(studio), false); + rasterizer->Setup(std::move(studio), /*support_thread_merging=*/true); rasterizer->AddSurface(kDefaultViewId, std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); @@ -407,8 +402,6 @@ TEST(RasterizerTest, .WillOnce(Return(ByMove(std::move(surface_frame2)))); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); - EXPECT_CALL(*external_view_embedder, SupportsDynamicThreadMerging) - .WillRepeatedly(Return(true)); EXPECT_CALL(*external_view_embedder, BeginFrame(/*frame_size=*/SkISize(), /*context=*/nullptr, @@ -420,7 +413,7 @@ TEST(RasterizerTest, /*raster_thread_merger=*/_)) .Times(2); - rasterizer->Setup(std::move(studio), false); + rasterizer->Setup(std::move(studio), /*support_thread_merging=*/true); rasterizer->AddSurface(kDefaultViewId, std::move(surface)); auto pipeline = std::make_shared(/*depth=*/10); diff --git a/shell/common/shell_test_platform_view_gl.cc b/shell/common/shell_test_platform_view_gl.cc index ae437e989360b..6ba0d60fbcfff 100644 --- a/shell/common/shell_test_platform_view_gl.cc +++ b/shell/common/shell_test_platform_view_gl.cc @@ -25,7 +25,7 @@ ShellTestPlatformViewGL::ShellTestPlatformViewGL( create_vsync_waiter_(std::move(create_vsync_waiter)), vsync_clock_(std::move(vsync_clock)), shell_test_external_view_embedder_( - std::move(shell_test_external_view_embedder), ), + std::move(shell_test_external_view_embedder)), support_thread_merging_(support_thread_merging) {} ShellTestPlatformViewGL::~ShellTestPlatformViewGL() = default; diff --git a/shell/common/shell_test_platform_view_vulkan.h b/shell/common/shell_test_platform_view_vulkan.h index fcc3a4bdeaa47..845604640d0fe 100644 --- a/shell/common/shell_test_platform_view_vulkan.h +++ b/shell/common/shell_test_platform_view_vulkan.h @@ -44,7 +44,6 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { std::unique_ptr logical_device_; sk_sp memory_allocator_; sk_sp context_; - bool support_thread_merging_; bool CreateSkiaGrContext(); bool CreateSkiaBackendContext(GrVkBackendContext* context); @@ -116,6 +115,8 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { std::shared_ptr offscreen_context_; + bool support_thread_merging_; + // |PlatformView| std::unique_ptr CreateRenderingStudio() override; diff --git a/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h b/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h index 394de12c7887a..c637aad11d1eb 100644 --- a/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h +++ b/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h @@ -93,9 +93,6 @@ class FlatlandExternalViewEmbedder final // |ExternalViewEmbedder| void CancelFrame() override { Reset(); } - // |ExternalViewEmbedder| - bool SupportsDynamicThreadMerging() override { return false; } - // View manipulation. // |SetViewProperties| doesn't manipulate the view directly -- it sets pending // properties for the next |UpdateView| call. diff --git a/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h b/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h index 7d2f545cf238e..54ed5d6261b43 100644 --- a/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h +++ b/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h @@ -116,9 +116,6 @@ class GfxExternalViewEmbedder final : public flutter::ExternalViewEmbedder { // |ExternalViewEmbedder| void CancelFrame() override { Reset(); } - // |ExternalViewEmbedder| - bool SupportsDynamicThreadMerging() override { return false; } - // View manipulation. // |SetViewProperties| doesn't manipulate the view directly -- it sets pending // properties for the next |UpdateView| call. From 9e4b4950267c300c584844dcb005fd9cf5f5f654 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 30 May 2023 11:12:14 -0700 Subject: [PATCH 136/147] Fix compile --- shell/common/shell_test_platform_view_vulkan.cc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index cd89d06b8955d..5ff444495d5f0 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -79,11 +79,6 @@ ShellTestPlatformViewVulkan::CreateExternalViewEmbedder() { return shell_test_external_view_embedder_; } -// |PlatformView| -bool ShellTestPlatformViewVulkan::SupportsDynamicThreadMerging() { - return support_thread_merging_; -} - // |PlatformView| PointerDataDispatcherMaker ShellTestPlatformViewVulkan::GetDispatcherMaker() { return [](DefaultPointerDataDispatcher::Delegate& delegate) { From e4832a3048bcf613f58a8d9d7fd60f1d5e8b55e4 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 30 May 2023 11:20:55 -0700 Subject: [PATCH 137/147] Fix test parameter --- shell/common/shell_test_platform_view_vulkan.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index 5ff444495d5f0..cd89d06b8955d 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -79,6 +79,11 @@ ShellTestPlatformViewVulkan::CreateExternalViewEmbedder() { return shell_test_external_view_embedder_; } +// |PlatformView| +bool ShellTestPlatformViewVulkan::SupportsDynamicThreadMerging() { + return support_thread_merging_; +} + // |PlatformView| PointerDataDispatcherMaker ShellTestPlatformViewVulkan::GetDispatcherMaker() { return [](DefaultPointerDataDispatcher::Delegate& delegate) { From 3eb95bdfcf5e0d90523ebfc1b37c30f41c558369 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 30 May 2023 12:40:40 -0700 Subject: [PATCH 138/147] Fix compile --- shell/common/shell_test_platform_view_vulkan.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shell/common/shell_test_platform_view_vulkan.h b/shell/common/shell_test_platform_view_vulkan.h index 845604640d0fe..1bc3f98e20271 100644 --- a/shell/common/shell_test_platform_view_vulkan.h +++ b/shell/common/shell_test_platform_view_vulkan.h @@ -126,6 +126,9 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { // |PlatformView| std::shared_ptr CreateExternalViewEmbedder() override; + // |PlatformView| + bool SupportsDynamicThreadMerging() override; + // |PlatformView| std::unique_ptr CreateVSyncWaiter() override; From 6892eb62344ac5006abc76bdf4c77bd0d436bd96 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 30 May 2023 14:01:30 -0700 Subject: [PATCH 139/147] Move assigning view embedder to add surface --- shell/common/rasterizer.cc | 15 +++-- shell/common/rasterizer.h | 14 +---- shell/common/rasterizer_unittests.cc | 90 +++++++--------------------- shell/common/shell.cc | 9 +-- 4 files changed, 39 insertions(+), 89 deletions(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 30e533da2038a..bf2e7f6e527fd 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -152,7 +152,15 @@ void Rasterizer::NotifyLowMemoryWarning() const { context->performDeferredCleanup(std::chrono::milliseconds(0)); } -void Rasterizer::AddSurface(int64_t view_id, std::unique_ptr surface) { +void Rasterizer::AddSurface( + int64_t view_id, + std::unique_ptr surface, + std::shared_ptr view_embedder) { + if (!external_view_embedder_) { + external_view_embedder_ = view_embedder; + } else { + FML_DCHECK(external_view_embedder_ == view_embedder); + } bool insertion_happened = surfaces_ .try_emplace(/* map key=*/view_id, /*constructor args:*/ view_id, @@ -859,11 +867,6 @@ void Rasterizer::SetNextFrameCallback(const fml::closure& callback) { next_frame_callback_ = callback; } -void Rasterizer::SetExternalViewEmbedder( - const std::shared_ptr& view_embedder) { - external_view_embedder_ = view_embedder; -} - void Rasterizer::SetSnapshotSurfaceProducer( std::unique_ptr producer) { snapshot_surface_producer_ = std::move(producer); diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 65442b44f9a2e..1848a33a545d9 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -206,7 +206,9 @@ class Rasterizer final : public SnapshotDelegate, fml::TaskRunnerAffineWeakPtr GetSnapshotDelegate() const; - void AddSurface(int64_t view_id, std::unique_ptr surface); + void AddSurface(int64_t view_id, + std::unique_ptr surface, + std::shared_ptr view_embedder); void RemoveSurface(int64_t view_id); @@ -400,16 +402,6 @@ class Rasterizer final : public SnapshotDelegate, /// void SetNextFrameCallback(const fml::closure& callback); - //---------------------------------------------------------------------------- - /// @brief Set the External View Embedder. This is done on shell - /// initialization. This is non-null on platforms that support - /// embedding externally composited views. - /// - /// @param[in] view_embedder The external view embedder object. - /// - void SetExternalViewEmbedder( - const std::shared_ptr& view_embedder); - //---------------------------------------------------------------------------- /// @brief Set the snapshot surface producer. This is done on shell /// initialization. This is non-null on platforms that support taking diff --git a/shell/common/rasterizer_unittests.cc b/shell/common/rasterizer_unittests.cc index 6708125c84004..d803e1c082507 100644 --- a/shell/common/rasterizer_unittests.cc +++ b/shell/common/rasterizer_unittests.cc @@ -136,7 +136,7 @@ TEST(RasterizerTest, drawEmptyPipeline) { EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio), false); - rasterizer->AddSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface), nullptr); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -169,7 +169,6 @@ TEST(RasterizerTest, std::shared_ptr> external_view_embedder = std::make_shared>(); - rasterizer->SetExternalViewEmbedder(external_view_embedder); SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; @@ -199,7 +198,8 @@ TEST(RasterizerTest, .Times(1); rasterizer->Setup(std::move(studio), false); - rasterizer->AddSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface), + external_view_embedder); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -242,7 +242,6 @@ TEST( auto surface = std::make_unique>(); std::shared_ptr> external_view_embedder = std::make_shared>(); - rasterizer->SetExternalViewEmbedder(external_view_embedder); SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; auto surface_frame = std::make_unique( @@ -266,7 +265,8 @@ TEST( .Times(1); rasterizer->Setup(std::move(studio), /*support_thread_merging=*/true); - rasterizer->AddSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface), + external_view_embedder); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -313,7 +313,6 @@ TEST( std::shared_ptr> external_view_embedder = std::make_shared>(); - rasterizer->SetExternalViewEmbedder(external_view_embedder); SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; @@ -339,7 +338,8 @@ TEST( .Times(1); rasterizer->Setup(std::move(studio), /*support_thread_merging=*/true); - rasterizer->AddSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface), + external_view_embedder); auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique(/*config=*/LayerTree::Config(), @@ -381,7 +381,6 @@ TEST(RasterizerTest, std::shared_ptr> external_view_embedder = std::make_shared>(); - rasterizer->SetExternalViewEmbedder(external_view_embedder); SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; @@ -414,7 +413,8 @@ TEST(RasterizerTest, .Times(2); rasterizer->Setup(std::move(studio), /*support_thread_merging=*/true); - rasterizer->AddSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface), + external_view_embedder); auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique(/*config=*/LayerTree::Config(), @@ -436,52 +436,6 @@ TEST(RasterizerTest, rasterizer->DrawLastLayerTree(CreateFinishedBuildRecorder()); } -TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNoSurfaceIsSet) { - std::string test_name = - ::testing::UnitTest::GetInstance()->current_test_info()->name(); - ThreadHost thread_host("io.flutter.test." + test_name + ".", - ThreadHost::Type::Platform | ThreadHost::Type::RASTER | - ThreadHost::Type::IO | ThreadHost::Type::UI); - TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(), - thread_host.raster_thread->GetTaskRunner(), - thread_host.ui_thread->GetTaskRunner(), - thread_host.io_thread->GetTaskRunner()); - NiceMock delegate; - Settings settings; - ON_CALL(delegate, GetSettings()).WillByDefault(ReturnRef(settings)); - EXPECT_CALL(delegate, GetTaskRunners()) - .WillRepeatedly(ReturnRef(task_runners)); - auto rasterizer = std::make_unique(delegate); - - std::shared_ptr> external_view_embedder = - std::make_shared>(); - rasterizer->SetExternalViewEmbedder(external_view_embedder); - - EXPECT_CALL( - *external_view_embedder, - EndFrame(/*should_resubmit_frame=*/false, - /*raster_thread_merger=*/fml::RefPtr( - nullptr))) - .Times(0); - - fml::AutoResetWaitableEvent latch; - thread_host.raster_thread->GetTaskRunner()->PostTask([&] { - auto pipeline = std::make_shared(/*depth=*/10); - auto layer_tree = std::make_unique( - /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); - auto layer_tree_item = std::make_unique( - kDefaultViewId, std::move(layer_tree), CreateFinishedBuildRecorder(), - kDevicePixelRatio); - PipelineProduceResult result = - pipeline->Produce().Complete(std::move(layer_tree_item)); - EXPECT_TRUE(result.success); - auto no_discard = [](int64_t, LayerTree&) { return false; }; - rasterizer->Draw(pipeline, no_discard); - latch.Signal(); - }); - latch.Wait(); -} - TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNotUsedThisFrame) { std::string test_name = ::testing::UnitTest::GetInstance()->current_test_info()->name(); @@ -506,9 +460,9 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNotUsedThisFrame) { std::shared_ptr> external_view_embedder = std::make_shared>(); - rasterizer->SetExternalViewEmbedder(external_view_embedder); rasterizer->Setup(std::move(studio), false); - rasterizer->AddSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface), + external_view_embedder); EXPECT_CALL(*external_view_embedder, BeginFrame(/*frame_size=*/SkISize(), /*context=*/nullptr, @@ -566,9 +520,9 @@ TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenPipelineIsEmpty) { std::shared_ptr> external_view_embedder = std::make_shared>(); - rasterizer->SetExternalViewEmbedder(external_view_embedder); rasterizer->Setup(std::move(studio), false); - rasterizer->AddSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface), + external_view_embedder); EXPECT_CALL( *external_view_embedder, @@ -628,7 +582,7 @@ TEST(RasterizerTest, .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio), false); - rasterizer->AddSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface), nullptr); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -688,7 +642,7 @@ TEST( .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio), false); - rasterizer->AddSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface), nullptr); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -748,7 +702,7 @@ TEST( .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio), false); - rasterizer->AddSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface), nullptr); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -807,7 +761,7 @@ TEST( .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio), false); - rasterizer->AddSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface), nullptr); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -865,7 +819,7 @@ TEST( EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio), false); - rasterizer->AddSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface), nullptr); fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = std::make_shared(/*depth=*/10); @@ -947,7 +901,7 @@ TEST(RasterizerTest, thread_host.raster_thread->GetTaskRunner()->PostTask([&] { rasterizer->Setup(std::move(studio), false); - rasterizer->AddSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface), nullptr); auto pipeline = std::make_shared(/*depth=*/10); for (int i = 0; i < 2; i++) { auto layer_tree = std::make_unique( @@ -1003,7 +957,7 @@ TEST(RasterizerTest, TeardownFreesResourceCache) { EXPECT_CALL(*studio, GetContext()).WillRepeatedly(Return(context.get())); rasterizer->Setup(std::move(studio), false); - rasterizer->AddSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface), nullptr); EXPECT_EQ(context->getResourceCacheLimit(), 0ul); rasterizer->SetResourceCacheMaxBytes(10000000, false); @@ -1122,7 +1076,7 @@ TEST(RasterizerTest, presentationTimeSetWhenVsyncTargetInFuture) { thread_host.raster_thread->GetTaskRunner()->PostTask([&] { rasterizer->Setup(std::move(studio), false); - rasterizer->AddSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface), nullptr); auto pipeline = std::make_shared(/*depth=*/10); for (int i = 0; i < 2; i++) { auto layer_tree = std::make_unique( @@ -1205,7 +1159,7 @@ TEST(RasterizerTest, presentationTimeNotSetWhenVsyncTargetInPast) { thread_host.raster_thread->GetTaskRunner()->PostTask([&] { rasterizer->Setup(std::move(studio), false); - rasterizer->AddSurface(kDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kDefaultViewId, std::move(surface), nullptr); auto pipeline = std::make_shared(/*depth=*/10); auto layer_tree = std::make_unique( /*config=*/LayerTree::Config(), /*frame_size=*/SkISize()); diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 1eb566b493910..4f85cefbf26b9 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -682,8 +682,6 @@ bool Shell::Setup(std::unique_ptr platform_view, io_manager_ = io_manager; // Set the external view embedder for the rasterizer. - auto view_embedder = platform_view_->CreateExternalViewEmbedder(); - rasterizer_->SetExternalViewEmbedder(view_embedder); rasterizer_->SetSnapshotSurfaceProducer( platform_view_->CreateSnapshotSurfaceProducer()); @@ -803,13 +801,15 @@ void Shell::OnPlatformViewCreated() { ]() mutable { std::unique_ptr surface = platform_view->CreateSurface(kFlutterDefaultViewId); + auto view_embedder = platform_view->CreateExternalViewEmbedder(); if (rasterizer) { // Enables the thread merger which may be used by the external view // embedder. rasterizer->EnableThreadMergerIfNeeded(); rasterizer->Setup(std::move(studio), platform_view->SupportsDynamicThreadMerging()); - rasterizer->AddSurface(kFlutterDefaultViewId, std::move(surface)); + rasterizer->AddSurface(kFlutterDefaultViewId, std::move(surface), + view_embedder); } waiting_for_first_frame.store(true); @@ -2024,10 +2024,11 @@ void Shell::AddRenderSurface(int64_t view_id) { view_id // ]() mutable { if (platform_view && rasterizer) { + auto view_embedder = platform_view->CreateExternalViewEmbedder(); std::unique_ptr surface = platform_view->CreateSurface(view_id); if (surface) { - rasterizer->AddSurface(view_id, std::move(surface)); + rasterizer->AddSurface(view_id, std::move(surface), view_embedder); } } })); From cd7960ae13299b0f8afd7d9dfc54f8b69ca0140d Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 30 May 2023 21:07:53 -0700 Subject: [PATCH 140/147] View record --- .../macos/framework/Source/FlutterEngine.mm | 98 +++++++++++-------- 1 file changed, 55 insertions(+), 43 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index bc51c76d9afb0..79133a59f4a7a 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -77,6 +77,30 @@ - (instancetype)initWithConnection:(NSNumber*)connection #pragma mark - +@interface FlutterEngineViewRecord : NSObject + +- (instancetype)initWithViewController:(nonnull FlutterViewController*)controller; + +@property(nonatomic, weak) FlutterViewController* controller; + +@end + +@implementation FlutterEngineViewRecord { + __weak FlutterViewController* _controller; +} + +@synthesize controller = _controller; + +- (instancetype)initWithViewController:(FlutterViewController*)controller { + self = [super init]; + NSAssert(self, @"Super init cannot be nil"); + _controller = controller; + return self; +} +@end + +#pragma mark - + /** * Private interface declaration for FlutterEngine. */ @@ -366,9 +390,7 @@ @implementation FlutterEngine { std::unique_ptr _macOSCompositor; // The information of all views attached to this engine mapped from IDs. - // - // It can't use NSDictionary, because the values need to be weak references. - NSMapTable* _viewControllers; + NSMutableDictionary* _viewRecords; // FlutterCompositor is copied and used in embedder.cc. FlutterCompositor _compositor; @@ -429,7 +451,7 @@ - (instancetype)initWithName:(NSString*)labelPrefix _embedderAPI.struct_size = sizeof(FlutterEngineProcTable); FlutterEngineGetProcAddresses(&_embedderAPI); - _viewControllers = [NSMapTable weakToWeakObjectsMapTable]; + _viewRecords = [NSMutableDictionary dictionary]; _renderer = [[FlutterRenderer alloc] initWithFlutterEngine:self]; NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter]; @@ -471,7 +493,7 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint { return NO; } - if (!_allowHeadlessExecution && [_viewControllers count] == 0) { + if (!_allowHeadlessExecution && [_viewRecords count] == 0) { NSLog(@"Attempted to run an engine with no view controller without headless mode enabled."); return NO; } @@ -566,10 +588,8 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint { [self sendUserLocales]; // Update window metric for all view controllers. - NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator]; - FlutterViewController* nextViewController; - while ((nextViewController = [viewControllerEnumerator nextObject])) { - [self updateWindowMetricsForViewController:nextViewController]; + for (id viewId in _viewRecords) { + [self updateWindowMetricsForViewController:_viewRecords[viewId].controller]; } [self updateDisplayConfig]; @@ -609,41 +629,40 @@ - (void)registerViewController:(FlutterViewController*)controller forId:(Flutter NSAssert(controller != nil, @"The controller must not be nil."); NSAssert(![controller attached], @"The incoming view controller is already attached to an engine."); - NSAssert([_viewControllers objectForKey:@(viewId)] == nil, @"The requested view ID is occupied."); + NSAssert(_viewRecords[@(viewId)] == nil, @"The requested view ID is occupied."); [controller setUpWithEngine:self viewId:viewId threadSynchronizer:_threadSynchronizer]; NSAssert(controller.viewId == viewId, @"Failed to assign view ID."); - [_viewControllers setObject:controller forKey:@(viewId)]; + _viewRecords[@(viewId)] = [[FlutterEngineViewRecord alloc] initWithViewController:controller]; } - (void)deregisterViewControllerForId:(FlutterViewId)viewId { - FlutterViewController* oldController = [self viewControllerForId:viewId]; - if (oldController != nil) { - [oldController detachFromEngine]; - [_viewControllers removeObjectForKey:@(viewId)]; + FlutterEngineViewRecord* oldRecord = _viewRecords[@(viewId)]; + if (oldRecord.controller != nil) { + [oldRecord.controller detachFromEngine]; } + [_viewRecords removeObjectForKey:@(viewId)]; } - (void)shutDownIfNeeded { - if ([_viewControllers count] == 0 && !_allowHeadlessExecution) { + if ([_viewRecords count] == 0 && !_allowHeadlessExecution) { [self shutDownEngine]; } } - (FlutterViewController*)viewControllerForId:(FlutterViewId)viewId { - FlutterViewController* controller = [_viewControllers objectForKey:@(viewId)]; - NSAssert(controller == nil || controller.viewId == viewId, + FlutterEngineViewRecord* viewRecord = _viewRecords[@(viewId)]; + NSAssert(viewRecord == nil || viewRecord.controller.viewId == viewId, @"The stored controller has unexpected view ID."); - return controller; + return viewRecord.controller; } - (void)setViewController:(FlutterViewController*)controller { - FlutterViewController* currentController = - [_viewControllers objectForKey:@(kFlutterDefaultViewId)]; - if (currentController == controller) { + FlutterEngineViewRecord* currentViewRecord = _viewRecords[@(kFlutterDefaultViewId)]; + if (currentViewRecord.controller == controller) { // From nil to nil, or from non-nil to the same controller. return; } - if (currentController == nil && controller != nil) { + if (currentViewRecord == nil && controller != nil) { // From nil to non-nil. NSAssert(controller.engine == nil, @"Failed to set view controller to the engine: " @@ -652,9 +671,10 @@ - (void)setViewController:(FlutterViewController*)controller { @"you should use FlutterViewController#init(engine:, nibName, bundle:) instead.", controller.engine); [self registerViewController:controller forId:kFlutterDefaultViewId]; - } else if (currentController != nil && controller == nil) { - NSAssert(currentController.viewId == kFlutterDefaultViewId, - @"The default controller has an unexpected ID %llu", currentController.viewId); + } else if (currentViewRecord != nil && controller == nil) { + NSAssert(currentViewRecord.controller.viewId == kFlutterDefaultViewId, + @"The default controller has an unexpected ID %llu", + currentViewRecord.controller.viewId); // From non-nil to nil. [self deregisterViewControllerForId:kFlutterDefaultViewId]; [self shutDownIfNeeded]; @@ -665,7 +685,7 @@ - (void)setViewController:(FlutterViewController*)controller { @"The engine already has a default view controller %@. " @"If you wanted to make the default view render in a different window, " @"you should attach the current view controller to the window instead.", - [_viewControllers objectForKey:@(kFlutterDefaultViewId)]); + currentViewRecord.controller); } } @@ -842,10 +862,8 @@ - (void)setSemanticsEnabled:(BOOL)enabled { _semanticsEnabled = enabled; // Update all view controllers' bridges. - NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator]; - FlutterViewController* nextViewController; - while ((nextViewController = [viewControllerEnumerator nextObject])) { - [nextViewController notifySemanticsEnabledChanged]; + for (id viewId in _viewRecords) { + [_viewRecords[viewId].controller notifySemanticsEnabledChanged]; } _embedderAPI.UpdateSemanticsEnabled(_engine, _semanticsEnabled); @@ -926,10 +944,8 @@ - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message { } - (void)engineCallbackOnPreEngineRestart { - NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator]; - FlutterViewController* nextViewController; - while ((nextViewController = [viewControllerEnumerator nextObject])) { - [nextViewController onPreEngineRestart]; + for (id viewId in _viewRecords) { + [_viewRecords[viewId].controller onPreEngineRestart]; } } @@ -1026,19 +1042,15 @@ - (void)applicationWillTerminate:(NSNotification*)notification { - (void)windowDidChangeScreen:(NSNotification*)notification { // Update window metric for all view controllers since the display_id has // changed. - NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator]; - FlutterViewController* nextViewController; - while ((nextViewController = [viewControllerEnumerator nextObject])) { - [self updateWindowMetricsForViewController:nextViewController]; + for (id viewId in _viewRecords) { + [self updateWindowMetricsForViewController:_viewRecords[viewId].controller]; } } - (void)onAccessibilityStatusChanged:(NSNotification*)notification { BOOL enabled = [notification.userInfo[kEnhancedUserInterfaceKey] boolValue]; - NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator]; - FlutterViewController* nextViewController; - while ((nextViewController = [viewControllerEnumerator nextObject])) { - [nextViewController onAccessibilityStatusChanged:enabled]; + for (id viewId in _viewRecords) { + [_viewRecords[viewId].controller onAccessibilityStatusChanged:enabled]; } self.semanticsEnabled = enabled; From 52f0aa77d33b7b3f2059196b35f9b6920f5bf4f0 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 30 May 2023 22:15:22 -0700 Subject: [PATCH 141/147] Change to multiple view embedder and multiple compositor --- flow/embedded_views.cc | 3 +- flow/embedded_views.h | 3 +- shell/common/platform_view.h | 6 + shell/common/rasterizer.cc | 78 ++++++------ shell/common/rasterizer.h | 21 +++- shell/common/rasterizer_unittests.cc | 5 +- shell/common/shell.cc | 9 +- shell/common/shell.h | 8 +- .../shell_test_external_view_embedder.cc | 3 +- .../shell_test_external_view_embedder.h | 3 +- .../external_view_embedder.cc | 3 +- .../external_view_embedder.h | 3 +- .../external_view_embedder_unittests.cc | 27 ++--- .../framework/Source/FlutterPlatformViews.mm | 5 +- .../Source/FlutterPlatformViewsTest.mm | 33 +++--- .../Source/FlutterPlatformViews_Internal.h | 1 - .../darwin/ios/ios_external_view_embedder.h | 3 +- .../darwin/ios/ios_external_view_embedder.mm | 5 +- .../framework/Source/FlutterCompositor.h | 13 +- .../framework/Source/FlutterCompositor.mm | 12 +- .../framework/Source/FlutterCompositorTest.mm | 8 +- .../macos/framework/Source/FlutterEngine.mm | 112 +++++++++++------- .../framework/Source/FlutterEngineTest.mm | 1 + .../framework/Source/FlutterEngine_Internal.h | 1 - shell/platform/embedder/embedder.cc | 19 +-- shell/platform/embedder/embedder.h | 24 ++-- .../embedder_external_view_embedder.cc | 11 +- .../embedder_external_view_embedder.h | 6 +- shell/platform/embedder/embedder_layers.cc | 6 +- shell/platform/embedder/embedder_layers.h | 6 +- .../embedder/tests/embedder_config_builder.cc | 1 - .../flatland_external_view_embedder.cc | 3 +- .../flutter/flatland_external_view_embedder.h | 3 +- .../flutter/gfx_external_view_embedder.cc | 3 +- .../flutter/gfx_external_view_embedder.h | 3 +- ...atland_external_view_embedder_unittests.cc | 26 ++-- .../tests/flatland_platform_view_unittest.cc | 3 +- .../gfx_external_view_embedder_unittests.cc | 26 ++-- 38 files changed, 262 insertions(+), 244 deletions(-) diff --git a/flow/embedded_views.cc b/flow/embedded_views.cc index 2065ecd8747c3..406f3c476aa07 100644 --- a/flow/embedded_views.cc +++ b/flow/embedded_views.cc @@ -43,8 +43,7 @@ bool DisplayListEmbedderViewSlice::recording_ended() { } void ExternalViewEmbedder::SubmitFrame(GrDirectContext* context, - std::unique_ptr frame, - int64_t window_view_id) { + std::unique_ptr frame) { frame->Submit(); }; diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 37743843eeabf..f382c161c6451 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -415,8 +415,7 @@ class ExternalViewEmbedder { // // It can also allocate frames for overlay surfaces to compose hybrid views. virtual void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame, - int64_t window_view_id); + std::unique_ptr frame); // This method provides the embedder a way to do additional tasks after // |SubmitFrame|. For example, merge task runners if `should_resubmit_frame` diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 287ba91330ad4..5c85107b547e4 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -688,6 +688,12 @@ class PlatformView { /// Shell::AddRenderSurface. virtual std::shared_ptr CreateExternalViewEmbedder(); + /// Whether the embedder should support dynamic thread merging. + /// + /// Returning `true` results a |RasterThreadMerger| instance to be created by + /// |Rasterizer|. + /// * See also |BegineFrame| and |EndFrame| of |ExternalViewEmbedder| for + /// getting the |RasterThreadMerger| instance. virtual bool SupportsDynamicThreadMerging(); //-------------------------------------------------------------------------- diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index a75ad8ab10f46..7eaecabffb9fc 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -39,6 +39,7 @@ Rasterizer::Rasterizer(Delegate& delegate, user_override_resource_cache_bytes_(false), snapshot_controller_( SnapshotController::Make(*this, delegate.GetSettings())), + requires_view_embedder_(false), weak_factory_(this) { FML_DCHECK(compositor_context_); } @@ -92,8 +93,9 @@ void Rasterizer::Setup(std::unique_ptr studio, } void Rasterizer::TeardownExternalViewEmbedder() { - if (external_view_embedder_) { - external_view_embedder_->Teardown(); + for (auto& surface_record : surfaces_) { + surface_record.second.view_embedder->Teardown(); + surface_record.second.view_embedder = nullptr; } } @@ -153,15 +155,20 @@ void Rasterizer::AddSurface( int64_t view_id, std::unique_ptr surface, std::shared_ptr view_embedder) { - if (!external_view_embedder_) { - external_view_embedder_ = view_embedder; - } else { - FML_DCHECK(external_view_embedder_ == view_embedder); + // Only allows having no view embedders if there is one surface and there has + // never been an view embedder. + if (!requires_view_embedder_) { + if (view_embedder || !surfaces_.empty()) { + requires_view_embedder_ = true; + } + } + if (requires_view_embedder_) { + FML_DCHECK(view_embedder); } bool insertion_happened = surfaces_ .try_emplace(/* map key=*/view_id, /*constructor args:*/ view_id, - std::move(surface)) + view_embedder, std::move(surface)) .second; if (!insertion_happened) { FML_DLOG(INFO) << "Rasterizer::AddSurface called with an existing view ID " @@ -202,6 +209,7 @@ int Rasterizer::DrawLastLayerTree( bool should_resubmit_frame = false; for (auto& [view_id, surface_record] : surfaces_) { Surface* surface = surface_record.surface.get(); + auto view_embedder = surface_record.view_embedder; flutter::LayerTree* layer_tree = surface_record.last_tree.get(); float device_pixel_ratio = surface_record.last_pixel_ratio; if (!surface || !layer_tree) { @@ -219,14 +227,14 @@ int Rasterizer::DrawLastLayerTree( should_resubmit_frame = should_resubmit_frame || ShouldResubmitFrame(raster_status); success_count += 1; - } - // EndFrame should perform cleanups for the external_view_embedder. - if (external_view_embedder_ && external_view_embedder_->GetUsedThisFrame()) { - external_view_embedder_->SetUsedThisFrame(false); - external_view_embedder_->EndFrame(should_resubmit_frame, - raster_thread_merger_); + // EndFrame should perform cleanups for the external_view_embedder. + if (view_embedder && view_embedder->GetUsedThisFrame()) { + view_embedder->SetUsedThisFrame(false); + view_embedder->EndFrame(should_resubmit_frame, raster_thread_merger_); + } } + return success_count; } @@ -252,6 +260,7 @@ RasterStatus Rasterizer::Draw( std::move(item->frame_timings_recorder); float device_pixel_ratio = item->device_pixel_ratio; int64_t view_id = item->view_id; + draw_result.view_id = view_id; if (discard_callback(view_id, *layer_tree.get())) { draw_result.raster_status = RasterStatus::kDiscarded; } else { @@ -280,10 +289,12 @@ RasterStatus Rasterizer::Draw( } // EndFrame should perform cleanups for the external_view_embedder. - if (external_view_embedder_ && external_view_embedder_->GetUsedThisFrame()) { - external_view_embedder_->SetUsedThisFrame(false); - external_view_embedder_->EndFrame(should_resubmit_frame, - raster_thread_merger_); + auto surface_record = surfaces_.find(draw_result.view_id); + FML_DCHECK(surface_record != surfaces_.end()); + auto view_embedder = surface_record->second.view_embedder; + if (view_embedder && view_embedder->GetUsedThisFrame()) { + view_embedder->SetUsedThisFrame(false); + view_embedder->EndFrame(should_resubmit_frame, raster_thread_merger_); } // Consume as many pipeline items as possible. But yield the event loop @@ -578,19 +589,19 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( float device_pixel_ratio, SurfaceRecord* surface_record) { Surface* surface = surface_record->surface.get(); + auto view_embedder = surface_record->view_embedder; FML_DCHECK(surface); compositor_context_->ui_time().SetLapTime( frame_timings_recorder.GetBuildDuration()); DlCanvas* embedder_root_canvas = nullptr; - if (external_view_embedder_) { - FML_DCHECK(!external_view_embedder_->GetUsedThisFrame()); - external_view_embedder_->SetUsedThisFrame(true); - external_view_embedder_->BeginFrame( - layer_tree->frame_size(), studio_->GetContext(), device_pixel_ratio, - raster_thread_merger_); - embedder_root_canvas = external_view_embedder_->GetRootCanvas(); + if (view_embedder) { + FML_DCHECK(!view_embedder->GetUsedThisFrame()); + view_embedder->SetUsedThisFrame(true); + view_embedder->BeginFrame(layer_tree->frame_size(), studio_->GetContext(), + device_pixel_ratio, raster_thread_merger_); + embedder_root_canvas = view_embedder->GetRootCanvas(); } frame_timings_recorder.RecordRasterStart(fml::TimePoint::Now()); @@ -617,11 +628,11 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( embedder_root_canvas ? embedder_root_canvas : frame->Canvas(); auto compositor_frame = compositor_context_->AcquireFrame( - studio_->GetContext(), // skia GrContext - root_surface_canvas, // root surface canvas - external_view_embedder_.get(), // external view embedder - root_surface_transformation, // root surface transformation - true, // instrumentation enabled + studio_->GetContext(), // skia GrContext + root_surface_canvas, // root surface canvas + view_embedder.get(), // external view embedder + root_surface_transformation, // root surface transformation + true, // instrumentation enabled frame->framebuffer_info() .supports_readback, // surface supports pixel reads raster_thread_merger_, // thread merger @@ -636,12 +647,12 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( // for accurate performance metrics. if (frame->framebuffer_info().supports_partial_repaint && !layer_tree->is_leaf_layer_tracing_enabled()) { - // Disable partial repaint if external_view_embedder_ SubmitFrame is + // Disable partial repaint if view_embedder SubmitFrame is // involved - ExternalViewEmbedder unconditionally clears the entire // surface and also partial repaint with platform view present is // something that still need to be figured out. bool force_full_repaint = - external_view_embedder_ && + view_embedder && (!raster_thread_merger_ || raster_thread_merger_->IsMerged()); damage = std::make_unique(); @@ -687,11 +698,10 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( frame->set_submit_info(submit_info); - if (external_view_embedder_ && + if (view_embedder && (!raster_thread_merger_ || raster_thread_merger_->IsMerged())) { FML_DCHECK(!frame->IsSubmitted()); - external_view_embedder_->SubmitFrame( - studio_->GetContext(), std::move(frame), surface_record->view_id); + view_embedder->SubmitFrame(studio_->GetContext(), std::move(frame)); } else { frame->Submit(); } diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 1848a33a545d9..20a7db54ee0ac 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -206,6 +206,8 @@ class Rasterizer final : public SnapshotDelegate, fml::TaskRunnerAffineWeakPtr GetSnapshotDelegate() const; + //---------------------------------------------------------------------------- + /// @brief Add a surface, implicit or not. void AddSurface(int64_t view_id, std::unique_ptr surface, std::shared_ptr view_embedder); @@ -504,17 +506,22 @@ class Rasterizer final : public SnapshotDelegate, // front of the pipeline. struct DoDrawResult { RasterStatus raster_status = RasterStatus::kFailed; - + int64_t view_id; std::unique_ptr resubmitted_layer_tree_item; }; struct SurfaceRecord { - SurfaceRecord(int64_t view_id, std::unique_ptr surface) - : view_id(view_id), surface(std::move(surface)) {} + SurfaceRecord(int64_t view_id, + std::shared_ptr view_embedder, + std::unique_ptr surface) + : view_id(view_id), + view_embedder(std::move(view_embedder)), + surface(std::move(surface)) {} int64_t view_id; - + std::shared_ptr view_embedder; std::unique_ptr surface; + // This is the information for the last successfully drawing. // // Sometimes, it may be necessary to render the same frame again without @@ -630,8 +637,12 @@ class Rasterizer final : public SnapshotDelegate, bool user_override_resource_cache_bytes_; std::optional max_cache_bytes_; fml::RefPtr raster_thread_merger_; - std::shared_ptr external_view_embedder_; std::unique_ptr snapshot_controller_; + // The Rasterizer allows having no view embedder, but only in a legacy mode + // where there is only one surface (the implicit surface), and no view + // embedder is provided ever. In other cases, the Rasterizer should always + // have a view embedder. + bool requires_view_embedder_; // WeakPtrFactory must be the last member. fml::TaskRunnerAffineWeakPtrFactory weak_factory_; diff --git a/shell/common/rasterizer_unittests.cc b/shell/common/rasterizer_unittests.cc index d803e1c082507..0c7226a23eb2c 100644 --- a/shell/common/rasterizer_unittests.cc +++ b/shell/common/rasterizer_unittests.cc @@ -84,10 +84,9 @@ class MockExternalViewEmbedder : public ExternalViewEmbedder { PostPrerollResult( fml::RefPtr raster_thread_merger)); MOCK_METHOD1(CompositeEmbeddedView, DlCanvas*(int64_t view_id)); - MOCK_METHOD3(SubmitFrame, + MOCK_METHOD2(SubmitFrame, void(GrDirectContext* context, - std::unique_ptr frame, - int64_t window_view_id)); + std::unique_ptr frame)); MOCK_METHOD2(EndFrame, void(bool should_resubmit_frame, fml::RefPtr raster_thread_merger)); diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 4f85cefbf26b9..96ed75ad05aef 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -2001,17 +2001,16 @@ bool Shell::OnServiceProtocolReloadAssetFonts( return true; } -void Shell::AddRenderSurface(int64_t view_id) { +void Shell::AddRenderSurface( + int64_t view_id, + std::unique_ptr external_view_embedder) { TRACE_EVENT0("flutter", "Shell::AddRenderSurface"); FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + FML_DCHECK(view_id != kFlutterDefaultViewId); if (!engine_) { return; } - if (view_id == kFlutterDefaultViewId) { - return; - } - task_runners_.GetUITaskRunner()->PostTask([engine = engine_->GetWeakPtr(), // view_id // ] { engine->AddView(view_id); }); diff --git a/shell/common/shell.h b/shell/common/shell.h index 78cbd49d626fa..1f5b1d2974630 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -298,7 +298,13 @@ class Shell final : public PlatformView::Delegate, /// bool IsSetup() const; - void AddRenderSurface(int64_t view_id); + /// @brief Add a non-implicit render surface. The implicit render surface + /// is created in OnPlatformViewCreated. + /// @param view_id + /// @param external_view_embedder Must not be null. + void AddRenderSurface( + int64_t view_id, + std::unique_ptr external_view_embedder); void RemoveRenderSurface(int64_t view_id); //---------------------------------------------------------------------------- diff --git a/shell/common/shell_test_external_view_embedder.cc b/shell/common/shell_test_external_view_embedder.cc index f9ece8067b3c3..9484c9a607c18 100644 --- a/shell/common/shell_test_external_view_embedder.cc +++ b/shell/common/shell_test_external_view_embedder.cc @@ -89,8 +89,7 @@ DlCanvas* ShellTestExternalViewEmbedder::CompositeEmbeddedView( // |ExternalViewEmbedder| void ShellTestExternalViewEmbedder::SubmitFrame( GrDirectContext* context, - std::unique_ptr frame, - int64_t view_id) { + std::unique_ptr frame) { if (!frame) { return; } diff --git a/shell/common/shell_test_external_view_embedder.h b/shell/common/shell_test_external_view_embedder.h index cd5c7481b40bf..414d3ff2f2f2e 100644 --- a/shell/common/shell_test_external_view_embedder.h +++ b/shell/common/shell_test_external_view_embedder.h @@ -74,8 +74,7 @@ class ShellTestExternalViewEmbedder final : public ExternalViewEmbedder { // |ExternalViewEmbedder| void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame, - int64_t view_id) override; + std::unique_ptr frame) override; // |ExternalViewEmbedder| void EndFrame( 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 cf846608196ef..0bc41ed13d723 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder.cc @@ -66,8 +66,7 @@ SkRect AndroidExternalViewEmbedder::GetViewRect(int64_t view_id) const { // |ExternalViewEmbedder| void AndroidExternalViewEmbedder::SubmitFrame( GrDirectContext* context, - std::unique_ptr frame, - int64_t window_view_id) { + std::unique_ptr frame) { TRACE_EVENT0("flutter", "AndroidExternalViewEmbedder::SubmitFrame"); if (!FrameHasPlatformLayers()) { 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 7cd72ca3d8173..42aa48235a909 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.h +++ b/shell/platform/android/external_view_embedder/external_view_embedder.h @@ -46,8 +46,7 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder { // |ExternalViewEmbedder| void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame, - int64_t view_id) override; + std::unique_ptr frame) override; // |ExternalViewEmbedder| PostPrerollResult PostPrerollAction( 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 ac3148eba8770..1d38f73069874 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 @@ -308,8 +308,7 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame) { }, /*frame_size=*/SkISize::Make(800, 600)); - embedder->SubmitFrame(gr_context.get(), std::move(surface_frame), - kDefaultViewId); + embedder->SubmitFrame(gr_context.get(), std::move(surface_frame)); // Submits frame if no Android view in the current frame. EXPECT_TRUE(did_submit_frame); // Doesn't resubmit frame. @@ -377,8 +376,7 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame) { }, /*frame_size=*/SkISize::Make(800, 600)); - embedder->SubmitFrame(gr_context.get(), std::move(surface_frame), - kDefaultViewId); + embedder->SubmitFrame(gr_context.get(), std::move(surface_frame)); // Doesn't submit frame if there aren't Android views in the previous frame. EXPECT_FALSE(did_submit_frame); // Resubmits frame. @@ -443,8 +441,7 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - embedder->SubmitFrame(gr_context.get(), std::move(surface_frame), - kDefaultViewId); + embedder->SubmitFrame(gr_context.get(), std::move(surface_frame)); // Submits frame if there are Android views in the previous frame. EXPECT_TRUE(did_submit_frame); // Doesn't resubmit frame. @@ -551,8 +548,7 @@ TEST(AndroidExternalViewEmbedder, OverlayCoverTwoPlatformViews) { }, /*frame_size=*/SkISize::Make(800, 600)); - embedder->SubmitFrame(gr_context.get(), std::move(surface_frame), - kDefaultViewId); + embedder->SubmitFrame(gr_context.get(), std::move(surface_frame)); EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); @@ -655,8 +651,7 @@ TEST(AndroidExternalViewEmbedder, SubmitFrameOverlayComposition) { }, /*frame_size=*/SkISize::Make(800, 600)); - embedder->SubmitFrame(gr_context.get(), std::move(surface_frame), - kDefaultViewId); + embedder->SubmitFrame(gr_context.get(), std::move(surface_frame)); EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); @@ -724,8 +719,7 @@ TEST(AndroidExternalViewEmbedder, SubmitFramePlatformViewWithoutAnyOverlay) { }, /*frame_size=*/SkISize::Make(800, 600)); - embedder->SubmitFrame(gr_context.get(), std::move(surface_frame), - kDefaultViewId); + embedder->SubmitFrame(gr_context.get(), std::move(surface_frame)); EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); @@ -826,8 +820,7 @@ TEST(AndroidExternalViewEmbedder, DestroyOverlayLayersOnSizeChange) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - embedder->SubmitFrame(gr_context.get(), std::move(surface_frame), - kDefaultViewId); + embedder->SubmitFrame(gr_context.get(), std::move(surface_frame)); EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); @@ -914,8 +907,7 @@ TEST(AndroidExternalViewEmbedder, DoesNotDestroyOverlayLayersOnSizeChange) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - embedder->SubmitFrame(gr_context.get(), std::move(surface_frame), - kDefaultViewId); + embedder->SubmitFrame(gr_context.get(), std::move(surface_frame)); EXPECT_CALL(*jni_mock, FlutterViewEndFrame()); embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); @@ -1028,8 +1020,7 @@ TEST(AndroidExternalViewEmbedder, Teardown) { SkSurfaces::Null(1000, 1000), framebuffer_info, [](const SurfaceFrame& surface_frame, DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - embedder->SubmitFrame(gr_context.get(), std::move(surface_frame), - kDefaultViewId); + embedder->SubmitFrame(gr_context.get(), std::move(surface_frame)); embedder->EndFrame(/*should_resubmit_frame=*/false, raster_thread_merger); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 5ac65ae7607b7..784045f1b6be9 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -653,7 +653,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, bool FlutterPlatformViewsController::SubmitFrame(GrDirectContext* gr_context, const std::shared_ptr& ios_context, - int64_t render_view_id, std::unique_ptr frame) { TRACE_EVENT0("flutter", "FlutterPlatformViewsController::SubmitFrame"); @@ -728,7 +727,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, // Get a new host layer. std::shared_ptr layer = GetLayer(gr_context, // ios_context, // - render_view_id, // slice, // joined_rect, // current_platform_view_id, // @@ -800,7 +798,6 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, std::shared_ptr FlutterPlatformViewsController::GetLayer( GrDirectContext* gr_context, const std::shared_ptr& ios_context, - int64_t render_view_id, EmbedderViewSlice* slice, SkRect rect, int64_t view_id, @@ -827,7 +824,7 @@ static bool ClipRRectContainsPlatformViewBoundingRect(const SkRRect& clip_rrect, overlay_view.accessibilityIdentifier = [NSString stringWithFormat:@"platform_view[%lld].overlay_view[%lld]", view_id, overlay_id]; - std::unique_ptr frame = layer->surface->AcquireFrame(render_view_id, frame_size_); + std::unique_ptr frame = layer->surface->AcquireFrame(frame_size_); // If frame is null, AcquireFrame already printed out an error message. if (!frame) { return layer; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm index cc573a574a5dc..4ce072477d475 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm @@ -2272,8 +2272,8 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin nullptr, framebuffer_info, [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return false; }, /*frame_size=*/SkISize::Make(800, 600)); - XCTAssertFalse(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, kDefaultViewId, - std::move(mock_surface))); + XCTAssertFalse( + flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); auto embeddedViewParams_2 = std::make_unique(finalMatrix, SkSize::Make(300, 300), stack); @@ -2283,7 +2283,7 @@ - (void)testFlutterPlatformViewControllerSubmitFrameWithoutFlutterViewNotCrashin nullptr, framebuffer_info, [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, kDefaultViewId, + XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface_submit_true))); } @@ -2457,8 +2457,8 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, kDefaultViewId, - std::move(mock_surface))); + XCTAssertTrue( + flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view. UIView* clippingView1 = view1.superview.superview; UIView* clippingView2 = view2.superview.superview; @@ -2484,8 +2484,8 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { std::move(mock_sk_surface), framebuffer_info, [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, kDefaultViewId, - std::move(mock_surface))); + XCTAssertTrue( + flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] > [flutterView.subviews indexOfObject:clippingView2], @"The first clipping view should be added after the second clipping view."); @@ -2555,8 +2555,8 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, kDefaultViewId, - std::move(mock_surface))); + XCTAssertTrue( + flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); // platform view is wrapped by touch interceptor, which itself is wrapped by clipping view. UIView* clippingView1 = view1.superview.superview; UIView* clippingView2 = view2.superview.superview; @@ -2582,8 +2582,8 @@ - (void)testFlutterPlatformViewControllerBeginFrameShouldResetCompisitionOrder { std::move(mock_sk_surface), framebuffer_info, [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, kDefaultViewId, - std::move(mock_surface))); + XCTAssertTrue( + flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); XCTAssertTrue([flutterView.subviews indexOfObject:clippingView1] < [flutterView.subviews indexOfObject:clippingView2], @"The first clipping view should be added before the second clipping view."); @@ -2921,8 +2921,8 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { std::move(mock_sk_surface), framebuffer_info, [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, kDefaultViewId, - std::move(mock_surface))); + XCTAssertTrue( + flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); // Disposing won't remove embedded views until the view is removed from the composition_order_ XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 2UL); @@ -2948,8 +2948,8 @@ - (void)testDisposingViewInCompositionOrderDoNotCrash { std::move(mock_sk_surface), framebuffer_info, [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - XCTAssertTrue(flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, kDefaultViewId, - std::move(mock_surface))); + XCTAssertTrue( + flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface))); // Disposing won't remove embedded views until the view is removed from the composition_order_ XCTAssertEqual(flutterPlatformViewsController->EmbeddedViewCount(), 1UL); @@ -3015,8 +3015,7 @@ - (void)testOnlyPlatformViewsAreRemovedWhenReset { [](const flutter::SurfaceFrame& surface_frame, flutter::DlCanvas* canvas) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); - flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, kDefaultViewId, - std::move(mock_surface)); + flutterPlatformViewsController->SubmitFrame(nullptr, nullptr, std::move(mock_surface)); UIView* someView = [[[UIView alloc] init] autorelease]; [mockFlutterView addSubview:someView]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 7315f828f3038..80d3c92dfe612 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -253,7 +253,6 @@ class FlutterPlatformViewsController { bool SubmitFrame(GrDirectContext* gr_context, const std::shared_ptr& ios_context, - int64_t render_view_id, std::unique_ptr frame); void OnMethodCall(FlutterMethodCall* call, FlutterResult& result); diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.h b/shell/platform/darwin/ios/ios_external_view_embedder.h index 1192dba92d02e..b785d18103faf 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.h +++ b/shell/platform/darwin/ios/ios_external_view_embedder.h @@ -51,8 +51,7 @@ class IOSExternalViewEmbedder : public ExternalViewEmbedder { // |ExternalViewEmbedder| void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame, - int64_t window_view_id) override; + std::unique_ptr frame) override; // |ExternalViewEmbedder| void EndFrame( diff --git a/shell/platform/darwin/ios/ios_external_view_embedder.mm b/shell/platform/darwin/ios/ios_external_view_embedder.mm index 98713d914e1a7..8aded863ae4ab 100644 --- a/shell/platform/darwin/ios/ios_external_view_embedder.mm +++ b/shell/platform/darwin/ios/ios_external_view_embedder.mm @@ -67,11 +67,10 @@ // |ExternalViewEmbedder| void IOSExternalViewEmbedder::SubmitFrame(GrDirectContext* context, - std::unique_ptr frame, - int64_t window_view_id) { + std::unique_ptr frame) { TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::SubmitFrame"); FML_CHECK(platform_views_controller_); - platform_views_controller_->SubmitFrame(context, ios_context_, window_view_id, std::move(frame)); + platform_views_controller_->SubmitFrame(context, ios_context_, std::move(frame)); TRACE_EVENT0("flutter", "IOSExternalViewEmbedder::DidSubmitFrame"); } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h b/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h index ce22bb9f54916..fd96194b94ad1 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h @@ -27,8 +27,9 @@ class FlutterCompositor { // The view_provider is used to query FlutterViews from view IDs, // which are used for presenting and creating backing stores. // It must not be null, and is typically FlutterViewEngineProvider. - explicit FlutterCompositor(id view_provider, - FlutterPlatformViewController* platform_views_controller); + FlutterCompositor(id view_provider, + int64_t view_id, + FlutterPlatformViewController* platform_views_controller); ~FlutterCompositor() = default; @@ -47,9 +48,9 @@ class FlutterCompositor { bool CreateBackingStore(const FlutterBackingStoreConfig* config, FlutterBackingStore* backing_store_out); - // Presents the FlutterLayers by updating the FlutterView specified by - // `view_id` using the layer content. Sets frame_started_ to false. - bool Present(FlutterViewId view_id, const FlutterLayer** layers, size_t layers_count); + // Presents the FlutterLayers by updating the FlutterView. Sets frame_started_ + // to false. + bool Present(const FlutterLayer** layers, size_t layers_count); private: void PresentPlatformViews(FlutterView* default_base_view, @@ -63,8 +64,8 @@ class FlutterCompositor { const FlutterLayer* layer, size_t layer_position); - // Where the compositor can query FlutterViews. Must not be null. id const view_provider_; + int64_t view_id_; // The controller used to manage creation and deletion of platform views. const FlutterPlatformViewController* platform_view_controller_; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm b/shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm index 79ff85b4adb4e..38e4219aa67cb 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm @@ -10,16 +10,18 @@ namespace flutter { FlutterCompositor::FlutterCompositor(id view_provider, + int64_t view_id, FlutterPlatformViewController* platform_view_controller) : view_provider_(view_provider), + view_id_(view_id), platform_view_controller_(platform_view_controller), mutator_views_([NSMapTable strongToStrongObjectsMapTable]) { - FML_CHECK(view_provider != nullptr) << "view_provider cannot be nullptr"; + FML_CHECK(view_provider_ != nullptr) << "view provider cannot be nullptr"; } bool FlutterCompositor::CreateBackingStore(const FlutterBackingStoreConfig* config, FlutterBackingStore* backing_store_out) { - FlutterView* view = [view_provider_ viewForId:config->view_id]; + FlutterView* view = [view_provider_ viewForId:view_id_]; if (!view) { return false; } @@ -34,10 +36,8 @@ return true; } -bool FlutterCompositor::Present(FlutterViewId view_id, - const FlutterLayer** layers, - size_t layers_count) { - FlutterView* view = [view_provider_ viewForId:view_id]; +bool FlutterCompositor::Present(const FlutterLayer** layers, size_t layers_count) { + FlutterView* view = [view_provider_ viewForId:view_id_]; if (!view) { return false; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterCompositorTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterCompositorTest.mm index e8903c1667c79..60d82d065591e 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterCompositorTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterCompositorTest.mm @@ -87,7 +87,7 @@ - (nullable FlutterView*)viewForId:(FlutterViewId)viewId { TEST(FlutterCompositorTest, TestCreate) { std::unique_ptr macos_compositor = - std::make_unique(MockViewProvider(), + std::make_unique(MockViewProvider(), kFlutterDefaultViewId, /*platform_view_controller*/ nullptr); FlutterBackingStore backing_store; @@ -95,7 +95,6 @@ - (nullable FlutterView*)viewForId:(FlutterViewId)viewId { config.struct_size = sizeof(FlutterBackingStoreConfig); config.size.width = 800; config.size.height = 600; - config.view_id = 0; ASSERT_TRUE(macos_compositor->CreateBackingStore(&config, &backing_store)); ASSERT_EQ(backing_store.type, kFlutterBackingStoreTypeMetal); @@ -113,7 +112,7 @@ - (nullable FlutterView*)viewForId:(FlutterViewId)viewId { }; std::unique_ptr macos_compositor = - std::make_unique(MockViewProvider(onPresent), + std::make_unique(MockViewProvider(onPresent), kFlutterDefaultViewId, /*platform_view_controller*/ nullptr); FlutterBackingStore backing_store; @@ -121,7 +120,6 @@ - (nullable FlutterView*)viewForId:(FlutterViewId)viewId { config.struct_size = sizeof(FlutterBackingStoreConfig); config.size.width = 800; config.size.height = 600; - config.view_id = 0; macos_compositor->CreateBackingStore(&config, &backing_store); FlutterLayer layers[] = {{ @@ -133,7 +131,7 @@ - (nullable FlutterView*)viewForId:(FlutterViewId)viewId { }}; const FlutterLayer* layers_ptr = layers; - macos_compositor->Present(kFlutterDefaultViewId, &layers_ptr, 1); + macos_compositor->Present(&layers_ptr, 1); ASSERT_EQ(presentedSurfaces.count, 1ul); } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 79133a59f4a7a..e608b4ef121b7 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -47,6 +47,36 @@ static FlutterLocale FlutterLocaleFromNSLocale(NSLocale* locale) { @"NSApplicationDidChangeAccessibilityEnhancedUserInterfaceNotification"; static NSString* const kEnhancedUserInterfaceKey = @"AXEnhancedUserInterface"; +static FlutterCompositor createFlutterCompositorFor(flutter::FlutterCompositor* macOSCompositor) { + FlutterCompositor compositor; + compositor = {}; + compositor.struct_size = sizeof(FlutterCompositor); + compositor.user_data = macOSCompositor; + + compositor.create_backing_store_callback = [](const FlutterBackingStoreConfig* config, // + FlutterBackingStore* backing_store_out, // + void* user_data // + ) { + return reinterpret_cast(user_data)->CreateBackingStore( + config, backing_store_out); + }; + + compositor.collect_backing_store_callback = [](const FlutterBackingStore* backing_store, // + void* user_data // + ) { return true; }; + + compositor.present_layers_callback = [](const FlutterLayer** layers, // + size_t layers_count, // + void* user_data // + ) { + return reinterpret_cast(user_data)->Present(layers, layers_count); + }; + + compositor.avoid_backing_store_cache = true; + + return compositor; +} + /// Clipboard plain text format. constexpr char kTextPlainFormat[] = "text/plain"; @@ -79,7 +109,10 @@ - (instancetype)initWithConnection:(NSNumber*)connection @interface FlutterEngineViewRecord : NSObject -- (instancetype)initWithViewController:(nonnull FlutterViewController*)controller; +- (nullable instancetype)initWithViewController:(nonnull FlutterViewController*)controller + macOSCompositor: + (std::shared_ptr)macOSCompositor + compositor:(FlutterCompositor)compositor; @property(nonatomic, weak) FlutterViewController* controller; @@ -87,14 +120,25 @@ - (instancetype)initWithViewController:(nonnull FlutterViewController*)controlle @implementation FlutterEngineViewRecord { __weak FlutterViewController* _controller; + + // _macOSCompositor is created when the engine is created and its destruction is handled by ARC + // when the engine is destroyed. + std::shared_ptr _macOSCompositor; + + // FlutterCompositor is copied and used in embedder.cc. + FlutterCompositor _compositor; } @synthesize controller = _controller; -- (instancetype)initWithViewController:(FlutterViewController*)controller { +- (instancetype)initWithViewController:(FlutterViewController*)controller + macOSCompositor:(std::shared_ptr)macOSCompositor + compositor:(FlutterCompositor)compositor { self = [super init]; NSAssert(self, @"Super init cannot be nil"); _controller = controller; + _macOSCompositor = macOSCompositor; + _compositor = compositor; return self; } @end @@ -385,15 +429,15 @@ @implementation FlutterEngine { // Pointer to the Dart AOT snapshot and instruction data. _FlutterEngineAOTData* _aotData; - // _macOSCompositor is created when the engine is created and its destruction is handled by ARC - // when the engine is destroyed. - std::unique_ptr _macOSCompositor; - // The information of all views attached to this engine mapped from IDs. NSMutableDictionary* _viewRecords; + // _macOSCompositor is created when the engine is created and its destruction is handled by ARC + // when the engine is destroyed. + std::shared_ptr _implicitMacOSCompositor; + // FlutterCompositor is copied and used in embedder.cc. - FlutterCompositor _compositor; + FlutterCompositor _implicitCompositor; // Method channel for platform view functions. These functions include creating, disposing and // mutating a platform view. @@ -564,7 +608,11 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint { flutterArguments.aot_data = _aotData; } - flutterArguments.compositor = [self createFlutterCompositor]; + _implicitMacOSCompositor = std::make_shared( + [[FlutterViewEngineProvider alloc] initWithEngine:self], kFlutterDefaultViewId, + _platformViewController); + _implicitCompositor = createFlutterCompositorFor(_implicitMacOSCompositor.get()); + flutterArguments.compositor = &_implicitCompositor; flutterArguments.on_pre_engine_restart_callback = [](void* user_data) { FlutterEngine* engine = (__bridge FlutterEngine*)user_data; @@ -632,7 +680,17 @@ - (void)registerViewController:(FlutterViewController*)controller forId:(Flutter NSAssert(_viewRecords[@(viewId)] == nil, @"The requested view ID is occupied."); [controller setUpWithEngine:self viewId:viewId threadSynchronizer:_threadSynchronizer]; NSAssert(controller.viewId == viewId, @"Failed to assign view ID."); - _viewRecords[@(viewId)] = [[FlutterEngineViewRecord alloc] initWithViewController:controller]; + const bool isImplicitView = viewId == kFlutterDefaultViewId; + auto macOSCompositor = isImplicitView + ? _implicitMacOSCompositor + : std::make_shared( + [[FlutterViewEngineProvider alloc] initWithEngine:self], viewId, + _platformViewController); + auto compositor = + isImplicitView ? _implicitCompositor : createFlutterCompositorFor(macOSCompositor.get()); + _viewRecords[@(viewId)] = [[FlutterEngineViewRecord alloc] initWithViewController:controller + macOSCompositor:macOSCompositor + compositor:compositor]; } - (void)deregisterViewControllerForId:(FlutterViewId)viewId { @@ -693,40 +751,6 @@ - (FlutterViewController*)viewController { return [self viewControllerForId:kFlutterDefaultViewId]; } -- (FlutterCompositor*)createFlutterCompositor { - _macOSCompositor = std::make_unique( - [[FlutterViewEngineProvider alloc] initWithEngine:self], _platformViewController); - - _compositor = {}; - _compositor.struct_size = sizeof(FlutterCompositor); - _compositor.user_data = _macOSCompositor.get(); - - _compositor.create_backing_store_callback = [](const FlutterBackingStoreConfig* config, // - FlutterBackingStore* backing_store_out, // - void* user_data // - ) { - return reinterpret_cast(user_data)->CreateBackingStore( - config, backing_store_out); - }; - - _compositor.collect_backing_store_callback = [](const FlutterBackingStore* backing_store, // - void* user_data // - ) { return true; }; - - _compositor.present_layers_callback = [](const FlutterLayer** layers, // - size_t layers_count, // - int64_t view_id, // - void* user_data // - ) { - return reinterpret_cast(user_data)->Present(view_id, layers, - layers_count); - }; - - _compositor.avoid_backing_store_cache = true; - - return &_compositor; -} - - (id)binaryMessenger { // TODO(stuartmorgan): Switch to FlutterBinaryMessengerRelay to avoid plugins // keeping the engine alive. @@ -1342,7 +1366,7 @@ - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)t // Getter used by test harness, only exposed through the FlutterEngine(Test) category - (flutter::FlutterCompositor*)macOSCompositor { - return _macOSCompositor.get(); + return _implicitMacOSCompositor.get(); } @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm index 60e8bc363f8df..10abab8a52a94 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm @@ -15,6 +15,7 @@ #include "flutter/shell/platform/common/accessibility_bridge.h" #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngineTestUtils.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h" #include "flutter/shell/platform/embedder/embedder.h" diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h b/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h index 9e003988ed41e..947c54d7a2ce1 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h @@ -11,7 +11,6 @@ #include "flutter/shell/platform/common/app_lifecycle_state.h" #import "flutter/shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMac.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h" diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 8725a724cce65..817cb3788b18a 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -485,7 +485,6 @@ InferMetalPlatformViewCreationCallback( frame_info.struct_size = sizeof(FlutterFrameInfo); frame_info.size = {static_cast(frame_size.width()), static_cast(frame_size.height())}; - frame_info.view_id = view_id; flutter::GPUMTLTextureInfo texture_info; FlutterMetalTexture metal_texture = ptr(user_data, &frame_info); @@ -1070,12 +1069,12 @@ InferExternalViewEmbedderFromArgs(const FlutterCompositor* compositor) { }; flutter::EmbedderExternalViewEmbedder::PresentCallback present_callback = - [c_present_callback, user_data = compositor->user_data]( - const auto& layers, int64_t window_view_id) { + [c_present_callback, + user_data = compositor->user_data](const auto& layers) { TRACE_EVENT0("flutter", "FlutterCompositorPresentLayers"); return c_present_callback( const_cast(layers.data()), layers.size(), - window_view_id, user_data); + user_data); }; return {std::make_unique( @@ -2052,14 +2051,20 @@ FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine) FLUTTER_EXPORT FlutterEngineResult FlutterEngineAddRenderSurface( FLUTTER_API_SYMBOL(FlutterEngine) engine, - void* user_data, - int64_t view_id) { + FlutterRenderSurfaceConfig* config) { if (engine == nullptr) { return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid."); } + auto external_view_embedder_result = + InferExternalViewEmbedderFromArgs(config->compositor); + if (external_view_embedder_result.second) { + return LOG_EMBEDDER_ERROR(kInvalidArguments, + "Compositor arguments were invalid."); + } flutter::EmbedderEngine* embedder_engine = reinterpret_cast(engine); - embedder_engine->GetShell().AddRenderSurface(view_id); + embedder_engine->GetShell().AddRenderSurface( + config->view_id, std::move(external_view_embedder_result.first)); return kSuccess; } diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index db03e7985c9f7..2a54931713f49 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -464,7 +464,6 @@ typedef struct { size_t struct_size; /// The size of the surface that will be backed by the fbo. FlutterUIntSize size; - int64_t view_id; } FlutterFrameInfo; /// Callback for when a frame buffer object is requested. @@ -654,7 +653,6 @@ typedef struct { /// The callback invoked by the engine when it no longer needs this backing /// store. VoidCallback destruction_callback; - int64_t view_id; } FlutterMetalTexture; /// Callback for when a metal texture is requested. @@ -1560,7 +1558,6 @@ typedef struct { size_t struct_size; /// The size of the render target the engine expects to render into. FlutterSize size; - int64_t view_id; } FlutterBackingStoreConfig; typedef enum { @@ -1603,16 +1600,15 @@ typedef bool (*FlutterBackingStoreCollectCallback)( typedef bool (*FlutterLayersPresentCallback)(const FlutterLayer** layers, size_t layers_count, - int64_t view_id, void* user_data); typedef struct { /// This size of this struct. Must be sizeof(FlutterCompositor). size_t struct_size; - /// A baton that in not interpreted by the engine in any way. If it passed + /// A baton that in not interpreted by the engine in any way. It is passed /// back to the embedder in `FlutterCompositor.create_backing_store_callback`, /// `FlutterCompositor.collect_backing_store_callback` and - /// `FlutterCompositor.present_layers_callback` + /// `FlutterCompositor.present_layers_callback`. void* user_data; /// A callback invoked by the engine to obtain a backing store for a specific /// `FlutterLayer`. @@ -1625,13 +1621,17 @@ typedef struct { /// A callback invoked by the engine to release the backing store. The /// embedder may collect any resources associated with the backing store. FlutterBackingStoreCollectCallback collect_backing_store_callback; - /// Callback invoked by the engine to composite the contents of each layer /// onto the screen. FlutterLayersPresentCallback present_layers_callback; /// Avoid caching backing stores provided by this compositor. bool avoid_backing_store_cache; } FlutterCompositor; +typedef struct { + int64_t view_id; + FlutterCompositor* compositor; +} FlutterRenderSurfaceConfig; + typedef struct { /// This size of this struct. Must be sizeof(FlutterLocale). size_t struct_size; @@ -2265,7 +2265,11 @@ FlutterEngineResult FlutterEngineRunInitialized( FLUTTER_EXPORT FlutterEngineResult FlutterEngineAddRenderSurface( FLUTTER_API_SYMBOL(FlutterEngine) engine, - void* user_data, + FlutterRenderSurfaceConfig* config); + +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineRemoveRenderSurface( + FLUTTER_API_SYMBOL(FlutterEngine) engine, int64_t view_id); FLUTTER_EXPORT @@ -2845,7 +2849,9 @@ typedef FlutterEngineResult (*FlutterEngineRunInitializedFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine); typedef FlutterEngineResult (*FlutterEngineAddRenderSurfaceFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine, - void* user_data, + FlutterRenderSurfaceConfig* config); +typedef FlutterEngineResult (*FlutterEngineRemoveRenderSurfaceFnPtr)( + FLUTTER_API_SYMBOL(FlutterEngine) engine, int64_t view_id); typedef FlutterEngineResult (*FlutterEngineSendWindowMetricsEventFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine, diff --git a/shell/platform/embedder/embedder_external_view_embedder.cc b/shell/platform/embedder/embedder_external_view_embedder.cc index 985d0ba3ddcd8..e4870090c6c9f 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.cc +++ b/shell/platform/embedder/embedder_external_view_embedder.cc @@ -111,15 +111,13 @@ DlCanvas* EmbedderExternalViewEmbedder::CompositeEmbeddedView(int64_t view_id) { } static FlutterBackingStoreConfig MakeBackingStoreConfig( - const SkISize& backing_store_size, - int64_t window_view_id) { + const SkISize& backing_store_size) { FlutterBackingStoreConfig config = {}; config.struct_size = sizeof(config); config.size.width = backing_store_size.width(); config.size.height = backing_store_size.height(); - config.view_id = window_view_id; return config; } @@ -127,8 +125,7 @@ static FlutterBackingStoreConfig MakeBackingStoreConfig( // |ExternalViewEmbedder| void EmbedderExternalViewEmbedder::SubmitFrame( GrDirectContext* context, - std::unique_ptr frame, - int64_t window_view_id) { + std::unique_ptr frame) { auto [matched_render_targets, pending_keys] = render_target_cache_.GetExistingTargetsInCache(pending_views_); @@ -167,7 +164,7 @@ void EmbedderExternalViewEmbedder::SubmitFrame( const auto render_surface_size = external_view->GetRenderSurfaceSize(); const auto backing_store_config = - MakeBackingStoreConfig(render_surface_size, window_view_id); + MakeBackingStoreConfig(render_surface_size); // This is where the embedder will create render targets for us. Control // flow to the embedder makes the engine susceptible to having the embedder @@ -248,7 +245,7 @@ void EmbedderExternalViewEmbedder::SubmitFrame( // Flush the layer description down to the embedder for presentation. // // @warning: Embedder may trample on our OpenGL context here. - presented_layers.InvokePresentCallback(present_callback_, window_view_id); + presented_layers.InvokePresentCallback(present_callback_); } // See why this is necessary in the comment where this collection in realized. diff --git a/shell/platform/embedder/embedder_external_view_embedder.h b/shell/platform/embedder/embedder_external_view_embedder.h index 2118db5e25758..55a08f0627a72 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.h +++ b/shell/platform/embedder/embedder_external_view_embedder.h @@ -33,8 +33,7 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder { GrDirectContext* context, const FlutterBackingStoreConfig& config)>; using PresentCallback = - std::function& layers, - int64_t window_view_id)>; + std::function& layers)>; using SurfaceTransformationCallback = std::function; //---------------------------------------------------------------------------- @@ -96,8 +95,7 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder { // |ExternalViewEmbedder| void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame, - int64_t window_view_id) override; + std::unique_ptr frame) override; // |ExternalViewEmbedder| DlCanvas* GetRootCanvas() override; diff --git a/shell/platform/embedder/embedder_layers.cc b/shell/platform/embedder/embedder_layers.cc index f5a62b26c4bea..6465293748f9f 100644 --- a/shell/platform/embedder/embedder_layers.cc +++ b/shell/platform/embedder/embedder_layers.cc @@ -199,14 +199,14 @@ void EmbedderLayers::PushPlatformViewLayer( presented_layers_.push_back(layer); } -void EmbedderLayers::InvokePresentCallback(const PresentCallback& callback, - int64_t window_view_id) const { +void EmbedderLayers::InvokePresentCallback( + const PresentCallback& callback) const { std::vector presented_layers_pointers; presented_layers_pointers.reserve(presented_layers_.size()); for (const auto& layer : presented_layers_) { presented_layers_pointers.push_back(&layer); } - callback(presented_layers_pointers, window_view_id); + callback(presented_layers_pointers); } } // namespace flutter diff --git a/shell/platform/embedder/embedder_layers.h b/shell/platform/embedder/embedder_layers.h index 46137f459df99..c1cb2907588c9 100644 --- a/shell/platform/embedder/embedder_layers.h +++ b/shell/platform/embedder/embedder_layers.h @@ -30,10 +30,8 @@ class EmbedderLayers { const EmbeddedViewParams& params); using PresentCallback = - std::function& layers, - int64_t view_id)>; - void InvokePresentCallback(const PresentCallback& callback, - int64_t window_view_id) const; + std::function& layers)>; + void InvokePresentCallback(const PresentCallback& callback) const; private: const SkISize frame_size_; diff --git a/shell/platform/embedder/tests/embedder_config_builder.cc b/shell/platform/embedder/tests/embedder_config_builder.cc index dae2c4dc12110..f12a9e58b130a 100644 --- a/shell/platform/embedder/tests/embedder_config_builder.cc +++ b/shell/platform/embedder/tests/embedder_config_builder.cc @@ -362,7 +362,6 @@ void EmbedderConfigBuilder::SetCompositor(bool avoid_backing_store_cache) { }; compositor_.present_layers_callback = [](const FlutterLayer** layers, // size_t layers_count, // - int64_t view_id, // void* user_data // ) { return reinterpret_cast(user_data)->Present( diff --git a/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc b/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc index 9d970c6489865..901bd8c078e97 100644 --- a/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc +++ b/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc @@ -132,8 +132,7 @@ void FlatlandExternalViewEmbedder::EndFrame( void FlatlandExternalViewEmbedder::SubmitFrame( GrDirectContext* context, - std::unique_ptr frame, - int64_t window_view_id) { + std::unique_ptr frame) { TRACE_EVENT0("flutter", "FlatlandExternalViewEmbedder::SubmitFrame"); std::vector> frame_surfaces; std::unordered_map frame_surface_indices; diff --git a/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h b/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h index c637aad11d1eb..033efbe6ae62a 100644 --- a/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h +++ b/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h @@ -87,8 +87,7 @@ class FlatlandExternalViewEmbedder final // |ExternalViewEmbedder| void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame, - int64_t window_view_id) override; + std::unique_ptr frame) override; // |ExternalViewEmbedder| void CancelFrame() override { Reset(); } diff --git a/shell/platform/fuchsia/flutter/gfx_external_view_embedder.cc b/shell/platform/fuchsia/flutter/gfx_external_view_embedder.cc index e34dfc5e64bc3..21c6810c4e11b 100644 --- a/shell/platform/fuchsia/flutter/gfx_external_view_embedder.cc +++ b/shell/platform/fuchsia/flutter/gfx_external_view_embedder.cc @@ -231,8 +231,7 @@ void GfxExternalViewEmbedder::EndFrame( void GfxExternalViewEmbedder::SubmitFrame( GrDirectContext* context, - std::unique_ptr frame, - int64_t window_view_id) { + std::unique_ptr frame) { TRACE_EVENT0("flutter", "GfxExternalViewEmbedder::SubmitFrame"); std::vector> frame_surfaces; std::unordered_map frame_surface_indices; diff --git a/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h b/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h index 54ed5d6261b43..39e25dc3daeaa 100644 --- a/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h +++ b/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h @@ -110,8 +110,7 @@ class GfxExternalViewEmbedder final : public flutter::ExternalViewEmbedder { // |ExternalViewEmbedder| void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame, - int64_t window_view_id) override; + std::unique_ptr frame) override; // |ExternalViewEmbedder| void CancelFrame() override { Reset(); } diff --git a/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc b/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc index 7d6034cb7ef91..35f734d2044ac 100644 --- a/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc +++ b/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc @@ -53,8 +53,6 @@ using ::testing::VariantWith; namespace flutter_runner::testing { namespace { -constexpr int64_t kDefaultViewId = 0ll; - constexpr static fuchsia::ui::composition::BlendMode kFirstLayerBlendMode{ fuchsia::ui::composition::BlendMode::SRC}; constexpr static fuchsia::ui::composition::BlendMode kUpperLayerBlendMode{ @@ -337,13 +335,11 @@ void DrawSimpleFrame(FlatlandExternalViewEmbedder& external_view_embedder, flutter::SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; external_view_embedder.SubmitFrame( - nullptr, - std::make_unique( - nullptr, std::move(framebuffer_info), - [](const flutter::SurfaceFrame& surface_frame, - flutter::DlCanvas* canvas) { return true; }, - frame_size), - kDefaultViewId); + nullptr, std::make_unique( + nullptr, std::move(framebuffer_info), + [](const flutter::SurfaceFrame& surface_frame, + flutter::DlCanvas* canvas) { return true; }, + frame_size)); } void DrawFrameWithView( @@ -369,13 +365,11 @@ void DrawFrameWithView( flutter::SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; external_view_embedder.SubmitFrame( - nullptr, - std::make_unique( - nullptr, std::move(framebuffer_info), - [](const flutter::SurfaceFrame& surface_frame, - flutter::DlCanvas* canvas) { return true; }, - frame_size), - kDefaultViewId); + nullptr, std::make_unique( + nullptr, std::move(framebuffer_info), + [](const flutter::SurfaceFrame& surface_frame, + flutter::DlCanvas* canvas) { return true; }, + frame_size)); } }; // namespace diff --git a/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc b/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc index ae595bea88986..f0e3c6553fced 100644 --- a/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc @@ -54,8 +54,7 @@ class MockExternalViewEmbedder : public flutter::ExternalViewEmbedder { fml::RefPtr raster_thread_merger) override {} void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame, - int64_t window_view_id) override {} + std::unique_ptr frame) override {} void PrerollCompositeEmbeddedView( int64_t view_id, diff --git a/shell/platform/fuchsia/flutter/tests/gfx_external_view_embedder_unittests.cc b/shell/platform/fuchsia/flutter/tests/gfx_external_view_embedder_unittests.cc index ba4cf9f4c1fbd..77c04bb6ab7ed 100644 --- a/shell/platform/fuchsia/flutter/tests/gfx_external_view_embedder_unittests.cc +++ b/shell/platform/fuchsia/flutter/tests/gfx_external_view_embedder_unittests.cc @@ -54,8 +54,6 @@ using ::testing::VariantWith; namespace flutter_runner::testing { namespace { -constexpr int64_t kDefaultViewId = 0ll; - class FakeSurfaceProducerSurface : public SurfaceProducerSurface { public: explicit FakeSurfaceProducerSurface(scenic::Session* session, @@ -460,13 +458,11 @@ void DrawSimpleFrame(GfxExternalViewEmbedder& external_view_embedder, external_view_embedder.EndFrame(false, nullptr); flutter::SurfaceFrame::FramebufferInfo framebuffer_info; external_view_embedder.SubmitFrame( - nullptr, - std::make_unique( - nullptr, framebuffer_info, - [](const flutter::SurfaceFrame& surface_frame, - flutter::DlCanvas* canvas) { return true; }, - frame_size), - kDefaultViewId); + nullptr, std::make_unique( + nullptr, framebuffer_info, + [](const flutter::SurfaceFrame& surface_frame, + flutter::DlCanvas* canvas) { return true; }, + frame_size)); } void DrawFrameWithView( @@ -491,13 +487,11 @@ void DrawFrameWithView( external_view_embedder.EndFrame(false, nullptr); flutter::SurfaceFrame::FramebufferInfo framebuffer_info; external_view_embedder.SubmitFrame( - nullptr, - std::make_unique( - nullptr, framebuffer_info, - [](const flutter::SurfaceFrame& surface_frame, - flutter::DlCanvas* canvas) { return true; }, - frame_size), - kDefaultViewId); + nullptr, std::make_unique( + nullptr, framebuffer_info, + [](const flutter::SurfaceFrame& surface_frame, + flutter::DlCanvas* canvas) { return true; }, + frame_size)); } FramePresentedInfo MakeFramePresentedInfoForOnePresent( From 5ad2f7d34caea39ad0bdf536a4cb6f076c22ec46 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 30 May 2023 23:25:21 -0700 Subject: [PATCH 142/147] Fix bug --- shell/common/shell.cc | 16 ++++++++++------ .../macos/framework/Source/FlutterEngine.mm | 15 ++++++++++----- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 96ed75ad05aef..607c862e53c52 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -2007,7 +2007,9 @@ void Shell::AddRenderSurface( TRACE_EVENT0("flutter", "Shell::AddRenderSurface"); FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); - FML_DCHECK(view_id != kFlutterDefaultViewId); + if (view_id == kFlutterDefaultViewId) { + return; + } if (!engine_) { return; } @@ -2017,13 +2019,15 @@ void Shell::AddRenderSurface( // TODO(dkwingsmt): platform_view_ is captured illegally here. // We need some mechanism from it being collected. - task_runners_.GetRasterTaskRunner()->PostTask( - fml::MakeCopyable([platform_view = platform_view_.get(), // - rasterizer = rasterizer_->GetWeakPtr(), // - view_id // + task_runners_.GetRasterTaskRunner()->PostTask(fml::MakeCopyable( + [platform_view = platform_view_.get(), // + rasterizer = rasterizer_->GetWeakPtr(), // + view_embedder_ptr = external_view_embedder.release(), // + view_id // ]() mutable { if (platform_view && rasterizer) { - auto view_embedder = platform_view->CreateExternalViewEmbedder(); + std::shared_ptr view_embedder( + view_embedder_ptr); std::unique_ptr surface = platform_view->CreateSurface(view_id); if (surface) { diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index e608b4ef121b7..37f11bf25ca45 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -513,6 +513,11 @@ - (instancetype)initWithName:(NSString*)labelPrefix reinterpret_cast([[NSApplication sharedApplication] delegate]); [appDelegate addApplicationLifecycleDelegate:self]; + _implicitMacOSCompositor = std::make_shared( + [[FlutterViewEngineProvider alloc] initWithEngine:self], kFlutterDefaultViewId, + _platformViewController); + _implicitCompositor = createFlutterCompositorFor(_implicitMacOSCompositor.get()); + return self; } @@ -608,10 +613,6 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint { flutterArguments.aot_data = _aotData; } - _implicitMacOSCompositor = std::make_shared( - [[FlutterViewEngineProvider alloc] initWithEngine:self], kFlutterDefaultViewId, - _platformViewController); - _implicitCompositor = createFlutterCompositorFor(_implicitMacOSCompositor.get()); flutterArguments.compositor = &_implicitCompositor; flutterArguments.on_pre_engine_restart_callback = [](void* user_data) { @@ -686,8 +687,12 @@ - (void)registerViewController:(FlutterViewController*)controller forId:(Flutter : std::make_shared( [[FlutterViewEngineProvider alloc] initWithEngine:self], viewId, _platformViewController); - auto compositor = + FlutterCompositor compositor = isImplicitView ? _implicitCompositor : createFlutterCompositorFor(macOSCompositor.get()); + NSAssert(macOSCompositor, @"No macOSCompositor"); + ; + NSAssert(compositor.create_backing_store_callback, @"Invalid compositor"); + ; _viewRecords[@(viewId)] = [[FlutterEngineViewRecord alloc] initWithViewController:controller macOSCompositor:macOSCompositor compositor:compositor]; From 682c1572eecccbf16a32584bb7633ffd36531b7e Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 30 May 2023 23:31:42 -0700 Subject: [PATCH 143/147] Fix linux compiling --- shell/platform/darwin/macos/framework/Source/FlutterEngine.mm | 2 -- shell/platform/linux/fl_engine.cc | 1 - 2 files changed, 3 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 37f11bf25ca45..c382a99bcc8a2 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -690,9 +690,7 @@ - (void)registerViewController:(FlutterViewController*)controller forId:(Flutter FlutterCompositor compositor = isImplicitView ? _implicitCompositor : createFlutterCompositorFor(macOSCompositor.get()); NSAssert(macOSCompositor, @"No macOSCompositor"); - ; NSAssert(compositor.create_backing_store_callback, @"Invalid compositor"); - ; _viewRecords[@(viewId)] = [[FlutterEngineViewRecord alloc] initWithViewController:controller macOSCompositor:macOSCompositor compositor:compositor]; diff --git a/shell/platform/linux/fl_engine.cc b/shell/platform/linux/fl_engine.cc index 34170f388a96c..513b1e19cd32c 100644 --- a/shell/platform/linux/fl_engine.cc +++ b/shell/platform/linux/fl_engine.cc @@ -203,7 +203,6 @@ static bool compositor_collect_backing_store_callback( // Called when embedder should composite contents of each layer onto the screen. static bool compositor_present_layers_callback(const FlutterLayer** layers, size_t layers_count, - int64_t view_id, void* user_data) { g_return_val_if_fail(FL_IS_RENDERER(user_data), false); return fl_renderer_present_layers(FL_RENDERER(user_data), layers, From b4cb9307a959b5dcf4db18b62a348b99ff45d03b Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 30 May 2023 23:56:11 -0700 Subject: [PATCH 144/147] Remove view_id from AcquireFrame --- flow/surface.h | 3 +- shell/common/rasterizer.cc | 3 +- shell/common/rasterizer_unittests.cc | 31 +++++++++---------- shell/common/shell_test_platform_view_metal.h | 3 +- .../common/shell_test_platform_view_metal.mm | 3 +- .../common/shell_test_platform_view_vulkan.cc | 1 - .../common/shell_test_platform_view_vulkan.h | 3 +- shell/common/shell_unittests.cc | 5 ++- shell/gpu/gpu_surface_gl_impeller.cc | 1 - shell/gpu/gpu_surface_gl_impeller.h | 3 +- shell/gpu/gpu_surface_gl_skia.cc | 1 - shell/gpu/gpu_surface_gl_skia.h | 3 +- shell/gpu/gpu_surface_metal_delegate.h | 3 +- shell/gpu/gpu_surface_metal_impeller.h | 3 +- shell/gpu/gpu_surface_metal_impeller.mm | 3 +- shell/gpu/gpu_surface_metal_skia.h | 4 +-- shell/gpu/gpu_surface_metal_skia.mm | 8 ++--- shell/gpu/gpu_surface_software.cc | 1 - shell/gpu/gpu_surface_software.h | 3 +- shell/gpu/gpu_surface_vulkan.cc | 1 - shell/gpu/gpu_surface_vulkan.h | 3 +- shell/gpu/gpu_surface_vulkan_impeller.cc | 1 - shell/gpu/gpu_surface_vulkan_impeller.h | 3 +- .../external_view_embedder.cc | 7 +---- .../external_view_embedder.h | 1 - .../external_view_embedder_unittests.cc | 18 +++++------ .../darwin/ios/ios_surface_metal_impeller.h | 2 +- .../darwin/ios/ios_surface_metal_impeller.mm | 3 +- .../darwin/ios/ios_surface_metal_skia.h | 2 +- .../darwin/ios/ios_surface_metal_skia.mm | 3 +- shell/platform/embedder/embedder.cc | 5 ++- .../platform/embedder/embedder_studio_metal.h | 5 ++- .../embedder/embedder_studio_metal.mm | 5 ++- shell/platform/fuchsia/flutter/surface.cc | 1 - shell/platform/fuchsia/flutter/surface.h | 1 - 35 files changed, 53 insertions(+), 93 deletions(-) diff --git a/flow/surface.h b/flow/surface.h index 6de182275a90e..4699bc61b4d6d 100644 --- a/flow/surface.h +++ b/flow/surface.h @@ -35,8 +35,7 @@ class Surface { virtual bool IsValid() = 0; - virtual std::unique_ptr AcquireFrame(int64_t view_id, - const SkISize& size) = 0; + virtual std::unique_ptr AcquireFrame(const SkISize& size) = 0; virtual SkMatrix GetRootTransformation() const = 0; diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 7eaecabffb9fc..6ef17f7b8d591 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -610,8 +610,7 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( // // Deleting a surface also clears the GL context. Therefore, acquire the // frame after calling `BeginFrame` as this operation resets the GL context. - auto frame = - surface->AcquireFrame(surface_record->view_id, layer_tree->frame_size()); + auto frame = surface->AcquireFrame(layer_tree->frame_size()); if (frame == nullptr) { frame_timings_recorder.RecordRasterEnd( &compositor_context_->raster_cache()); diff --git a/shell/common/rasterizer_unittests.cc b/shell/common/rasterizer_unittests.cc index 0c7226a23eb2c..b647338b9c014 100644 --- a/shell/common/rasterizer_unittests.cc +++ b/shell/common/rasterizer_unittests.cc @@ -57,9 +57,8 @@ class MockStudio : public Studio { class MockSurface : public Surface { public: MOCK_METHOD0(IsValid, bool()); - MOCK_METHOD2(AcquireFrame, - std::unique_ptr(int64_t view_id, - const SkISize& size)); + MOCK_METHOD1(AcquireFrame, + std::unique_ptr(const SkISize& size)); MOCK_CONST_METHOD0(GetRootTransformation, SkMatrix()); MOCK_METHOD0(GetContext, GrDirectContext*()); MOCK_METHOD0(GetExternalViewEmbedder, ExternalViewEmbedder*()); @@ -177,7 +176,7 @@ TEST(RasterizerTest, /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); - EXPECT_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) + EXPECT_CALL(*surface, AcquireFrame(SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame)))); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); @@ -248,7 +247,7 @@ TEST( /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); - EXPECT_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) + EXPECT_CALL(*surface, AcquireFrame(SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame)))); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); @@ -321,7 +320,7 @@ TEST( /*submit_callback=*/[](const SurfaceFrame&, DlCanvas*) { return true; }, /*frame_size=*/SkISize::Make(800, 600)); EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(true)); - EXPECT_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) + EXPECT_CALL(*surface, AcquireFrame(SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame)))); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); @@ -395,7 +394,7 @@ TEST(RasterizerTest, EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()) .WillRepeatedly(Return(true)); // Prepare two frames for Draw() and DrawLastLayerTree(). - EXPECT_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) + EXPECT_CALL(*surface, AcquireFrame(SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame1)))) .WillOnce(Return(ByMove(std::move(surface_frame2)))); EXPECT_CALL(*studio, MakeRenderContextCurrent()) @@ -575,7 +574,7 @@ TEST(RasterizerTest, ON_CALL(delegate, GetIsGpuDisabledSyncSwitch()) .WillByDefault(Return(is_gpu_disabled_sync_switch)); EXPECT_CALL(delegate, GetIsGpuDisabledSyncSwitch()).Times(0); - EXPECT_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) + EXPECT_CALL(*surface, AcquireFrame(SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame)))); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); @@ -635,7 +634,7 @@ TEST( ON_CALL(delegate, GetIsGpuDisabledSyncSwitch()) .WillByDefault(Return(is_gpu_disabled_sync_switch)); EXPECT_CALL(delegate, GetIsGpuDisabledSyncSwitch()).Times(0); - EXPECT_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) + EXPECT_CALL(*surface, AcquireFrame(SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame)))); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); @@ -695,7 +694,7 @@ TEST( EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(false)); EXPECT_CALL(delegate, GetIsGpuDisabledSyncSwitch()) .WillOnce(Return(is_gpu_disabled_sync_switch)); - EXPECT_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) + EXPECT_CALL(*surface, AcquireFrame(SkISize())) .WillOnce(Return(ByMove(std::move(surface_frame)))); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); @@ -755,7 +754,7 @@ TEST( EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillOnce(Return(false)); EXPECT_CALL(delegate, GetIsGpuDisabledSyncSwitch()) .WillOnce(Return(is_gpu_disabled_sync_switch)); - EXPECT_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())).Times(0); + EXPECT_CALL(*surface, AcquireFrame(SkISize())).Times(0); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); @@ -812,9 +811,9 @@ TEST( std::make_shared(false); ON_CALL(delegate, GetIsGpuDisabledSyncSwitch()) .WillByDefault(Return(is_gpu_disabled_sync_switch)); - ON_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) + ON_CALL(*surface, AcquireFrame(SkISize())) .WillByDefault(::testing::Invoke([] { return nullptr; })); - EXPECT_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())); + EXPECT_CALL(*surface, AcquireFrame(SkISize())); EXPECT_CALL(*studio, MakeRenderContextCurrent()) .WillOnce(Return(ByMove(std::make_unique(true)))); rasterizer->Setup(std::move(studio), false); @@ -866,7 +865,7 @@ TEST(RasterizerTest, auto surface = std::make_unique>(); EXPECT_CALL(*studio, AllowsDrawingWhenGpuDisabled()) .WillRepeatedly(Return(true)); - ON_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) + ON_CALL(*surface, AcquireFrame(SkISize())) .WillByDefault(::testing::Invoke([] { SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; @@ -1050,7 +1049,7 @@ TEST(RasterizerTest, presentationTimeSetWhenVsyncTargetInFuture) { auto studio = std::make_unique>(); auto surface = std::make_unique(); ON_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillByDefault(Return(true)); - ON_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) + ON_CALL(*surface, AcquireFrame(SkISize())) .WillByDefault(::testing::Invoke([&] { SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; @@ -1135,7 +1134,7 @@ TEST(RasterizerTest, presentationTimeNotSetWhenVsyncTargetInPast) { auto studio = std::make_unique>(); auto surface = std::make_unique(); ON_CALL(*studio, AllowsDrawingWhenGpuDisabled()).WillByDefault(Return(true)); - ON_CALL(*surface, AcquireFrame(kDefaultViewId, SkISize())) + ON_CALL(*surface, AcquireFrame(SkISize())) .WillByDefault(::testing::Invoke([&] { SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; diff --git a/shell/common/shell_test_platform_view_metal.h b/shell/common/shell_test_platform_view_metal.h index c07258783f59c..e27cb965ce7c5 100644 --- a/shell/common/shell_test_platform_view_metal.h +++ b/shell/common/shell_test_platform_view_metal.h @@ -72,8 +72,7 @@ class ShellTestPlatformViewMetal final : public ShellTestPlatformView, bool PresentDrawable(GrMTLHandle drawable) const override; // |GPUSurfaceMetalDelegate| - GPUMTLTextureInfo GetMTLTexture(int64_t view_id, - const SkISize& frame_info) const override; + GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_info) const override; // |GPUSurfaceMetalDelegate| bool PresentTexture(GPUMTLTextureInfo texture) const override; diff --git a/shell/common/shell_test_platform_view_metal.mm b/shell/common/shell_test_platform_view_metal.mm index 9d82156f6f26c..e73162043a9ae 100644 --- a/shell/common/shell_test_platform_view_metal.mm +++ b/shell/common/shell_test_platform_view_metal.mm @@ -164,8 +164,7 @@ GPUMTLTextureInfo offscreen_texture_info() const { } // |GPUSurfaceMetalDelegate| -GPUMTLTextureInfo ShellTestPlatformViewMetal::GetMTLTexture(int64_t view_id, - const SkISize& frame_info) const { +GPUMTLTextureInfo ShellTestPlatformViewMetal::GetMTLTexture(const SkISize& frame_info) const { return metal_context_->offscreen_texture_info(); } diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index cd89d06b8955d..2ae1a935aa7df 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -216,7 +216,6 @@ bool ShellTestPlatformViewVulkan::OffScreenSurface::IsValid() { std::unique_ptr ShellTestPlatformViewVulkan::OffScreenSurface::AcquireFrame( - int64_t view_id, const SkISize& size) { auto image_info = SkImageInfo::Make(size, SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kOpaque_SkAlphaType); diff --git a/shell/common/shell_test_platform_view_vulkan.h b/shell/common/shell_test_platform_view_vulkan.h index 1bc3f98e20271..97b54c47d5e36 100644 --- a/shell/common/shell_test_platform_view_vulkan.h +++ b/shell/common/shell_test_platform_view_vulkan.h @@ -88,8 +88,7 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { bool IsValid() override; // |Surface| - std::unique_ptr AcquireFrame(int64_t view_id, - const SkISize& size) override; + std::unique_ptr AcquireFrame(const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index c21a3f50e68ea..42b4c8ec5ee7b 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -136,9 +136,8 @@ class MockSurface : public Surface { MOCK_METHOD0(GetContext, GrDirectContext*()); - MOCK_METHOD2(AcquireFrame, - std::unique_ptr(int64_t view_id, - const SkISize& size)); + MOCK_METHOD1(AcquireFrame, + std::unique_ptr(const SkISize& size)); MOCK_CONST_METHOD0(GetRootTransformation, SkMatrix()); }; diff --git a/shell/gpu/gpu_surface_gl_impeller.cc b/shell/gpu/gpu_surface_gl_impeller.cc index e8a75f216dfab..07633148e1f8b 100644 --- a/shell/gpu/gpu_surface_gl_impeller.cc +++ b/shell/gpu/gpu_surface_gl_impeller.cc @@ -51,7 +51,6 @@ bool GPUSurfaceGLImpeller::IsValid() { // |Surface| std::unique_ptr GPUSurfaceGLImpeller::AcquireFrame( - int64_t view_id, const SkISize& size) { if (!IsValid()) { FML_LOG(ERROR) << "OpenGL surface was invalid."; diff --git a/shell/gpu/gpu_surface_gl_impeller.h b/shell/gpu/gpu_surface_gl_impeller.h index 49a750d5de2a6..9e054291391fc 100644 --- a/shell/gpu/gpu_surface_gl_impeller.h +++ b/shell/gpu/gpu_surface_gl_impeller.h @@ -35,8 +35,7 @@ class GPUSurfaceGLImpeller final : public Surface { fml::WeakPtrFactory weak_factory_; // |Surface| - std::unique_ptr AcquireFrame(int64_t view_id, - const SkISize& size) override; + std::unique_ptr AcquireFrame(const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; diff --git a/shell/gpu/gpu_surface_gl_skia.cc b/shell/gpu/gpu_surface_gl_skia.cc index 2a66d7e8862a5..d3a9a166e2241 100644 --- a/shell/gpu/gpu_surface_gl_skia.cc +++ b/shell/gpu/gpu_surface_gl_skia.cc @@ -166,7 +166,6 @@ SkMatrix GPUSurfaceGLSkia::GetRootTransformation() const { // |Surface| std::unique_ptr GPUSurfaceGLSkia::AcquireFrame( - int64_t view_id, const SkISize& size) { if (delegate_ == nullptr) { return nullptr; diff --git a/shell/gpu/gpu_surface_gl_skia.h b/shell/gpu/gpu_surface_gl_skia.h index ba666f35a43cf..42cb592f9ccd0 100644 --- a/shell/gpu/gpu_surface_gl_skia.h +++ b/shell/gpu/gpu_surface_gl_skia.h @@ -33,8 +33,7 @@ class GPUSurfaceGLSkia : public Surface { bool IsValid() override; // |Surface| - std::unique_ptr AcquireFrame(int64_t view_id, - const SkISize& size) override; + std::unique_ptr AcquireFrame(const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; diff --git a/shell/gpu/gpu_surface_metal_delegate.h b/shell/gpu/gpu_surface_metal_delegate.h index ece9f5f5ab006..1482da72f00c3 100644 --- a/shell/gpu/gpu_surface_metal_delegate.h +++ b/shell/gpu/gpu_surface_metal_delegate.h @@ -92,8 +92,7 @@ class GPUSurfaceMetalDelegate { /// @brief Returns the handle to the MTLTexture to render to. This is only /// called when the specified render target type is `kMTLTexture`. /// - virtual GPUMTLTextureInfo GetMTLTexture(int64_t view_id, - const SkISize& frame_info) const = 0; + virtual GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_info) const = 0; //------------------------------------------------------------------------------ /// @brief Presents the texture with `texture_id` to the "screen". diff --git a/shell/gpu/gpu_surface_metal_impeller.h b/shell/gpu/gpu_surface_metal_impeller.h index 3121c905d2bed..ece62e423f5be 100644 --- a/shell/gpu/gpu_surface_metal_impeller.h +++ b/shell/gpu/gpu_surface_metal_impeller.h @@ -44,8 +44,7 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUSurfaceMetalImpeller : public Surface { std::map damage_; // |Surface| - std::unique_ptr AcquireFrame(int64_t view_id, - const SkISize& size) override; + std::unique_ptr AcquireFrame(const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; diff --git a/shell/gpu/gpu_surface_metal_impeller.mm b/shell/gpu/gpu_surface_metal_impeller.mm index 3c4d509298736..55ab6a9d662bc 100644 --- a/shell/gpu/gpu_surface_metal_impeller.mm +++ b/shell/gpu/gpu_surface_metal_impeller.mm @@ -50,8 +50,7 @@ } // |Surface| -std::unique_ptr GPUSurfaceMetalImpeller::AcquireFrame(int64_t view_id, - const SkISize& frame_info) { +std::unique_ptr GPUSurfaceMetalImpeller::AcquireFrame(const SkISize& frame_info) { TRACE_EVENT0("impeller", "GPUSurfaceMetalImpeller::AcquireFrame"); if (!IsValid()) { diff --git a/shell/gpu/gpu_surface_metal_skia.h b/shell/gpu/gpu_surface_metal_skia.h index 38920f02dbdc1..49747c4e7b280 100644 --- a/shell/gpu/gpu_surface_metal_skia.h +++ b/shell/gpu/gpu_surface_metal_skia.h @@ -46,8 +46,7 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUSurfaceMetalSkia : public Surface { std::map damage_; // |Surface| - std::unique_ptr AcquireFrame(int64_t view_id, - const SkISize& size) override; + std::unique_ptr AcquireFrame(const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; @@ -57,7 +56,6 @@ class SK_API_AVAILABLE_CA_METAL_LAYER GPUSurfaceMetalSkia : public Surface { const SkISize& frame_info); std::unique_ptr AcquireFrameFromMTLTexture( - int64_t view_id, const SkISize& frame_info); void PrecompileKnownSkSLsIfNecessary(); diff --git a/shell/gpu/gpu_surface_metal_skia.mm b/shell/gpu/gpu_surface_metal_skia.mm index e5566b42a6f50..1f2fffd974949 100644 --- a/shell/gpu/gpu_surface_metal_skia.mm +++ b/shell/gpu/gpu_surface_metal_skia.mm @@ -78,8 +78,7 @@ } // |Surface| -std::unique_ptr GPUSurfaceMetalSkia::AcquireFrame(int64_t view_id, - const SkISize& frame_size) { +std::unique_ptr GPUSurfaceMetalSkia::AcquireFrame(const SkISize& frame_size) { if (!IsValid()) { FML_LOG(ERROR) << "Metal surface was invalid."; return nullptr; @@ -102,7 +101,7 @@ case MTLRenderTargetType::kCAMetalLayer: return AcquireFrameFromCAMetalLayer(frame_size); case MTLRenderTargetType::kMTLTexture: - return AcquireFrameFromMTLTexture(view_id, frame_size); + return AcquireFrameFromMTLTexture(frame_size); default: FML_CHECK(false) << "Unknown MTLRenderTargetType type."; } @@ -192,9 +191,8 @@ } std::unique_ptr GPUSurfaceMetalSkia::AcquireFrameFromMTLTexture( - int64_t view_id, const SkISize& frame_info) { - GPUMTLTextureInfo texture = delegate_->GetMTLTexture(view_id, frame_info); + GPUMTLTextureInfo texture = delegate_->GetMTLTexture(frame_info); id mtl_texture = (id)(texture.texture); if (!mtl_texture) { diff --git a/shell/gpu/gpu_surface_software.cc b/shell/gpu/gpu_surface_software.cc index e4a71ca5355f4..2d9902794043c 100644 --- a/shell/gpu/gpu_surface_software.cc +++ b/shell/gpu/gpu_surface_software.cc @@ -27,7 +27,6 @@ bool GPUSurfaceSoftware::IsValid() { // |Surface| std::unique_ptr GPUSurfaceSoftware::AcquireFrame( - int64_t view_id, const SkISize& logical_size) { SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; diff --git a/shell/gpu/gpu_surface_software.h b/shell/gpu/gpu_surface_software.h index 0acd344a9d04d..abec2eb4b9460 100644 --- a/shell/gpu/gpu_surface_software.h +++ b/shell/gpu/gpu_surface_software.h @@ -23,8 +23,7 @@ class GPUSurfaceSoftware : public Surface { bool IsValid() override; // |Surface| - std::unique_ptr AcquireFrame(int64_t view_id, - const SkISize& size) override; + std::unique_ptr AcquireFrame(const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; diff --git a/shell/gpu/gpu_surface_vulkan.cc b/shell/gpu/gpu_surface_vulkan.cc index 5997f23a53875..ac909ed2c3533 100644 --- a/shell/gpu/gpu_surface_vulkan.cc +++ b/shell/gpu/gpu_surface_vulkan.cc @@ -30,7 +30,6 @@ bool GPUSurfaceVulkan::IsValid() { } std::unique_ptr GPUSurfaceVulkan::AcquireFrame( - int64_t view_id, const SkISize& frame_size) { if (!IsValid()) { FML_LOG(ERROR) << "Vulkan surface was invalid."; diff --git a/shell/gpu/gpu_surface_vulkan.h b/shell/gpu/gpu_surface_vulkan.h index a7ef9df141981..13bc39fcd33da 100644 --- a/shell/gpu/gpu_surface_vulkan.h +++ b/shell/gpu/gpu_surface_vulkan.h @@ -40,8 +40,7 @@ class GPUSurfaceVulkan : public Surface { bool IsValid() override; // |Surface| - std::unique_ptr AcquireFrame(int64_t view_id, - const SkISize& size) override; + std::unique_ptr AcquireFrame(const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; diff --git a/shell/gpu/gpu_surface_vulkan_impeller.cc b/shell/gpu/gpu_surface_vulkan_impeller.cc index 16aadcfa85817..9d4cab79bb3fc 100644 --- a/shell/gpu/gpu_surface_vulkan_impeller.cc +++ b/shell/gpu/gpu_surface_vulkan_impeller.cc @@ -44,7 +44,6 @@ bool GPUSurfaceVulkanImpeller::IsValid() { // |Surface| std::unique_ptr GPUSurfaceVulkanImpeller::AcquireFrame( - int64_t view_id, const SkISize& size) { if (!IsValid()) { FML_LOG(ERROR) << "Vulkan surface was invalid."; diff --git a/shell/gpu/gpu_surface_vulkan_impeller.h b/shell/gpu/gpu_surface_vulkan_impeller.h index 5f6aee9f13530..d0b4787fd7924 100644 --- a/shell/gpu/gpu_surface_vulkan_impeller.h +++ b/shell/gpu/gpu_surface_vulkan_impeller.h @@ -32,8 +32,7 @@ class GPUSurfaceVulkanImpeller final : public Surface { fml::WeakPtrFactory weak_factory_; // |Surface| - std::unique_ptr AcquireFrame(int64_t view_id, - const SkISize& size) override; + std::unique_ptr AcquireFrame(const SkISize& size) override; // |Surface| SkMatrix GetRootTransformation() const override; 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 0bc41ed13d723..d2c6bc971d755 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.cc +++ b/shell/platform/android/external_view_embedder/external_view_embedder.cc @@ -8,8 +8,6 @@ namespace flutter { -constexpr int64_t kFlutterDefaultViewId = 0ll; - AndroidExternalViewEmbedder::AndroidExternalViewEmbedder( const AndroidContext& android_context, std::shared_ptr jni_facade, @@ -173,8 +171,6 @@ void AndroidExternalViewEmbedder::SubmitFrame( } std::unique_ptr frame = CreateSurfaceIfNeeded(context, // - // TODO(dkwingsmt) - kFlutterDefaultViewId, // view_id, // slices_.at(view_id).get(), // overlay->second // @@ -188,7 +184,6 @@ void AndroidExternalViewEmbedder::SubmitFrame( // |ExternalViewEmbedder| std::unique_ptr AndroidExternalViewEmbedder::CreateSurfaceIfNeeded(GrDirectContext* context, - int64_t render_view_id, int64_t view_id, EmbedderViewSlice* slice, const SkRect& rect) { @@ -196,7 +191,7 @@ AndroidExternalViewEmbedder::CreateSurfaceIfNeeded(GrDirectContext* context, context, android_context_, jni_facade_, surface_factory_); std::unique_ptr frame = - layer->surface->AcquireFrame(render_view_id, frame_size_); + 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, // 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 42aa48235a909..15a96fc2c8424 100644 --- a/shell/platform/android/external_view_embedder/external_view_embedder.h +++ b/shell/platform/android/external_view_embedder/external_view_embedder.h @@ -136,7 +136,6 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder { // 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 render_view_id, int64_t view_id, EmbedderViewSlice* slice, const SkRect& rect); 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 1d38f73069874..523c48e3b7d77 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 @@ -24,8 +24,6 @@ namespace flutter { namespace testing { -constexpr int64_t kDefaultViewId = 0ll; - using ::testing::ByMove; using ::testing::Return; @@ -53,7 +51,7 @@ class SurfaceMock : public Surface { MOCK_METHOD(std::unique_ptr, AcquireFrame, - (int64_t view_id, const SkISize& size), + (const SkISize& size), (override)); MOCK_METHOD(SkMatrix, GetRootTransformation, (), (const, override)); @@ -274,7 +272,7 @@ TEST(AndroidExternalViewEmbedder, SubmitFrame) { /*frame_size=*/SkISize::Make(800, 600)); auto surface_mock = std::make_unique(); - EXPECT_CALL(*surface_mock, AcquireFrame(kDefaultViewId, frame_size)) + EXPECT_CALL(*surface_mock, AcquireFrame(frame_size)) .Times(2 /* frames */) .WillOnce(Return(ByMove(std::move(surface_frame_1)))) .WillOnce(Return(ByMove(std::move(surface_frame_2)))); @@ -475,7 +473,7 @@ TEST(AndroidExternalViewEmbedder, OverlayCoverTwoPlatformViews) { /*frame_size=*/SkISize::Make(800, 600)); auto surface_mock = std::make_unique(); - EXPECT_CALL(*surface_mock, AcquireFrame(kDefaultViewId, frame_size)) + EXPECT_CALL(*surface_mock, AcquireFrame(frame_size)) .Times(1 /* frames */) .WillOnce(Return(ByMove(std::move(surface_frame_1)))); @@ -573,7 +571,7 @@ TEST(AndroidExternalViewEmbedder, SubmitFrameOverlayComposition) { /*frame_size=*/SkISize::Make(800, 600)); auto surface_mock = std::make_unique(); - EXPECT_CALL(*surface_mock, AcquireFrame(kDefaultViewId, frame_size)) + EXPECT_CALL(*surface_mock, AcquireFrame(frame_size)) .Times(1 /* frames */) .WillOnce(Return(ByMove(std::move(surface_frame_1)))); @@ -676,7 +674,7 @@ TEST(AndroidExternalViewEmbedder, SubmitFramePlatformViewWithoutAnyOverlay) { /*frame_size=*/SkISize::Make(800, 600)); auto surface_mock = std::make_unique(); - EXPECT_CALL(*surface_mock, AcquireFrame(kDefaultViewId, frame_size)) + EXPECT_CALL(*surface_mock, AcquireFrame(frame_size)) .Times(1 /* frames */) .WillOnce(Return(ByMove(std::move(surface_frame_1)))); @@ -764,7 +762,7 @@ TEST(AndroidExternalViewEmbedder, DestroyOverlayLayersOnSizeChange) { /*frame_size=*/SkISize::Make(800, 600)); auto surface_mock = std::make_unique(); - EXPECT_CALL(*surface_mock, AcquireFrame(kDefaultViewId, frame_size)) + EXPECT_CALL(*surface_mock, AcquireFrame(frame_size)) .WillOnce(Return(ByMove(std::move(surface_frame_1)))); auto android_surface_mock = std::make_unique(); @@ -852,7 +850,7 @@ TEST(AndroidExternalViewEmbedder, DoesNotDestroyOverlayLayersOnSizeChange) { /*frame_size=*/SkISize::Make(800, 600)); auto surface_mock = std::make_unique(); - EXPECT_CALL(*surface_mock, AcquireFrame(kDefaultViewId, frame_size)) + EXPECT_CALL(*surface_mock, AcquireFrame(frame_size)) .WillOnce(Return(ByMove(std::move(surface_frame_1)))); auto android_surface_mock = std::make_unique(); @@ -979,7 +977,7 @@ TEST(AndroidExternalViewEmbedder, Teardown) { /*frame_size=*/SkISize::Make(800, 600)); auto surface_mock = std::make_unique(); - EXPECT_CALL(*surface_mock, AcquireFrame(kDefaultViewId, frame_size)) + EXPECT_CALL(*surface_mock, AcquireFrame(frame_size)) .WillOnce(Return(ByMove(std::move(surface_frame_1)))); auto android_surface_mock = std::make_unique(); diff --git a/shell/platform/darwin/ios/ios_surface_metal_impeller.h b/shell/platform/darwin/ios/ios_surface_metal_impeller.h index 88abe8fd82b24..9f29a9cc68dc3 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_impeller.h +++ b/shell/platform/darwin/ios/ios_surface_metal_impeller.h @@ -51,7 +51,7 @@ class SK_API_AVAILABLE_CA_METAL_LAYER IOSSurfaceMetalImpeller final bool PresentDrawable(GrMTLHandle drawable) const override; // |GPUSurfaceMetalDelegate| - GPUMTLTextureInfo GetMTLTexture(int64_t view_id, const SkISize& frame_info) const override; + GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_info) const override; // |GPUSurfaceMetalDelegate| bool PresentTexture(GPUMTLTextureInfo texture) const override; diff --git a/shell/platform/darwin/ios/ios_surface_metal_impeller.mm b/shell/platform/darwin/ios/ios_surface_metal_impeller.mm index 5f56d54f54af0..ee429fd4ffa2a 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_impeller.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_impeller.mm @@ -81,8 +81,7 @@ } // |GPUSurfaceMetalDelegate| -GPUMTLTextureInfo IOSSurfaceMetalImpeller::GetMTLTexture(int64_t view_id, - const SkISize& frame_info) const { +GPUMTLTextureInfo IOSSurfaceMetalImpeller::GetMTLTexture(const SkISize& frame_info) const { FML_CHECK(false); return GPUMTLTextureInfo{ .texture_id = -1, // diff --git a/shell/platform/darwin/ios/ios_surface_metal_skia.h b/shell/platform/darwin/ios/ios_surface_metal_skia.h index 926ad1346b117..43f6dd1268ae9 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_skia.h +++ b/shell/platform/darwin/ios/ios_surface_metal_skia.h @@ -49,7 +49,7 @@ class SK_API_AVAILABLE_CA_METAL_LAYER IOSSurfaceMetalSkia final : public IOSSurf bool PresentDrawable(GrMTLHandle drawable) const override; // |GPUSurfaceMetalDelegate| - GPUMTLTextureInfo GetMTLTexture(int64_t view_id, const SkISize& frame_info) const override; + GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_info) const override; // |GPUSurfaceMetalDelegate| bool PresentTexture(GPUMTLTextureInfo texture) const override; diff --git a/shell/platform/darwin/ios/ios_surface_metal_skia.mm b/shell/platform/darwin/ios/ios_surface_metal_skia.mm index f2984c8a6dc68..2c45d4de60f16 100644 --- a/shell/platform/darwin/ios/ios_surface_metal_skia.mm +++ b/shell/platform/darwin/ios/ios_surface_metal_skia.mm @@ -98,8 +98,7 @@ } // |GPUSurfaceMetalDelegate| -GPUMTLTextureInfo IOSSurfaceMetalSkia::GetMTLTexture(int64_t view_id, - const SkISize& frame_info) const { +GPUMTLTextureInfo IOSSurfaceMetalSkia::GetMTLTexture(const SkISize& frame_info) const { FML_CHECK(false) << "render to texture not supported on ios"; return {.texture_id = -1, .texture = nullptr}; } diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 817cb3788b18a..5da0d509f48f5 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -478,9 +478,8 @@ InferMetalPlatformViewCreationCallback( return ptr(user_data, &embedder_texture); }; auto metal_get_texture = - [ptr = config->metal.get_next_drawable_callback, user_data]( - int64_t view_id, - const SkISize& frame_size) -> flutter::GPUMTLTextureInfo { + [ptr = config->metal.get_next_drawable_callback, + user_data](const SkISize& frame_size) -> flutter::GPUMTLTextureInfo { FlutterFrameInfo frame_info = {}; frame_info.struct_size = sizeof(FlutterFrameInfo); frame_info.size = {static_cast(frame_size.width()), diff --git a/shell/platform/embedder/embedder_studio_metal.h b/shell/platform/embedder/embedder_studio_metal.h index 6c126370bed74..6c7d9e6e838fe 100644 --- a/shell/platform/embedder/embedder_studio_metal.h +++ b/shell/platform/embedder/embedder_studio_metal.h @@ -20,7 +20,7 @@ class EmbedderStudioMetal final : public EmbedderStudio, public: struct MetalDispatchTable { std::function present; // required - std::function + std::function get_texture; // required }; @@ -52,8 +52,7 @@ class EmbedderStudioMetal final : public EmbedderStudio, bool PresentDrawable(GrMTLHandle drawable) const override; // |GPUSurfaceMetalDelegate| - GPUMTLTextureInfo GetMTLTexture(int64_t view_id, - const SkISize& frame_size) const override; + GPUMTLTextureInfo GetMTLTexture(const SkISize& frame_size) const override; // |GPUSurfaceMetalDelegate| bool PresentTexture(GPUMTLTextureInfo texture) const override; diff --git a/shell/platform/embedder/embedder_studio_metal.mm b/shell/platform/embedder/embedder_studio_metal.mm index 4f1cbae62c16a..6a7f3fb267815 100644 --- a/shell/platform/embedder/embedder_studio_metal.mm +++ b/shell/platform/embedder/embedder_studio_metal.mm @@ -84,9 +84,8 @@ return false; } -GPUMTLTextureInfo EmbedderStudioMetal::GetMTLTexture(int64_t view_id, - const SkISize& frame_info) const { - return metal_dispatch_table_.get_texture(view_id, frame_info); +GPUMTLTextureInfo EmbedderStudioMetal::GetMTLTexture(const SkISize& frame_info) const { + return metal_dispatch_table_.get_texture(frame_info); } bool EmbedderStudioMetal::PresentTexture(GPUMTLTextureInfo texture) const { diff --git a/shell/platform/fuchsia/flutter/surface.cc b/shell/platform/fuchsia/flutter/surface.cc index 5b8e1d29623fe..beb58b3d51478 100644 --- a/shell/platform/fuchsia/flutter/surface.cc +++ b/shell/platform/fuchsia/flutter/surface.cc @@ -27,7 +27,6 @@ bool Surface::IsValid() { // |flutter::Surface| std::unique_ptr Surface::AcquireFrame( - int64_t view_id, const SkISize& size) { flutter::SurfaceFrame::FramebufferInfo framebuffer_info; framebuffer_info.supports_readback = true; diff --git a/shell/platform/fuchsia/flutter/surface.h b/shell/platform/fuchsia/flutter/surface.h index e6876392c7499..aa76a65d5f285 100644 --- a/shell/platform/fuchsia/flutter/surface.h +++ b/shell/platform/fuchsia/flutter/surface.h @@ -29,7 +29,6 @@ class Surface final : public flutter::Surface { // |flutter::Surface| std::unique_ptr AcquireFrame( - int64_t view_id, const SkISize& size) override; // |flutter::Surface| From 1b1209777c5f8e2fbf55e0e61e3820c78d51e2d6 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 31 May 2023 00:25:24 -0700 Subject: [PATCH 145/147] Fix compile --- .../ios/framework/Source/FlutterPlatformViews_Internal.h | 1 - shell/platform/fuchsia/flutter/platform_view_unittest.cc | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 80d3c92dfe612..8447d19ca2df9 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -318,7 +318,6 @@ class FlutterPlatformViewsController { // the picture on the layer's canvas. std::shared_ptr GetLayer(GrDirectContext* gr_context, const std::shared_ptr& ios_context, - int64_t render_view_id, EmbedderViewSlice* slice, SkRect rect, int64_t view_id, diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index d18e11d56694a..db1a8577679f0 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -55,8 +55,7 @@ class MockExternalViewEmbedder : public flutter::ExternalViewEmbedder { fml::RefPtr raster_thread_merger) override {} void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame, - int64_t window_view_id) override {} + std::unique_ptr frame) override {} void PrerollCompositeEmbeddedView( int64_t view_id, From 6192080a2e79d4be2fcff22fb12bfbdf133fb442 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 2 Jun 2023 13:27:26 -0700 Subject: [PATCH 146/147] Doc fix --- shell/platform/embedder/embedder.h | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 2a54931713f49..1abc1011ccbe4 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -1621,6 +1621,7 @@ typedef struct { /// A callback invoked by the engine to release the backing store. The /// embedder may collect any resources associated with the backing store. FlutterBackingStoreCollectCallback collect_backing_store_callback; + /// Callback invoked by the engine to composite the contents of each layer /// onto the screen. FlutterLayersPresentCallback present_layers_callback; /// Avoid caching backing stores provided by this compositor. From fa03dd2dedd69db8de82385a4faaeffa8eb4e671 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 6 Jun 2023 15:08:41 -0700 Subject: [PATCH 147/147] Add back files that were somehow removed --- .github/labeler.yml | 34 +++ .github/workflows/labeler.yml | 20 ++ .../benchmarking/dl_region_benchmarks.cc | 92 ++++++ display_list/geometry/dl_region.cc | 248 ++++++++++++++++ display_list/geometry/dl_region.h | 65 ++++ display_list/geometry/dl_region_unittests.cc | 207 +++++++++++++ fml/concurrent_message_loop_factory.cc | 15 + .../darwin/concurrent_message_loop_factory.mm | 26 ++ .../backend/vulkan/compute_pass_vk.cc | 277 ++++++++++++++++++ .../renderer/backend/vulkan/compute_pass_vk.h | 40 +++ .../backend/vulkan/compute_pipeline_vk.cc | 54 ++++ .../backend/vulkan/compute_pipeline_vk.h | 53 ++++ .../renderer/threadgroup_sizing_test.comp | 18 ++ impeller/typographer/rectangle_packer.cc | 173 +++++++++++ impeller/typographer/rectangle_packer.h | 71 +++++ .../2_dispose_op_restore_previous.apng | Bin 0 -> 193101 bytes .../2_dispose_op_restore_previous.apng.67.png | Bin 0 -> 23297 bytes .../2_dispose_op_restore_previous.apng.68.png | Bin 0 -> 23242 bytes .../2_dispose_op_restore_previous.apng.69.png | Bin 0 -> 23451 bytes 19 files changed, 1393 insertions(+) create mode 100644 .github/labeler.yml create mode 100644 .github/workflows/labeler.yml create mode 100644 display_list/benchmarking/dl_region_benchmarks.cc create mode 100644 display_list/geometry/dl_region.cc create mode 100644 display_list/geometry/dl_region.h create mode 100644 display_list/geometry/dl_region_unittests.cc create mode 100644 fml/concurrent_message_loop_factory.cc create mode 100644 fml/platform/darwin/concurrent_message_loop_factory.mm create mode 100644 impeller/renderer/backend/vulkan/compute_pass_vk.cc create mode 100644 impeller/renderer/backend/vulkan/compute_pass_vk.h create mode 100644 impeller/renderer/backend/vulkan/compute_pipeline_vk.cc create mode 100644 impeller/renderer/backend/vulkan/compute_pipeline_vk.h create mode 100644 impeller/renderer/threadgroup_sizing_test.comp create mode 100644 impeller/typographer/rectangle_packer.cc create mode 100644 impeller/typographer/rectangle_packer.h create mode 100644 lib/ui/fixtures/2_dispose_op_restore_previous.apng create mode 100644 lib/ui/fixtures/2_dispose_op_restore_previous.apng.67.png create mode 100644 lib/ui/fixtures/2_dispose_op_restore_previous.apng.68.png create mode 100644 lib/ui/fixtures/2_dispose_op_restore_previous.apng.69.png diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000000000..91d8713608a3d --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,34 @@ +# 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. + +# See https://github.com/actions/labeler/blob/main/README.md for docs. +embedder: + - shell/platform/embedder + +impeller: + - impeller/**/* + +platform-android: + - shell/platform/android/**/* + +platform-ios: + - shell/platform/darwin/common/**/* + - shell/platform/darwin/ios/**/* + +platform-fuchsia: + - shell/platform/fuchsia/**/* + +platform-linux: + - shell/platform/linux/**/* + +platform-macos: + - shell/platform/darwin/common/**/* + - shell/platform/darwin/macos/**/* + +platform-web: + - lib/web_ui/**/* + - '**/web_sdk/**/*' + +platform-windows: + - shell/platform/windows/**/* diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 0000000000000..3a5577155a088 --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,20 @@ +# 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. + +name: "Pull Request Labeler" +on: +- pull_request_target + +# Declare default permissions as read only. +permissions: read-all + +jobs: + triage: + if: ${{ github.repository == 'flutter/flutter' }} + permissions: + pull-requests: write + runs-on: ubuntu-latest + steps: + # Source available at https://github.com/actions/labeler/blob/main/README.md + - uses: actions/labeler@0776a679364a9a16110aac8d0f40f5e11009e327 diff --git a/display_list/benchmarking/dl_region_benchmarks.cc b/display_list/benchmarking/dl_region_benchmarks.cc new file mode 100644 index 0000000000000..a07faeba88e8e --- /dev/null +++ b/display_list/benchmarking/dl_region_benchmarks.cc @@ -0,0 +1,92 @@ +// 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. + +#include "flutter/benchmarking/benchmarking.h" + +#include "flutter/display_list/geometry/dl_region.h" +#include "third_party/skia/include/core/SkRegion.h" + +#include + +class SkRegionAdapter { + public: + void addRect(const SkIRect& rect) { region_.op(rect, SkRegion::kUnion_Op); } + + std::vector getRects() { + std::vector rects; + SkRegion::Iterator it(region_); + while (!it.done()) { + rects.push_back(it.rect()); + it.next(); + } + return rects; + } + + private: + SkRegion region_; +}; + +class DlRegionAdapter { + public: + void addRect(const SkIRect& rect) { rects_.push_back(rect); } + + std::vector getRects() { + flutter::DlRegion region(std::move(rects_)); + return region.getRects(false); + } + + private: + std::vector rects_; +}; + +template +void RunRegionBenchmark(benchmark::State& state, int maxSize) { + while (state.KeepRunning()) { + std::random_device d; + std::seed_seq seed{2, 1, 3}; + std::mt19937 rng(seed); + + std::uniform_int_distribution pos(0, 4000); + std::uniform_int_distribution size(1, maxSize); + + Region region; + + for (int i = 0; i < 2000; ++i) { + SkIRect rect = + SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); + region.addRect(rect); + } + + auto vec2 = region.getRects(); + } +} + +namespace flutter { + +static void BM_RegionBenchmarkSkRegion(benchmark::State& state, int maxSize) { + RunRegionBenchmark(state, maxSize); +} + +static void BM_RegionBenchmarkDlRegion(benchmark::State& state, int maxSize) { + RunRegionBenchmark(state, maxSize); +} + +BENCHMARK_CAPTURE(BM_RegionBenchmarkDlRegion, Tiny, 30) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_RegionBenchmarkSkRegion, Tiny, 30) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_RegionBenchmarkDlRegion, Small, 100) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_RegionBenchmarkSkRegion, Small, 100) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_RegionBenchmarkDlRegion, Medium, 400) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_RegionBenchmarkSkRegion, Medium, 400) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_RegionBenchmarkDlRegion, Large, 1500) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_RegionBenchmarkSkRegion, Large, 1500) + ->Unit(benchmark::kMicrosecond); + +} // namespace flutter \ No newline at end of file diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc new file mode 100644 index 0000000000000..281fd142b393f --- /dev/null +++ b/display_list/geometry/dl_region.cc @@ -0,0 +1,248 @@ +// 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. + +#include "flutter/display_list/geometry/dl_region.h" + +#include "flutter/fml/logging.h" + +namespace flutter { + +DlRegion::DlRegion(std::vector&& rects) { + // If SpanLines can not be memmoved `addRect` would be signifantly slower + // due to cost of inserting and removing elements from the `lines_` vector. + static_assert(std::is_trivially_constructible::value, + "SpanLine must be trivially constructible."); + addRects(std::move(rects)); + + for (auto& spanvec : spanvec_pool_) { + delete spanvec; + } + spanvec_pool_.clear(); +} + +DlRegion::~DlRegion() { + for (auto& line : lines_) { + delete line.spans; + } +} + +std::vector DlRegion::getRects(bool deband) const { + std::vector rects; + size_t previous_span_end = 0; + for (const auto& line : lines_) { + for (const Span& span : *line.spans) { + SkIRect rect{span.left, line.top, span.right, line.bottom}; + if (deband) { + auto iter = rects.begin() + previous_span_end; + // If there is rectangle previously in rects on which this one is a + // vertical continuation, remove the previous rectangle and expand + // this one vertically to cover the area. + while (iter != rects.begin()) { + --iter; + if (iter->bottom() < rect.top()) { + // Went all the way to previous span line. + break; + } else if (iter->left() == rect.left() && + iter->right() == rect.right()) { + FML_DCHECK(iter->bottom() == rect.top()); + rect.fTop = iter->fTop; + rects.erase(iter); + --previous_span_end; + break; + } + } + } + rects.push_back(rect); + } + previous_span_end = rects.size(); + } + return rects; +} + +void DlRegion::SpanLine::insertSpan(int32_t left, int32_t right) { + auto& spans = *this->spans; + auto size = spans.size(); + for (size_t i = 0; i < size; ++i) { + Span& span = spans[i]; + if (right < span.left) { + spans.insert(spans.begin() + i, {left, right}); + return; + } + if (left > span.right) { + continue; + } + size_t last_index = i; + while (last_index + 1 < size && right >= spans[last_index + 1].left) { + ++last_index; + } + span.left = std::min(span.left, left); + span.right = std::max(spans[last_index].right, right); + if (last_index > i) { + spans.erase(spans.begin() + i + 1, spans.begin() + last_index + 1); + } + return; + } + + spans.push_back({left, right}); +} + +bool DlRegion::SpanLine::spansEqual(const SpanLine& l2) const { + SpanVec& spans = *this->spans; + SpanVec& otherSpans = *l2.spans; + FML_DCHECK(this != &l2); + + if (spans.size() != otherSpans.size()) { + return false; + } + return memcmp(spans.data(), otherSpans.data(), spans.size() * sizeof(Span)) == + 0; +} + +void DlRegion::insertLine(size_t position, SpanLine line) { + lines_.insert(lines_.begin() + position, line); +} + +DlRegion::LineVec::iterator DlRegion::removeLine( + DlRegion::LineVec::iterator line) { + spanvec_pool_.push_back(line->spans); + return lines_.erase(line); +} + +DlRegion::SpanLine DlRegion::makeLine(int32_t top, + int32_t bottom, + int32_t spanLeft, + int32_t spanRight) { + SpanVec* span_vec; + if (!spanvec_pool_.empty()) { + span_vec = spanvec_pool_.back(); + spanvec_pool_.pop_back(); + span_vec->clear(); + } else { + span_vec = new SpanVec(); + } + span_vec->push_back({spanLeft, spanRight}); + return {top, bottom, span_vec}; +} + +DlRegion::SpanLine DlRegion::makeLine(int32_t top, + int32_t bottom, + const SpanVec& spans) { + SpanVec* span_vec; + if (!spanvec_pool_.empty()) { + span_vec = spanvec_pool_.back(); + spanvec_pool_.pop_back(); + } else { + span_vec = new SpanVec(); + } + *span_vec = spans; + return {top, bottom, span_vec}; +} + +void DlRegion::addRects(std::vector&& rects) { + std::sort(rects.begin(), rects.end(), [](const SkIRect& a, const SkIRect& b) { + // Sort the rectangles by Y axis. Because the rectangles have varying + // height, they are added to span lines in non-deterministic order and thus + // it makes no difference if they are also sorted by the X axis. + return a.top() < b.top(); + }); + + size_t start_index = 0; + + size_t dirty_start = std::numeric_limits::max(); + size_t dirty_end = 0; + + // Marks line as dirty. Dirty lines will be checked for equality + // later and merged as needed. + auto mark_dirty = [&](size_t line) { + dirty_start = std::min(dirty_start, line); + dirty_end = std::max(dirty_end, line); + }; + + for (const SkIRect& rect : rects) { + if (rect.isEmpty()) { + continue; + } + + int32_t y1 = rect.fTop; + int32_t y2 = rect.fBottom; + + for (size_t i = start_index; i < lines_.size() && y1 < y2; ++i) { + SpanLine& line = lines_[i]; + + if (rect.fTop >= line.bottom) { + start_index = i; + continue; + } + + if (y2 <= line.top) { + insertLine(i, makeLine(y1, y2, rect.fLeft, rect.fRight)); + mark_dirty(i); + y1 = y2; + break; + } + if (y1 < line.top) { + auto prevLineStart = line.top; + insertLine(i, makeLine(y1, prevLineStart, rect.fLeft, rect.fRight)); + mark_dirty(i); + y1 = prevLineStart; + continue; + } + if (y1 > line.top) { + // duplicate line + auto prevLineEnd = line.bottom; + line.bottom = y1; + mark_dirty(i); + insertLine(i + 1, makeLine(y1, prevLineEnd, *line.spans)); + continue; + } + FML_DCHECK(y1 == line.top); + if (y2 < line.bottom) { + // duplicate line + auto newLine = makeLine(y2, line.bottom, *line.spans); + line.bottom = y2; + line.insertSpan(rect.fLeft, rect.fRight); + insertLine(i + 1, newLine); + y1 = y2; + mark_dirty(i); + break; + } + FML_DCHECK(y2 >= line.bottom); + line.insertSpan(rect.fLeft, rect.fRight); + mark_dirty(i); + y1 = line.bottom; + } + + if (y1 < y2) { + lines_.push_back(makeLine(y1, y2, rect.fLeft, rect.fRight)); + mark_dirty(lines_.size() - 1); + } + + // Check for duplicate lines and merge them. + if (dirty_start <= dirty_end) { + // Expand the region by one if possible. + if (dirty_start > 0) { + --dirty_start; + } + if (dirty_end + 1 < lines_.size()) { + ++dirty_end; + } + for (auto i = lines_.begin() + dirty_start; + i < lines_.begin() + dirty_end;) { + auto& line = *i; + auto& next = *(i + 1); + if (line.bottom == next.top && line.spansEqual(next)) { + --dirty_end; + next.top = line.top; + i = removeLine(i); + } else { + ++i; + } + } + } + dirty_start = std::numeric_limits::max(); + dirty_end = 0; + } +} + +} // namespace flutter diff --git a/display_list/geometry/dl_region.h b/display_list/geometry/dl_region.h new file mode 100644 index 0000000000000..159b32c37d7dd --- /dev/null +++ b/display_list/geometry/dl_region.h @@ -0,0 +1,65 @@ +// 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_DISPLAY_LIST_GEOMETRY_REGION_H_ +#define FLUTTER_DISPLAY_LIST_GEOMETRY_REGION_H_ + +#include "third_party/skia/include/core/SkRect.h" + +#include + +namespace flutter { + +/// Represents a region as a collection of non-overlapping rectangles. +/// Implements a subset of SkRegion functionality optimized for quickly +/// converting set of overlapping rectangles to non-overlapping rectangles. +class DlRegion { + public: + /// Creates region by bulk adding the rectangles./// Matches + /// SkRegion::op(rect, SkRegion::kUnion_Op) behavior. + explicit DlRegion(std::vector&& rects); + ~DlRegion(); + + /// Returns list of non-overlapping rectangles that cover current region. + /// If |deband| is false, each span line will result in separate rectangles, + /// closely matching SkRegion::Iterator behavior. + /// If |deband| is true, matching rectangles from adjacent span lines will be + /// merged into single rectange. + std::vector getRects(bool deband = true) const; + + private: + void addRects(std::vector&& rects); + + struct Span { + int32_t left; + int32_t right; + }; + typedef std::vector SpanVec; + struct SpanLine { + int32_t top; + int32_t bottom; + SpanVec* spans; + + void insertSpan(int32_t left, int32_t right); + bool spansEqual(const SpanLine& l2) const; + }; + + typedef std::vector LineVec; + + std::vector lines_; + std::vector spanvec_pool_; + + void insertLine(size_t position, SpanLine line); + LineVec::iterator removeLine(LineVec::iterator position); + + SpanLine makeLine(int32_t top, + int32_t bottom, + int32_t spanLeft, + int32_t spanRight); + SpanLine makeLine(int32_t top, int32_t bottom, const SpanVec& spans); +}; + +} // namespace flutter + +#endif // FLUTTER_DISPLAY_LIST_GEOMETRY_REGION_H_ diff --git a/display_list/geometry/dl_region_unittests.cc b/display_list/geometry/dl_region_unittests.cc new file mode 100644 index 0000000000000..f9610ff3c5561 --- /dev/null +++ b/display_list/geometry/dl_region_unittests.cc @@ -0,0 +1,207 @@ +// 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. + +#include "flutter/display_list/geometry/dl_region.h" +#include "gtest/gtest.h" + +#include "third_party/skia/include/core/SkRegion.h" + +#include + +namespace flutter { +namespace testing { + +TEST(DisplayListRegion, EmptyRegion) { + DlRegion region({}); + EXPECT_TRUE(region.getRects().empty()); +} + +TEST(DisplayListRegion, SingleRectangle) { + DlRegion region({SkIRect::MakeLTRB(10, 10, 50, 50)}); + auto rects = region.getRects(); + ASSERT_EQ(rects.size(), 1u); + EXPECT_EQ(rects.front(), SkIRect::MakeLTRB(10, 10, 50, 50)); +} + +TEST(DisplayListRegion, NonOverlappingRectangles1) { + std::vector rects_in; + for (int i = 0; i < 10; ++i) { + SkIRect rect = SkIRect::MakeXYWH(50 * i, 50 * i, 50, 50); + rects_in.push_back(rect); + } + DlRegion region(std::move(rects_in)); + auto rects = region.getRects(); + std::vector expected{ + {0, 0, 50, 50}, {50, 50, 100, 100}, {100, 100, 150, 150}, + {150, 150, 200, 200}, {200, 200, 250, 250}, {250, 250, 300, 300}, + {300, 300, 350, 350}, {350, 350, 400, 400}, {400, 400, 450, 450}, + {450, 450, 500, 500}, + }; + EXPECT_EQ(rects, expected); +} + +TEST(DisplayListRegion, NonOverlappingRectangles2) { + DlRegion region({ + SkIRect::MakeXYWH(5, 5, 10, 10), + SkIRect::MakeXYWH(25, 5, 10, 10), + SkIRect::MakeXYWH(5, 25, 10, 10), + SkIRect::MakeXYWH(25, 25, 10, 10), + }); + auto rects = region.getRects(); + std::vector expected{ + SkIRect::MakeXYWH(5, 5, 10, 10), + SkIRect::MakeXYWH(25, 5, 10, 10), + SkIRect::MakeXYWH(5, 25, 10, 10), + SkIRect::MakeXYWH(25, 25, 10, 10), + }; + EXPECT_EQ(rects, expected); +} + +TEST(DisplayListRegion, NonOverlappingRectangles3) { + DlRegion region({ + SkIRect::MakeXYWH(0, 0, 10, 10), + SkIRect::MakeXYWH(-11, -11, 10, 10), + SkIRect::MakeXYWH(11, 11, 10, 10), + SkIRect::MakeXYWH(-11, 0, 10, 10), + SkIRect::MakeXYWH(0, 11, 10, 10), + SkIRect::MakeXYWH(0, -11, 10, 10), + SkIRect::MakeXYWH(11, 0, 10, 10), + SkIRect::MakeXYWH(11, -11, 10, 10), + SkIRect::MakeXYWH(-11, 11, 10, 10), + }); + auto rects = region.getRects(); + std::vector expected{ + SkIRect::MakeXYWH(-11, -11, 10, 10), // + SkIRect::MakeXYWH(0, -11, 10, 10), // + SkIRect::MakeXYWH(11, -11, 10, 10), // + SkIRect::MakeXYWH(-11, 0, 10, 10), // + SkIRect::MakeXYWH(0, 0, 10, 10), // + SkIRect::MakeXYWH(11, 0, 10, 10), // + SkIRect::MakeXYWH(-11, 11, 10, 10), // + SkIRect::MakeXYWH(0, 11, 10, 10), // + SkIRect::MakeXYWH(11, 11, 10, 10), + }; + EXPECT_EQ(rects, expected); +} + +TEST(DisplayListRegion, MergeTouchingRectangles) { + DlRegion region({ + SkIRect::MakeXYWH(0, 0, 10, 10), + SkIRect::MakeXYWH(-10, -10, 10, 10), + SkIRect::MakeXYWH(10, 10, 10, 10), + SkIRect::MakeXYWH(-10, 0, 10, 10), + SkIRect::MakeXYWH(0, 10, 10, 10), + SkIRect::MakeXYWH(0, -10, 10, 10), + SkIRect::MakeXYWH(10, 0, 10, 10), + SkIRect::MakeXYWH(10, -10, 10, 10), + SkIRect::MakeXYWH(-10, 10, 10, 10), + }); + + auto rects = region.getRects(); + std::vector expected{ + SkIRect::MakeXYWH(-10, -10, 30, 30), + }; + EXPECT_EQ(rects, expected); +} + +TEST(DisplayListRegion, OverlappingRectangles) { + std::vector rects_in; + for (int i = 0; i < 10; ++i) { + SkIRect rect = SkIRect::MakeXYWH(10 * i, 10 * i, 50, 50); + rects_in.push_back(rect); + } + DlRegion region(std::move(rects_in)); + auto rects = region.getRects(); + std::vector expected{ + {0, 0, 50, 10}, {0, 10, 60, 20}, {0, 20, 70, 30}, + {0, 30, 80, 40}, {0, 40, 90, 50}, {10, 50, 100, 60}, + {20, 60, 110, 70}, {30, 70, 120, 80}, {40, 80, 130, 90}, + {50, 90, 140, 100}, {60, 100, 140, 110}, {70, 110, 140, 120}, + {80, 120, 140, 130}, {90, 130, 140, 140}, + }; + + EXPECT_EQ(rects, expected); +} + +TEST(DisplayListRegion, Deband) { + DlRegion region({ + SkIRect::MakeXYWH(0, 0, 50, 50), + SkIRect::MakeXYWH(60, 0, 20, 20), + SkIRect::MakeXYWH(90, 0, 50, 50), + }); + + auto rects_with_deband = region.getRects(true); + std::vector expected{ + SkIRect::MakeXYWH(60, 0, 20, 20), + SkIRect::MakeXYWH(0, 0, 50, 50), + SkIRect::MakeXYWH(90, 0, 50, 50), + }; + EXPECT_EQ(rects_with_deband, expected); + + auto rects_without_deband = region.getRects(false); + std::vector expected_without_deband{ + SkIRect::MakeXYWH(0, 0, 50, 20), // + SkIRect::MakeXYWH(60, 0, 20, 20), // + SkIRect::MakeXYWH(90, 0, 50, 20), // + SkIRect::MakeXYWH(0, 20, 50, 30), // + SkIRect::MakeXYWH(90, 20, 50, 30), + }; + EXPECT_EQ(rects_without_deband, expected_without_deband); +} + +TEST(DisplayListRegion, TestAgainstSkRegion) { + struct Settings { + int max_size; + size_t iteration_count; + }; + std::vector all_settings{ + {100, 10}, // + {100, 100}, // + {100, 1000}, // + {400, 10}, // + {400, 100}, // + {400, 1000}, // + {800, 10}, // + {800, 100}, // + {800, 1000}, + }; + + for (const auto& settings : all_settings) { + std::random_device d; + std::seed_seq seed{::testing::UnitTest::GetInstance()->random_seed()}; + std::mt19937 rng(seed); + + SkRegion sk_region; + + std::uniform_int_distribution pos(0, 4000); + std::uniform_int_distribution size(1, settings.max_size); + + std::vector rects_in; + + for (size_t i = 0; i < settings.iteration_count; ++i) { + SkIRect rect = + SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); + rects_in.push_back(rect); + sk_region.op(rect, SkRegion::kUnion_Op); + } + + DlRegion region(std::move(rects_in)); + + // Do not deband the rectangles - identical to SkRegion::Iterator + auto rects = region.getRects(false); + + std::vector skia_rects; + + auto iterator = SkRegion::Iterator(sk_region); + while (!iterator.done()) { + skia_rects.push_back(iterator.rect()); + iterator.next(); + } + + EXPECT_EQ(rects, skia_rects); + } +} + +} // namespace testing +} // namespace flutter diff --git a/fml/concurrent_message_loop_factory.cc b/fml/concurrent_message_loop_factory.cc new file mode 100644 index 0000000000000..e5f879efd765f --- /dev/null +++ b/fml/concurrent_message_loop_factory.cc @@ -0,0 +1,15 @@ +// 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. + +#include "flutter/fml/concurrent_message_loop.h" + +namespace fml { + +std::shared_ptr ConcurrentMessageLoop::Create( + size_t worker_count) { + return std::shared_ptr{ + new ConcurrentMessageLoop(worker_count)}; +} + +} // namespace fml diff --git a/fml/platform/darwin/concurrent_message_loop_factory.mm b/fml/platform/darwin/concurrent_message_loop_factory.mm new file mode 100644 index 0000000000000..4abd1e1048e4e --- /dev/null +++ b/fml/platform/darwin/concurrent_message_loop_factory.mm @@ -0,0 +1,26 @@ +// 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. + +#include "flutter/fml/concurrent_message_loop.h" + +namespace fml { + +class ConcurrentMessageLoopDarwin : public ConcurrentMessageLoop { + friend class ConcurrentMessageLoop; + + protected: + explicit ConcurrentMessageLoopDarwin(size_t worker_count) : ConcurrentMessageLoop(worker_count) {} + + void ExecuteTask(const fml::closure& task) override { + @autoreleasepool { + task(); + } + } +}; + +std::shared_ptr ConcurrentMessageLoop::Create(size_t worker_count) { + return std::shared_ptr{new ConcurrentMessageLoopDarwin(worker_count)}; +} + +} // namespace fml diff --git a/impeller/renderer/backend/vulkan/compute_pass_vk.cc b/impeller/renderer/backend/vulkan/compute_pass_vk.cc new file mode 100644 index 0000000000000..cb1074f32f9d5 --- /dev/null +++ b/impeller/renderer/backend/vulkan/compute_pass_vk.cc @@ -0,0 +1,277 @@ +// 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. + +#include "impeller/renderer/backend/vulkan/compute_pass_vk.h" + +#include "flutter/fml/trace_event.h" +#include "impeller/renderer/backend/vulkan/compute_pipeline_vk.h" +#include "impeller/renderer/backend/vulkan/sampler_vk.h" +#include "impeller/renderer/backend/vulkan/texture_vk.h" + +namespace impeller { + +ComputePassVK::ComputePassVK(std::weak_ptr context, + std::weak_ptr encoder) + : ComputePass(std::move(context)), encoder_(std::move(encoder)) { + is_valid_ = true; +} + +ComputePassVK::~ComputePassVK() = default; + +bool ComputePassVK::IsValid() const { + return is_valid_; +} + +void ComputePassVK::OnSetLabel(const std::string& label) { + if (label.empty()) { + return; + } + label_ = label; +} + +static bool UpdateBindingLayouts(const Bindings& bindings, + const vk::CommandBuffer& buffer) { + LayoutTransition transition; + transition.cmd_buffer = buffer; + transition.src_access = vk::AccessFlagBits::eTransferWrite; + transition.src_stage = vk::PipelineStageFlagBits::eTransfer; + transition.dst_access = vk::AccessFlagBits::eShaderRead; + transition.dst_stage = vk::PipelineStageFlagBits::eComputeShader; + + transition.new_layout = vk::ImageLayout::eShaderReadOnlyOptimal; + + for (const auto& [_, texture] : bindings.textures) { + if (!TextureVK::Cast(*texture.resource).SetLayout(transition)) { + return false; + } + } + return true; +} + +static bool UpdateBindingLayouts(const ComputeCommand& command, + const vk::CommandBuffer& buffer) { + return UpdateBindingLayouts(command.bindings, buffer); +} + +static bool UpdateBindingLayouts(const std::vector& commands, + const vk::CommandBuffer& buffer) { + for (const auto& command : commands) { + if (!UpdateBindingLayouts(command, buffer)) { + return false; + } + } + return true; +} + +static bool AllocateAndBindDescriptorSets(const ContextVK& context, + const ComputeCommand& command, + CommandEncoderVK& encoder, + const ComputePipelineVK& pipeline) { + auto desc_set = pipeline.GetDescriptor().GetDescriptorSetLayouts(); + auto vk_desc_set = + encoder.AllocateDescriptorSet(pipeline.GetDescriptorSetLayout()); + if (!vk_desc_set) { + return false; + } + + auto& allocator = *context.GetResourceAllocator(); + + std::unordered_map buffers; + std::unordered_map images; + std::vector writes; + + auto bind_images = [&encoder, // + &images, // + &writes, // + &vk_desc_set // + ](const Bindings& bindings) -> bool { + for (const auto& [index, sampler_handle] : bindings.samplers) { + if (bindings.textures.find(index) == bindings.textures.end()) { + return false; + } + + auto texture = bindings.textures.at(index).resource; + const auto& texture_vk = TextureVK::Cast(*texture); + const SamplerVK& sampler = SamplerVK::Cast(*sampler_handle.resource); + + if (!encoder.Track(texture) || + !encoder.Track(sampler.GetSharedSampler())) { + return false; + } + + const SampledImageSlot& slot = bindings.sampled_images.at(index); + + vk::DescriptorImageInfo image_info; + image_info.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal; + image_info.sampler = sampler.GetSampler(); + image_info.imageView = texture_vk.GetImageView(); + + vk::WriteDescriptorSet write_set; + write_set.dstSet = vk_desc_set.value(); + write_set.dstBinding = slot.binding; + write_set.descriptorCount = 1u; + write_set.descriptorType = vk::DescriptorType::eCombinedImageSampler; + write_set.pImageInfo = &(images[slot.binding] = image_info); + + writes.push_back(write_set); + } + + return true; + }; + + auto bind_buffers = [&allocator, // + &encoder, // + &buffers, // + &writes, // + &desc_set, // + &vk_desc_set // + ](const Bindings& bindings) -> bool { + for (const auto& [buffer_index, view] : bindings.buffers) { + const auto& buffer_view = view.resource.buffer; + + auto device_buffer = buffer_view->GetDeviceBuffer(allocator); + if (!device_buffer) { + VALIDATION_LOG << "Failed to get device buffer for vertex binding"; + return false; + } + + auto buffer = DeviceBufferVK::Cast(*device_buffer).GetBuffer(); + if (!buffer) { + return false; + } + + if (!encoder.Track(device_buffer)) { + return false; + } + + uint32_t offset = view.resource.range.offset; + + vk::DescriptorBufferInfo buffer_info; + buffer_info.buffer = buffer; + buffer_info.offset = offset; + buffer_info.range = view.resource.range.length; + + const ShaderUniformSlot& uniform = bindings.uniforms.at(buffer_index); + auto layout_it = std::find_if(desc_set.begin(), desc_set.end(), + [&uniform](DescriptorSetLayout& layout) { + return layout.binding == uniform.binding; + }); + if (layout_it == desc_set.end()) { + VALIDATION_LOG << "Failed to get descriptor set layout for binding " + << uniform.binding; + return false; + } + auto layout = *layout_it; + + vk::WriteDescriptorSet write_set; + write_set.dstSet = vk_desc_set.value(); + write_set.dstBinding = uniform.binding; + write_set.descriptorCount = 1u; + write_set.descriptorType = ToVKDescriptorType(layout.descriptor_type); + write_set.pBufferInfo = &(buffers[uniform.binding] = buffer_info); + + writes.push_back(write_set); + } + return true; + }; + + if (!bind_buffers(command.bindings) || !bind_images(command.bindings)) { + return false; + } + + context.GetDevice().updateDescriptorSets(writes, {}); + + encoder.GetCommandBuffer().bindDescriptorSets( + vk::PipelineBindPoint::eCompute, // bind point + pipeline.GetPipelineLayout(), // layout + 0, // first set + {vk::DescriptorSet{*vk_desc_set}}, // sets + nullptr // offsets + ); + return true; +} + +bool ComputePassVK::OnEncodeCommands(const Context& context, + const ISize& grid_size, + const ISize& thread_group_size) const { + TRACE_EVENT0("impeller", "ComputePassVK::EncodeCommands"); + if (!IsValid()) { + return false; + } + + FML_DCHECK(!grid_size.IsEmpty() && !thread_group_size.IsEmpty()); + + const auto& vk_context = ContextVK::Cast(context); + auto encoder = encoder_.lock(); + if (!encoder) { + VALIDATION_LOG << "Command encoder died before commands could be encoded."; + return false; + } + + fml::ScopedCleanupClosure pop_marker( + [&encoder]() { encoder->PopDebugGroup(); }); + if (!label_.empty()) { + encoder->PushDebugGroup(label_.c_str()); + } else { + pop_marker.Release(); + } + auto cmd_buffer = encoder->GetCommandBuffer(); + + if (!UpdateBindingLayouts(commands_, cmd_buffer)) { + VALIDATION_LOG << "Could not update binding layouts for compute pass."; + return false; + } + + { + TRACE_EVENT0("impeller", "EncodeComputePassCommands"); + + for (const auto& command : commands_) { + if (!command.pipeline) { + continue; + } + + const auto& pipeline_vk = ComputePipelineVK::Cast(*command.pipeline); + + cmd_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, + pipeline_vk.GetPipeline()); + if (!AllocateAndBindDescriptorSets(vk_context, // + command, // + *encoder, // + pipeline_vk // + )) { + return false; + } + + // TOOD(dnfield): This should be moved to caps. But for now keeping this + // in parallel with Metal. + auto device_properties = vk_context.GetPhysicalDevice().getProperties(); + + auto max_wg_size = device_properties.limits.maxComputeWorkGroupSize; + + int64_t width = grid_size.width; + int64_t height = grid_size.height; + + // Special case for linear processing. + if (height == 1) { + int64_t minimum = 1; + int64_t threadGroups = std::max( + static_cast(std::ceil(width * 1.0 / max_wg_size[0] * 1.0)), + minimum); + cmd_buffer.dispatch(threadGroups, 1, 1); + } else { + while (width > max_wg_size[0]) { + width = std::max(static_cast(1), width / 2); + } + while (height > max_wg_size[1]) { + height = std::max(static_cast(1), height / 2); + } + cmd_buffer.dispatch(width, height, 1); + } + } + } + + return true; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/compute_pass_vk.h b/impeller/renderer/backend/vulkan/compute_pass_vk.h new file mode 100644 index 0000000000000..83c1c6f056adb --- /dev/null +++ b/impeller/renderer/backend/vulkan/compute_pass_vk.h @@ -0,0 +1,40 @@ +// 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. + +#pragma once + +#include "flutter/fml/macros.h" +#include "impeller/renderer/backend/vulkan/command_encoder_vk.h" +#include "impeller/renderer/compute_pass.h" + +namespace impeller { + +class ComputePassVK final : public ComputePass { + public: + // |ComputePass| + ~ComputePassVK() override; + + private: + friend class CommandBufferVK; + + std::weak_ptr encoder_; + std::string label_; + bool is_valid_ = false; + + ComputePassVK(std::weak_ptr context, + std::weak_ptr encoder); + + // |ComputePass| + bool IsValid() const override; + + // |ComputePass| + void OnSetLabel(const std::string& label) override; + + // |ComputePass| + bool OnEncodeCommands(const Context& context, + const ISize& grid_size, + const ISize& thread_group_size) const override; +}; + +} // namespace impeller \ No newline at end of file diff --git a/impeller/renderer/backend/vulkan/compute_pipeline_vk.cc b/impeller/renderer/backend/vulkan/compute_pipeline_vk.cc new file mode 100644 index 0000000000000..af48c2691ddcc --- /dev/null +++ b/impeller/renderer/backend/vulkan/compute_pipeline_vk.cc @@ -0,0 +1,54 @@ +// 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. + +#include "impeller/renderer/backend/vulkan/compute_pipeline_vk.h" + +namespace impeller { + +ComputePipelineVK::ComputePipelineVK( + std::weak_ptr device_holder, + std::weak_ptr library, + const ComputePipelineDescriptor& desc, + vk::UniquePipeline pipeline, + vk::UniquePipelineLayout layout, + vk::UniqueDescriptorSetLayout descriptor_set_layout) + : Pipeline(std::move(library), desc), + device_holder_(std::move(device_holder)), + pipeline_(std::move(pipeline)), + layout_(std::move(layout)), + descriptor_set_layout_(std::move(descriptor_set_layout)) { + is_valid_ = pipeline_ && layout_ && descriptor_set_layout_; +} + +ComputePipelineVK::~ComputePipelineVK() { + std::shared_ptr device_holder = device_holder_.lock(); + if (device_holder) { + descriptor_set_layout_.reset(); + layout_.reset(); + pipeline_.reset(); + } else { + descriptor_set_layout_.release(); + layout_.release(); + pipeline_.release(); + } +} + +bool ComputePipelineVK::IsValid() const { + return is_valid_; +} + +const vk::Pipeline& ComputePipelineVK::GetPipeline() const { + return *pipeline_; +} + +const vk::PipelineLayout& ComputePipelineVK::GetPipelineLayout() const { + return *layout_; +} + +const vk::DescriptorSetLayout& ComputePipelineVK::GetDescriptorSetLayout() + const { + return *descriptor_set_layout_; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/compute_pipeline_vk.h b/impeller/renderer/backend/vulkan/compute_pipeline_vk.h new file mode 100644 index 0000000000000..3d1b7622bc741 --- /dev/null +++ b/impeller/renderer/backend/vulkan/compute_pipeline_vk.h @@ -0,0 +1,53 @@ +// 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. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/base/backend_cast.h" +#include "impeller/renderer/backend/vulkan/device_holder.h" +#include "impeller/renderer/backend/vulkan/vk.h" +#include "impeller/renderer/pipeline.h" + +namespace impeller { + +class ComputePipelineVK final + : public Pipeline, + public BackendCast> { + public: + ComputePipelineVK(std::weak_ptr device_holder, + std::weak_ptr library, + const ComputePipelineDescriptor& desc, + vk::UniquePipeline pipeline, + vk::UniquePipelineLayout layout, + vk::UniqueDescriptorSetLayout descriptor_set_layout); + + // |Pipeline| + ~ComputePipelineVK() override; + + const vk::Pipeline& GetPipeline() const; + + const vk::PipelineLayout& GetPipelineLayout() const; + + const vk::DescriptorSetLayout& GetDescriptorSetLayout() const; + + private: + friend class PipelineLibraryVK; + + std::weak_ptr device_holder_; + vk::UniquePipeline pipeline_; + vk::UniquePipelineLayout layout_; + vk::UniqueDescriptorSetLayout descriptor_set_layout_; + bool is_valid_ = false; + + // |Pipeline| + bool IsValid() const override; + + FML_DISALLOW_COPY_AND_ASSIGN(ComputePipelineVK); +}; + +} // namespace impeller diff --git a/impeller/renderer/threadgroup_sizing_test.comp b/impeller/renderer/threadgroup_sizing_test.comp new file mode 100644 index 0000000000000..3d2e02af507ca --- /dev/null +++ b/impeller/renderer/threadgroup_sizing_test.comp @@ -0,0 +1,18 @@ +// 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. + +// Size is passed in via specialization constant. +layout(local_size_x_id = 0) in; + +layout(std430) buffer; + +layout(binding = 1) writeonly buffer OutputData { + uint data[]; +} +output_data; + +void main() { + uint ident = gl_GlobalInvocationID.x; + output_data.data[ident] = ident; +} diff --git a/impeller/typographer/rectangle_packer.cc b/impeller/typographer/rectangle_packer.cc new file mode 100644 index 0000000000000..b80faa7d4e147 --- /dev/null +++ b/impeller/typographer/rectangle_packer.cc @@ -0,0 +1,173 @@ +// 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. + +#include "impeller/typographer/rectangle_packer.h" + +#include +#include + +namespace impeller { + +// Pack rectangles and track the current silhouette +// Based, in part, on Jukka Jylanki's work at http://clb.demon.fi +// and ported from Skia's implementation +// https://github.com/google/skia/blob/b5de4b8ae95c877a9ecfad5eab0765bc22550301/src/gpu/RectanizerSkyline.cpp +class SkylineRectanglePacker final : public RectanglePacker { + public: + SkylineRectanglePacker(int w, int h) : RectanglePacker(w, h) { + this->reset(); + } + + ~SkylineRectanglePacker() final {} + + void reset() final { + area_so_far_ = 0; + skyline_.clear(); + skyline_.push_back(SkylineSegment{0, 0, this->width()}); + } + + bool addRect(int w, int h, IPoint16* loc) final; + + float percentFull() const final { + return area_so_far_ / ((float)this->width() * this->height()); + } + + private: + struct SkylineSegment { + int x_; + int y_; + int width_; + }; + + std::vector skyline_; + + int32_t area_so_far_; + + // Can a width x height rectangle fit in the free space represented by + // the skyline segments >= 'skylineIndex'? If so, return true and fill in + // 'y' with the y-location at which it fits (the x location is pulled from + // 'skylineIndex's segment. + bool rectangleFits(int skylineIndex, int width, int height, int* y) const; + // Update the skyline structure to include a width x height rect located + // at x,y. + void addSkylineLevel(int skylineIndex, int x, int y, int width, int height); +}; + +bool SkylineRectanglePacker::addRect(int width, int height, IPoint16* loc) { + if ((unsigned)width > (unsigned)this->width() || + (unsigned)height > (unsigned)this->height()) { + return false; + } + + // find position for new rectangle + int bestWidth = this->width() + 1; + int bestX = 0; + int bestY = this->height() + 1; + int bestIndex = -1; + for (int i = 0; i < (int)skyline_.size(); ++i) { + int y; + if (this->rectangleFits(i, width, height, &y)) { + // minimize y position first, then width of skyline + if (y < bestY || (y == bestY && skyline_[i].width_ < bestWidth)) { + bestIndex = i; + bestWidth = skyline_[i].width_; + bestX = skyline_[i].x_; + bestY = y; + } + } + } + + // add rectangle to skyline + if (-1 != bestIndex) { + this->addSkylineLevel(bestIndex, bestX, bestY, width, height); + loc->x_ = bestX; + loc->y_ = bestY; + + area_so_far_ += width * height; + return true; + } + + loc->x_ = 0; + loc->y_ = 0; + return false; +} + +bool SkylineRectanglePacker::rectangleFits(int skylineIndex, + int width, + int height, + int* ypos) const { + int x = skyline_[skylineIndex].x_; + if (x + width > this->width()) { + return false; + } + + int widthLeft = width; + int i = skylineIndex; + int y = skyline_[skylineIndex].y_; + while (widthLeft > 0 && i < (int)skyline_.size()) { + y = std::max(y, skyline_[i].y_); + if (y + height > this->height()) { + return false; + } + widthLeft -= skyline_[i].width_; + ++i; + } + + *ypos = y; + return true; +} + +void SkylineRectanglePacker::addSkylineLevel(int skylineIndex, + int x, + int y, + int width, + int height) { + SkylineSegment newSegment; + newSegment.x_ = x; + newSegment.y_ = y + height; + newSegment.width_ = width; + skyline_.insert(std::next(skyline_.begin(), skylineIndex), newSegment); + + FML_DCHECK(newSegment.x_ + newSegment.width_ <= this->width()); + FML_DCHECK(newSegment.y_ <= this->height()); + + // delete width of the new skyline segment from following ones + for (int i = skylineIndex + 1; i < (int)skyline_.size(); ++i) { + // The new segment subsumes all or part of skyline_[i] + FML_DCHECK(skyline_[i - 1].x_ <= skyline_[i].x_); + + if (skyline_[i].x_ < skyline_[i - 1].x_ + skyline_[i - 1].width_) { + int shrink = skyline_[i - 1].x_ + skyline_[i - 1].width_ - skyline_[i].x_; + + skyline_[i].x_ += shrink; + skyline_[i].width_ -= shrink; + + if (skyline_[i].width_ <= 0) { + // fully consumed, remove item at index i + skyline_.erase(std::next(skyline_.begin(), i)); + --i; + } else { + // only partially consumed + break; + } + } else { + break; + } + } + + // merge skyline_s + for (int i = 0; i < ((int)skyline_.size()) - 1; ++i) { + if (skyline_[i].y_ == skyline_[i + 1].y_) { + skyline_[i].width_ += skyline_[i + 1].width_; + skyline_.erase(std::next(skyline_.begin(), i)); + --i; + } + } +} + +RectanglePacker* RectanglePacker::Factory(int width, int height) { + return new SkylineRectanglePacker(width, height); +} + +} // namespace impeller diff --git a/impeller/typographer/rectangle_packer.h b/impeller/typographer/rectangle_packer.h new file mode 100644 index 0000000000000..836849b42e239 --- /dev/null +++ b/impeller/typographer/rectangle_packer.h @@ -0,0 +1,71 @@ +// 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. + +#pragma once + +#include "flutter/fml/logging.h" + +#include + +namespace impeller { + +struct IPoint16 { + int16_t x() const { return x_; } + int16_t y() const { return y_; } + + int16_t x_; + int16_t y_; +}; + +//------------------------------------------------------------------------------ +/// @brief Packs rectangles into a specified area without rotating them. +/// +class RectanglePacker { + public: + //---------------------------------------------------------------------------- + /// @brief Return an empty packer with area specified by width and height. + /// + static RectanglePacker* Factory(int width, int height); + + virtual ~RectanglePacker() {} + + //---------------------------------------------------------------------------- + /// @brief Attempt to add a rect without moving already placed rectangles. + /// + /// @param[in] width The width of the rectangle to add. + /// @param[in] height The height of the rectangle to add. + /// @param[out] loc If successful, will be set to the position of the + /// upper-left corner of the rectangle. + /// + /// @return Return true on success; false on failure. + /// + virtual bool addRect(int width, int height, IPoint16* loc) = 0; + + //---------------------------------------------------------------------------- + /// @brief Returns how much area has been filled with rectangles. + /// + /// @return Percentage as a decimal between 0.0 and 1.0 + /// + virtual float percentFull() const = 0; + + //---------------------------------------------------------------------------- + /// @brief Empty out all previously added rectangles. + /// + virtual void reset() = 0; + + protected: + RectanglePacker(int width, int height) : width_(width), height_(height) { + FML_DCHECK(width >= 0); + FML_DCHECK(height >= 0); + } + + int width() const { return width_; } + int height() const { return height_; } + + private: + const int width_; + const int height_; +}; + +} // namespace impeller diff --git a/lib/ui/fixtures/2_dispose_op_restore_previous.apng b/lib/ui/fixtures/2_dispose_op_restore_previous.apng new file mode 100644 index 0000000000000000000000000000000000000000..54f73736abc493ab22cd46338bcdc2d0b418b355 GIT binary patch literal 193101 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�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 ~Js7-*xjgY$ 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 z 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 literal 0 HcmV?d00001 diff --git a/lib/ui/fixtures/2_dispose_op_restore_previous.apng.67.png b/lib/ui/fixtures/2_dispose_op_restore_previous.apng.67.png new file mode 100644 index 0000000000000000000000000000000000000000..5410b759c04f499513e2dfaf359ebbde96f27a4d GIT binary patch literal 23297 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 literal 0 HcmV?d00001 diff --git a/lib/ui/fixtures/2_dispose_op_restore_previous.apng.68.png b/lib/ui/fixtures/2_dispose_op_restore_previous.apng.68.png new file mode 100644 index 0000000000000000000000000000000000000000..5f08106ccef06823377dfb7ec2c12fb8df20a32d GIT binary patch literal 23242 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[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