From 10410a2b70b2ae7b5a850cede66c971f25b66f64 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Fri, 13 Mar 2026 23:10:52 +0800 Subject: [PATCH 01/24] Add io task runner to pipeline library gles. Because IO thread is the owner of resource context, pipeline library gles can load resource by IO thread. --- .../renderer/backend/gles/context_gles.cc | 15 +++++++++------ .../impeller/renderer/backend/gles/context_gles.h | 6 ++++-- .../backend/gles/pipeline_library_gles.cc | 7 +++++-- .../renderer/backend/gles/pipeline_library_gles.h | 5 ++++- .../flutter/shell/platform/embedder/embedder.cc | 3 ++- .../embedder/embedder_surface_gl_impeller.cc | 5 +++-- .../embedder/embedder_surface_gl_impeller.h | 3 ++- 7 files changed, 29 insertions(+), 15 deletions(-) diff --git a/engine/src/flutter/impeller/renderer/backend/gles/context_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/context_gles.cc index 084b74d4e0ec5..070591eba3a7e 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/context_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/context_gles.cc @@ -22,16 +22,19 @@ std::shared_ptr ContextGLES::Create( const Flags& flags, std::unique_ptr gl, const std::vector>& shader_libraries, - bool enable_gpu_tracing) { - return std::shared_ptr(new ContextGLES( - flags, std::move(gl), shader_libraries, enable_gpu_tracing)); + bool enable_gpu_tracing, + fml::RefPtr io_task_runner) { + return std::shared_ptr( + new ContextGLES(flags, std::move(gl), shader_libraries, + enable_gpu_tracing, std::move(io_task_runner))); } ContextGLES::ContextGLES( const Flags& flags, std::unique_ptr gl, const std::vector>& shader_libraries_mappings, - bool enable_gpu_tracing) + bool enable_gpu_tracing, + fml::RefPtr io_task_runner) : Context(flags) { reactor_ = std::make_shared(std::move(gl)); if (!reactor_->IsValid()) { @@ -52,8 +55,8 @@ ContextGLES::ContextGLES( // Create the pipeline library. { - pipeline_library_ = - std::shared_ptr(new PipelineLibraryGLES(reactor_)); + pipeline_library_ = std::shared_ptr( + new PipelineLibraryGLES(reactor_, std::move(io_task_runner))); } // Create allocators. diff --git a/engine/src/flutter/impeller/renderer/backend/gles/context_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/context_gles.h index 44f2f873bb23e..ed1c4ab52920a 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/context_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/context_gles.h @@ -28,7 +28,8 @@ class ContextGLES final : public Context, const Flags& flags, std::unique_ptr gl, const std::vector>& shader_libraries, - bool enable_gpu_tracing); + bool enable_gpu_tracing, + fml::RefPtr io_task_runner = nullptr); // |Context| ~ContextGLES() override; @@ -64,7 +65,8 @@ class ContextGLES final : public Context, const Flags& flags, std::unique_ptr gl, const std::vector>& shader_libraries, - bool enable_gpu_tracing); + bool enable_gpu_tracing, + fml::RefPtr io_task_runner); // |Context| std::string DescribeGpuModel() const override; diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc index 240891dfc4b60..ba7893d4a1de2 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc @@ -16,8 +16,11 @@ namespace impeller { -PipelineLibraryGLES::PipelineLibraryGLES(std::shared_ptr reactor) - : reactor_(std::move(reactor)) {} +PipelineLibraryGLES::PipelineLibraryGLES( + std::shared_ptr reactor, + fml::RefPtr io_task_runner) + : reactor_(std::move(reactor)), + io_task_runner_(std::move(io_task_runner)) {} static std::string GetShaderInfoLog(const ProcTableGLES& gl, GLuint shader) { GLint log_length = 0; diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h index 25c51c8113d38..7c0103151c942 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h @@ -9,6 +9,7 @@ #include #include "flutter/fml/hash_combine.h" +#include "flutter/fml/task_runner.h" #include "impeller/base/thread.h" #include "impeller/renderer/backend/gles/reactor_gles.h" #include "impeller/renderer/backend/gles/unique_handle_gles.h" @@ -91,8 +92,10 @@ class PipelineLibraryGLES final PipelineMap pipelines_; Mutex programs_mutex_; ProgramMap programs_ IPLR_GUARDED_BY(programs_mutex_); + fml::RefPtr io_task_runner_; - explicit PipelineLibraryGLES(std::shared_ptr reactor); + explicit PipelineLibraryGLES(std::shared_ptr reactor, + fml::RefPtr io_task_runner); // |PipelineLibrary| bool IsValid() const override; diff --git a/engine/src/flutter/shell/platform/embedder/embedder.cc b/engine/src/flutter/shell/platform/embedder/embedder.cc index 190eb63969f04..88ed4161bb89e 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder.cc @@ -492,7 +492,8 @@ InferOpenGLPlatformViewCreationCallback( shell.GetTaskRunners(), // task runners std::make_unique( gl_dispatch_table, fbo_reset_after_present, - view_embedder), // embedder_surface + view_embedder, // embedder_surface + shell.GetTaskRunners().GetIOTaskRunner()), platform_dispatch_table, // embedder platform dispatch table view_embedder // external view embedder ); diff --git a/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc b/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc index 3832a69e3f3fc..42494a5575ce9 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc @@ -64,7 +64,8 @@ class ReactorWorker final : public impeller::ReactorGLES::Worker { EmbedderSurfaceGLImpeller::EmbedderSurfaceGLImpeller( EmbedderSurfaceGLSkia::GLDispatchTable gl_dispatch_table, bool fbo_reset_after_present, - std::shared_ptr external_view_embedder) + std::shared_ptr external_view_embedder, + fml::RefPtr io_task_runner) : gl_dispatch_table_(std::move(gl_dispatch_table)), fbo_reset_after_present_(fbo_reset_after_present), external_view_embedder_(std::move(external_view_embedder)), @@ -92,7 +93,7 @@ EmbedderSurfaceGLImpeller::EmbedderSurfaceGLImpeller( impeller_context_ = impeller::ContextGLES::Create( impeller::Flags{}, std::move(gl), shader_mappings, - /*enable_gpu_tracing=*/false); + /*enable_gpu_tracing=*/false, std::move(io_task_runner)); if (!impeller_context_) { FML_LOG(ERROR) << "Could not create Impeller context."; diff --git a/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.h b/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.h index 274e1ccb38f70..73db083573347 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.h +++ b/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.h @@ -30,7 +30,8 @@ class EmbedderSurfaceGLImpeller final : public EmbedderSurface, EmbedderSurfaceGLImpeller( EmbedderSurfaceGLSkia::GLDispatchTable gl_dispatch_table, bool fbo_reset_after_present, - std::shared_ptr external_view_embedder); + std::shared_ptr external_view_embedder, + fml::RefPtr io_task_runner); ~EmbedderSurfaceGLImpeller() override; From 6650c7ad377f77d560fefd030d8c6160a9e628ec Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Sat, 14 Mar 2026 05:03:52 +0800 Subject: [PATCH 02/24] Implement pipeline compile queue for gles --- .../gles/pipeline_compile_queue_gles.cc | 59 +++++++++++++++++++ .../gles/pipeline_compile_queue_gles.h | 45 ++++++++++++++ .../backend/gles/pipeline_library_gles.cc | 10 +++- .../backend/gles/pipeline_library_gles.h | 3 + .../renderer/pipeline_compile_queue.cc | 4 +- .../renderer/pipeline_compile_queue.h | 6 +- 6 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc create mode 100644 engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc new file mode 100644 index 0000000000000..3513bc6dc28c5 --- /dev/null +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc @@ -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. + +#include "impeller/renderer/backend/gles/pipeline_compile_queue_gles.h" + +#include "flutter/fml/logging.h" +#include "flutter/fml/trace_event.h" + +namespace impeller { + +std::shared_ptr PipelineCompileQueueGLES::Create( + fml::RefPtr task_runner) { + return std::shared_ptr( + new PipelineCompileQueueGLES(std::move(task_runner))); +} + +PipelineCompileQueueGLES::PipelineCompileQueueGLES( + fml::RefPtr task_runner) + : PipelineCompileQueue(nullptr), task_runner_(std::move(task_runner)) {} + +PipelineCompileQueueGLES::~PipelineCompileQueueGLES() { + FinishAllJobs(); +} + +bool PipelineCompileQueueGLES::PostJobForDescriptor( + const PipelineDescriptor& desc, + const fml::closure& job) { + if (!job) { + return false; + } + + { + Lock lock(pending_jobs_mutex_); + auto insertion_result = pending_jobs_.insert(std::make_pair(desc, job)); + if (!insertion_result.second) { + // This bit is being extremely conservative. If insertion did not take + // place, someone gave the compile queue a job for the same description. + // This is highly unusual but technically not impossible. Just run the job + // eagerly. + FML_LOG(ERROR) << "Got multiple compile jobs for the same descriptor. " + "Running eagerly."; + // Don't invoke the job here has there are we have currently acquired a + // mutex. + task_runner_->PostTask(job); + return true; + } + } + + task_runner_->PostTask([weak_queue = weak_from_this()]() { + if (auto queue = std::static_pointer_cast( + weak_queue.lock())) { + queue->DoOneJob(); + } + }); + return true; +} + +} // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h new file mode 100644 index 0000000000000..4d1971bd33d37 --- /dev/null +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h @@ -0,0 +1,45 @@ +// 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_IMPELLER_RENDERER_BACKEND_GLES_PIPELINE_COMPILE_QUEUE_GLES_H_ +#define FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_PIPELINE_COMPILE_QUEUE_GLES_H_ + +#include "flutter/fml/closure.h" +#include "flutter/fml/task_runner.h" +#include "impeller/renderer/pipeline_compile_queue.h" + +namespace impeller { + +class PipelineCompileQueueGLES : public PipelineCompileQueue { + public: + static std::shared_ptr Create( + fml::RefPtr task_runner); + + explicit PipelineCompileQueueGLES(fml::RefPtr task_runner); + + ~PipelineCompileQueueGLES() override; + + PipelineCompileQueueGLES(const PipelineCompileQueueGLES&) = delete; + + PipelineCompileQueueGLES& operator=(const PipelineCompileQueueGLES&) = delete; + + //---------------------------------------------------------------------------- + /// @brief Post a compile job for the specified descriptor. + /// + /// @param[in] desc The description + /// @param[in] job The job + /// + /// @return If the job was successfully posted to the parallel task + /// runners. + /// + bool PostJobForDescriptor(const PipelineDescriptor& desc, + const fml::closure& job) override; + + private: + fml::RefPtr task_runner_; +}; + +} // namespace impeller + +#endif // FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_PIPELINE_COMPILE_QUEUE_GLES_H_ diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc index ba7893d4a1de2..0f69a867722e3 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc @@ -20,7 +20,11 @@ PipelineLibraryGLES::PipelineLibraryGLES( std::shared_ptr reactor, fml::RefPtr io_task_runner) : reactor_(std::move(reactor)), - io_task_runner_(std::move(io_task_runner)) {} + io_task_runner_(std::move(io_task_runner)), + compile_queue_( + PipelineCompileQueue::Create(std::shared_ptr( + io_task_runner_.get(), + [runner = io_task_runner_](fml::BasicTaskRunner*) {}))) {} static std::string GetShaderInfoLog(const ProcTableGLES& gl, GLuint shader) { GLint log_length = 0; @@ -376,4 +380,8 @@ void PipelineLibraryGLES::SetProgramForKey( programs_[key] = std::move(program); } +PipelineCompileQueue* PipelineLibraryGLES::GetPipelineCompileQueue() const { + return compile_queue_.get(); +} + } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h index 7c0103151c942..62f9a837c65c0 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h @@ -93,6 +93,7 @@ class PipelineLibraryGLES final Mutex programs_mutex_; ProgramMap programs_ IPLR_GUARDED_BY(programs_mutex_); fml::RefPtr io_task_runner_; + std::shared_ptr compile_queue_; explicit PipelineLibraryGLES(std::shared_ptr reactor, fml::RefPtr io_task_runner); @@ -130,6 +131,8 @@ class PipelineLibraryGLES final void SetProgramForKey(const ProgramKey& key, std::shared_ptr program); + // |PipelineLibrary| + PipelineCompileQueue* GetPipelineCompileQueue() const override; }; } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc index 124e11e177346..2b9a453bf267d 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc @@ -10,13 +10,13 @@ namespace impeller { std::shared_ptr PipelineCompileQueue::Create( - std::shared_ptr worker_task_runner) { + std::shared_ptr worker_task_runner) { return std::shared_ptr( new PipelineCompileQueue(std::move(worker_task_runner))); } PipelineCompileQueue::PipelineCompileQueue( - std::shared_ptr worker_task_runner) + std::shared_ptr worker_task_runner) : worker_task_runner_(std::move(worker_task_runner)) {} PipelineCompileQueue::~PipelineCompileQueue() { diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h index d166679b3798f..31da6552157b4 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h @@ -43,7 +43,7 @@ class PipelineCompileQueue final : public std::enable_shared_from_this { public: static std::shared_ptr Create( - std::shared_ptr worker_task_runner); + std::shared_ptr worker_task_runner); ~PipelineCompileQueue(); @@ -73,7 +73,7 @@ class PipelineCompileQueue final void PerformJobEagerly(const PipelineDescriptor& desc); private: - std::shared_ptr worker_task_runner_; + std::shared_ptr worker_task_runner_; Mutex pending_jobs_mutex_; size_t priorities_elevated_ = {}; @@ -84,7 +84,7 @@ class PipelineCompileQueue final pending_jobs_ IPLR_GUARDED_BY(pending_jobs_mutex_); explicit PipelineCompileQueue( - std::shared_ptr worker_task_runner); + std::shared_ptr worker_task_runner); fml::closure TakeJob(const PipelineDescriptor& desc); From 4c3a1059f02e8f658cf8053374be19c195054ca0 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Mon, 16 Mar 2026 23:04:24 +0800 Subject: [PATCH 03/24] Fix build error --- .../impeller/renderer/backend/gles/BUILD.gn | 2 ++ .../gles/pipeline_compile_queue_gles.cc | 35 +++---------------- .../gles/pipeline_compile_queue_gles.h | 8 ++--- .../backend/gles/pipeline_library_gles.cc | 5 +-- .../backend/gles/pipeline_library_gles.h | 3 +- .../renderer/pipeline_compile_queue.cc | 12 +++++-- .../renderer/pipeline_compile_queue.h | 19 +++++++--- 7 files changed, 37 insertions(+), 47 deletions(-) diff --git a/engine/src/flutter/impeller/renderer/backend/gles/BUILD.gn b/engine/src/flutter/impeller/renderer/backend/gles/BUILD.gn index 38c9771b4be9e..e91c75671d3fb 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/BUILD.gn +++ b/engine/src/flutter/impeller/renderer/backend/gles/BUILD.gn @@ -68,6 +68,8 @@ impeller_component("gles") { "gpu_tracer_gles.h", "handle_gles.cc", "handle_gles.h", + "pipeline_compile_queue_gles.cc", + "pipeline_compile_queue_gles.h", "pipeline_gles.cc", "pipeline_gles.h", "pipeline_library_gles.cc", diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc index 3513bc6dc28c5..a2b51004e6a7d 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc @@ -19,41 +19,14 @@ PipelineCompileQueueGLES::PipelineCompileQueueGLES( fml::RefPtr task_runner) : PipelineCompileQueue(nullptr), task_runner_(std::move(task_runner)) {} -PipelineCompileQueueGLES::~PipelineCompileQueueGLES() { - FinishAllJobs(); -} +PipelineCompileQueueGLES::~PipelineCompileQueueGLES() {} -bool PipelineCompileQueueGLES::PostJobForDescriptor( - const PipelineDescriptor& desc, - const fml::closure& job) { +void PipelineCompileQueueGLES::PostJob(const fml::closure& job) { if (!job) { - return false; - } - - { - Lock lock(pending_jobs_mutex_); - auto insertion_result = pending_jobs_.insert(std::make_pair(desc, job)); - if (!insertion_result.second) { - // This bit is being extremely conservative. If insertion did not take - // place, someone gave the compile queue a job for the same description. - // This is highly unusual but technically not impossible. Just run the job - // eagerly. - FML_LOG(ERROR) << "Got multiple compile jobs for the same descriptor. " - "Running eagerly."; - // Don't invoke the job here has there are we have currently acquired a - // mutex. - task_runner_->PostTask(job); - return true; - } + return; } - task_runner_->PostTask([weak_queue = weak_from_this()]() { - if (auto queue = std::static_pointer_cast( - weak_queue.lock())) { - queue->DoOneJob(); - } - }); - return true; + task_runner_->PostTask(job); } } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h index 4d1971bd33d37..c35f0d34b7a23 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h @@ -25,16 +25,14 @@ class PipelineCompileQueueGLES : public PipelineCompileQueue { PipelineCompileQueueGLES& operator=(const PipelineCompileQueueGLES&) = delete; //---------------------------------------------------------------------------- - /// @brief Post a compile job for the specified descriptor. + /// @brief Post a job to the worker task runner. /// - /// @param[in] desc The description /// @param[in] job The job /// /// @return If the job was successfully posted to the parallel task - /// runners. + /// runners. /// - bool PostJobForDescriptor(const PipelineDescriptor& desc, - const fml::closure& job) override; + void PostJob(const fml::closure& job) override; private: fml::RefPtr task_runner_; diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc index 0f69a867722e3..70d2223cce3b9 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc @@ -21,10 +21,7 @@ PipelineLibraryGLES::PipelineLibraryGLES( fml::RefPtr io_task_runner) : reactor_(std::move(reactor)), io_task_runner_(std::move(io_task_runner)), - compile_queue_( - PipelineCompileQueue::Create(std::shared_ptr( - io_task_runner_.get(), - [runner = io_task_runner_](fml::BasicTaskRunner*) {}))) {} + compile_queue_(PipelineCompileQueueGLES::Create(io_task_runner_)) {} static std::string GetShaderInfoLog(const ProcTableGLES& gl, GLuint shader) { GLint log_length = 0; diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h index 62f9a837c65c0..fa36a88294144 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h @@ -11,6 +11,7 @@ #include "flutter/fml/hash_combine.h" #include "flutter/fml/task_runner.h" #include "impeller/base/thread.h" +#include "impeller/renderer/backend/gles/pipeline_compile_queue_gles.h" #include "impeller/renderer/backend/gles/reactor_gles.h" #include "impeller/renderer/backend/gles/unique_handle_gles.h" #include "impeller/renderer/pipeline_library.h" @@ -93,7 +94,7 @@ class PipelineLibraryGLES final Mutex programs_mutex_; ProgramMap programs_ IPLR_GUARDED_BY(programs_mutex_); fml::RefPtr io_task_runner_; - std::shared_ptr compile_queue_; + std::shared_ptr compile_queue_; explicit PipelineLibraryGLES(std::shared_ptr reactor, fml::RefPtr io_task_runner); diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc index 2b9a453bf267d..87e3f4fa8d4a2 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc @@ -41,12 +41,12 @@ bool PipelineCompileQueue::PostJobForDescriptor(const PipelineDescriptor& desc, "Running eagerly."; // Don't invoke the job here has there are we have currently acquired a // mutex. - worker_task_runner_->PostTask(job); + PostJob(job); return true; } } - worker_task_runner_->PostTask([weak_queue = weak_from_this()]() { + PostJob([weak_queue = weak_from_this()]() { if (auto queue = weak_queue.lock()) { queue->DoOneJob(); } @@ -115,4 +115,12 @@ void PipelineCompileQueue::PerformJobEagerly(const PipelineDescriptor& desc) { } } +void PipelineCompileQueue::PostJob(const fml::closure& job) { + if (!job) { + return; + } + + worker_task_runner_->PostTask(job); +} + } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h index 31da6552157b4..320ffc17bf2ae 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h @@ -39,13 +39,17 @@ namespace impeller { /// entirely optional. The queue skipping mechanism all assume the /// optional availability of a compile queue. /// -class PipelineCompileQueue final +class PipelineCompileQueue : public std::enable_shared_from_this { public: static std::shared_ptr Create( std::shared_ptr worker_task_runner); ~PipelineCompileQueue(); + explicit PipelineCompileQueue( + std::shared_ptr worker_task_runner); + + virtual ~PipelineCompileQueue(); PipelineCompileQueue(const PipelineCompileQueue&) = delete; @@ -72,6 +76,16 @@ class PipelineCompileQueue final /// void PerformJobEagerly(const PipelineDescriptor& desc); + //---------------------------------------------------------------------------- + /// @brief Post a job to the worker task runner. + /// + /// @param[in] job The job + /// + /// @return If the job was successfully posted to the parallel task + /// runners. + /// + virtual void PostJob(const fml::closure& job); + private: std::shared_ptr worker_task_runner_; Mutex pending_jobs_mutex_; @@ -83,9 +97,6 @@ class PipelineCompileQueue final ComparableEqual> pending_jobs_ IPLR_GUARDED_BY(pending_jobs_mutex_); - explicit PipelineCompileQueue( - std::shared_ptr worker_task_runner); - fml::closure TakeJob(const PipelineDescriptor& desc); fml::closure TakeNextJob(); From 6539904aff3b949078cca8a5d8cd57cf9be721bc Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Tue, 17 Mar 2026 00:18:49 +0800 Subject: [PATCH 04/24] Add pipeline_compile_queue_vulkan.h --- .../gles/pipeline_compile_queue_gles.cc | 11 ++--- .../gles/pipeline_compile_queue_gles.h | 10 ++--- .../impeller/renderer/backend/vulkan/BUILD.gn | 2 + .../vulkan/pipeline_compile_queue_vulkan.cc | 33 +++++++++++++++ .../vulkan/pipeline_compile_queue_vulkan.h | 42 +++++++++++++++++++ .../backend/vulkan/pipeline_library_vk.cc | 2 +- .../backend/vulkan/pipeline_library_vk.h | 4 +- .../renderer/pipeline_compile_queue.cc | 18 -------- .../renderer/pipeline_compile_queue.h | 4 +- 9 files changed, 92 insertions(+), 34 deletions(-) create mode 100644 engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.cc create mode 100644 engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc index a2b51004e6a7d..334b7e29a1894 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc @@ -10,14 +10,15 @@ namespace impeller { std::shared_ptr PipelineCompileQueueGLES::Create( - fml::RefPtr task_runner) { + fml::RefPtr worker_task_runner) { return std::shared_ptr( - new PipelineCompileQueueGLES(std::move(task_runner))); + new PipelineCompileQueueGLES(std::move(worker_task_runner))); } PipelineCompileQueueGLES::PipelineCompileQueueGLES( - fml::RefPtr task_runner) - : PipelineCompileQueue(nullptr), task_runner_(std::move(task_runner)) {} + fml::RefPtr worker_task_runner) + : PipelineCompileQueue(), + worker_task_runner_(std::move(worker_task_runner)) {} PipelineCompileQueueGLES::~PipelineCompileQueueGLES() {} @@ -26,7 +27,7 @@ void PipelineCompileQueueGLES::PostJob(const fml::closure& job) { return; } - task_runner_->PostTask(job); + worker_task_runner_->PostTask(job); } } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h index c35f0d34b7a23..31fc6a528e48e 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h @@ -14,9 +14,10 @@ namespace impeller { class PipelineCompileQueueGLES : public PipelineCompileQueue { public: static std::shared_ptr Create( - fml::RefPtr task_runner); + fml::RefPtr worker_task_runner); - explicit PipelineCompileQueueGLES(fml::RefPtr task_runner); + explicit PipelineCompileQueueGLES( + fml::RefPtr worker_task_runner); ~PipelineCompileQueueGLES() override; @@ -29,13 +30,10 @@ class PipelineCompileQueueGLES : public PipelineCompileQueue { /// /// @param[in] job The job /// - /// @return If the job was successfully posted to the parallel task - /// runners. - /// void PostJob(const fml::closure& job) override; private: - fml::RefPtr task_runner_; + fml::RefPtr worker_task_runner_; }; } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn b/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn index 12fbcd6aea5ef..2cb67f2920249 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn @@ -80,6 +80,8 @@ impeller_component("vulkan") { "pipeline_cache_data_vk.h", "pipeline_cache_vk.cc", "pipeline_cache_vk.h", + "pipeline_compile_queue_vulkan.cc", + "pipeline_compile_queue_vulkan.h", "pipeline_library_vk.cc", "pipeline_library_vk.h", "pipeline_vk.cc", diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.cc new file mode 100644 index 0000000000000..3ecaabb16bdcc --- /dev/null +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.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 "impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h" + +#include "flutter/fml/logging.h" +#include "flutter/fml/trace_event.h" + +namespace impeller { + +std::shared_ptr PipelineCompileQueueVulkan::Create( + std::shared_ptr worker_task_runner) { + return std::shared_ptr( + new PipelineCompileQueueVulkan(std::move(worker_task_runner))); +} + +PipelineCompileQueueVulkan::PipelineCompileQueueVulkan( + std::shared_ptr worker_task_runner) + : PipelineCompileQueue(), + worker_task_runner_(std::move(worker_task_runner)) {} + +PipelineCompileQueueVulkan::~PipelineCompileQueueVulkan() {} + +void PipelineCompileQueueVulkan::PostJob(const fml::closure& job) { + if (!job) { + return; + } + + worker_task_runner_->PostTask(job); +} + +} // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h new file mode 100644 index 0000000000000..9f6474c9f958c --- /dev/null +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h @@ -0,0 +1,42 @@ +// 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_IMPELLER_RENDERER_BACKEND_VULKAN_PIPELINE_COMPILE_QUEUE_VULKAN_H_ +#define FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_PIPELINE_COMPILE_QUEUE_VULKAN_H_ + +#include "flutter/fml/closure.h" +#include "flutter/fml/task_runner.h" +#include "impeller/renderer/pipeline_compile_queue.h" + +namespace impeller { + +class PipelineCompileQueueVulkan : public PipelineCompileQueue { + public: + static std::shared_ptr Create( + std::shared_ptr worker_task_runner); + + explicit PipelineCompileQueueVulkan( + std::shared_ptr worker_task_runner); + + ~PipelineCompileQueueVulkan() override; + + PipelineCompileQueueVulkan(const PipelineCompileQueueVulkan&) = delete; + + PipelineCompileQueueVulkan& operator=(const PipelineCompileQueueVulkan&) = + delete; + + //---------------------------------------------------------------------------- + /// @brief Post a job to the worker task runner. + /// + /// @param[in] job The job + /// + void PostJob(const fml::closure& job) override; + + private: + std::shared_ptr worker_task_runner_; +}; + +} // namespace impeller + +#endif // FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_PIPELINE_COMPILE_QUEUE_VULKAN_H_ diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.cc index 0cbef862c2bd1..50ae6b3efb352 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.cc @@ -27,7 +27,7 @@ PipelineLibraryVK::PipelineLibraryVK( device_holder, std::move(cache_directory))), worker_task_runner_(std::move(worker_task_runner)), - compile_queue_(PipelineCompileQueue::Create(worker_task_runner_)) { + compile_queue_(PipelineCompileQueueVulkan::Create(worker_task_runner)) { FML_DCHECK(worker_task_runner_); if (!pso_cache_->IsValid() || !worker_task_runner_) { return; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.h b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.h index 3ead3c7dc700d..4304db9b58293 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.h @@ -13,10 +13,10 @@ #include "impeller/base/thread.h" #include "impeller/renderer/backend/vulkan/compute_pipeline_vk.h" #include "impeller/renderer/backend/vulkan/pipeline_cache_vk.h" +#include "impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h" #include "impeller/renderer/backend/vulkan/pipeline_vk.h" #include "impeller/renderer/backend/vulkan/vk.h" #include "impeller/renderer/pipeline.h" -#include "impeller/renderer/pipeline_compile_queue.h" #include "impeller/renderer/pipeline_library.h" namespace impeller { @@ -49,7 +49,7 @@ class PipelineLibraryVK final PipelineKey pipeline_key_ IPLR_GUARDED_BY(pipelines_mutex_) = 1; bool is_valid_ = false; bool cache_dirty_ = false; - std::shared_ptr compile_queue_; + std::shared_ptr compile_queue_; PipelineLibraryVK( const std::shared_ptr& device_holder, diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc index 87e3f4fa8d4a2..ad6f0fc6e0969 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc @@ -9,16 +9,6 @@ namespace impeller { -std::shared_ptr PipelineCompileQueue::Create( - std::shared_ptr worker_task_runner) { - return std::shared_ptr( - new PipelineCompileQueue(std::move(worker_task_runner))); -} - -PipelineCompileQueue::PipelineCompileQueue( - std::shared_ptr worker_task_runner) - : worker_task_runner_(std::move(worker_task_runner)) {} - PipelineCompileQueue::~PipelineCompileQueue() { FinishAllJobs(); } @@ -115,12 +105,4 @@ void PipelineCompileQueue::PerformJobEagerly(const PipelineDescriptor& desc) { } } -void PipelineCompileQueue::PostJob(const fml::closure& job) { - if (!job) { - return; - } - - worker_task_runner_->PostTask(job); -} - } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h index 320ffc17bf2ae..4ccff58af9eaa 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h @@ -48,6 +48,7 @@ class PipelineCompileQueue ~PipelineCompileQueue(); explicit PipelineCompileQueue( std::shared_ptr worker_task_runner); + PipelineCompileQueue() = default; virtual ~PipelineCompileQueue(); @@ -84,10 +85,9 @@ class PipelineCompileQueue /// @return If the job was successfully posted to the parallel task /// runners. /// - virtual void PostJob(const fml::closure& job); + virtual void PostJob(const fml::closure& job) = 0; private: - std::shared_ptr worker_task_runner_; Mutex pending_jobs_mutex_; size_t priorities_elevated_ = {}; From b7f9f28e6505d4620909d944c1d82dd66d246379 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Tue, 17 Mar 2026 04:41:14 +0800 Subject: [PATCH 05/24] Update GetPipeline method when sync == true --- .../backend/gles/pipeline_library_gles.cc | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc index 70d2223cce3b9..a3486f4550e63 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc @@ -300,17 +300,34 @@ PipelineFuture PipelineLibraryGLES::GetPipeline( PipelineFuture{descriptor, promise->get_future()}; pipelines_[descriptor] = pipeline_future; - const auto result = reactor_->AddOperation([promise, // - weak_this = weak_from_this(), // - descriptor, // - vert_function, // - frag_function, // - threadsafe // - ](const ReactorGLES& reactor) { - promise->set_value(CreatePipeline(weak_this, descriptor, vert_function, - frag_function, threadsafe)); - }); - FML_CHECK(result); + auto weak_this = weak_from_this(); + auto reactor = reactor_; + auto generation_task = [promise, weak_this, descriptor, vert_function, + frag_function, threadsafe, reactor]() { + auto thiz = weak_this.lock(); + if (!thiz) { + promise->set_value(nullptr); + return; + } + const auto result = reactor->AddOperation([promise, // + weak_this, // + descriptor, // + vert_function, // + frag_function, // + threadsafe // + ](const ReactorGLES& reactor) { + promise->set_value(CreatePipeline(weak_this, descriptor, vert_function, + frag_function, threadsafe)); + }); + FML_CHECK(result); + }; + + if (async) { + compile_queue_->PostJobForDescriptor(descriptor, + std::move(generation_task)); + } else { + generation_task(); + } return pipeline_future; } From 59c67734bdec970e3b4823f89f1d8e21f68440d3 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Fri, 27 Mar 2026 18:00:48 +0800 Subject: [PATCH 06/24] Make link program after schedule frame on IO thread --- .../gles/pipeline_compile_queue_gles.cc | 2 +- .../renderer/pipeline_compile_queue.cc | 24 ++++++++++++++----- .../renderer/pipeline_compile_queue.h | 8 +++++++ engine/src/flutter/shell/common/shell.cc | 20 +++++++++++++++- 4 files changed, 46 insertions(+), 8 deletions(-) diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc index 334b7e29a1894..e3e3e1debadb4 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc @@ -17,7 +17,7 @@ std::shared_ptr PipelineCompileQueueGLES::Create( PipelineCompileQueueGLES::PipelineCompileQueueGLES( fml::RefPtr worker_task_runner) - : PipelineCompileQueue(), + : PipelineCompileQueue(true), worker_task_runner_(std::move(worker_task_runner)) {} PipelineCompileQueueGLES::~PipelineCompileQueueGLES() {} diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc index ad6f0fc6e0969..639b5da2d0346 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc @@ -9,6 +9,9 @@ namespace impeller { +PipelineCompileQueue::PipelineCompileQueue(bool wait_until_rendering) + : wait_until_rendering_(wait_until_rendering) {} + PipelineCompileQueue::~PipelineCompileQueue() { FinishAllJobs(); } @@ -35,12 +38,13 @@ bool PipelineCompileQueue::PostJobForDescriptor(const PipelineDescriptor& desc, return true; } } - - PostJob([weak_queue = weak_from_this()]() { - if (auto queue = weak_queue.lock()) { - queue->DoOneJob(); - } - }); + if (!WaitUntilRendering()) { + PostJob([weak_queue = weak_from_this()]() { + if (auto queue = weak_queue.lock()) { + queue->DoOneJob(); + } + }); + } return true; } @@ -82,6 +86,14 @@ void PipelineCompileQueue::DoOneJob() { } } +void PipelineCompileQueue::FlushPendingJobs() { + PostJob([weak_queue = weak_from_this()]() { + if (auto queue = weak_queue.lock()) { + queue->FinishAllJobs(); + } + }); +} + void PipelineCompileQueue::FinishAllJobs() { // This doesn't have to be fast. Just ensures the task queue is flushed when // the compile queue is shutting down with jobs still in it. diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h index 4ccff58af9eaa..3155731241217 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h @@ -49,6 +49,7 @@ class PipelineCompileQueue explicit PipelineCompileQueue( std::shared_ptr worker_task_runner); PipelineCompileQueue() = default; + PipelineCompileQueue(bool wait_until_rendering); virtual ~PipelineCompileQueue(); @@ -87,6 +88,13 @@ class PipelineCompileQueue /// virtual void PostJob(const fml::closure& job) = 0; + bool WaitUntilRendering() { return wait_until_rendering_; }; + + void FlushPendingJobs(); + + protected: + bool wait_until_rendering_ = false; + private: Mutex pending_jobs_mutex_; size_t priorities_elevated_ = {}; diff --git a/engine/src/flutter/shell/common/shell.cc b/engine/src/flutter/shell/common/shell.cc index 4e920cbbb5115..6b71d7ce93cf7 100644 --- a/engine/src/flutter/shell/common/shell.cc +++ b/engine/src/flutter/shell/common/shell.cc @@ -1006,6 +1006,20 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { } }; + auto flush_task = [io_manager = io_manager_->GetWeakPtr()] { + if (io_manager && io_manager->GetImpellerContext()) { + auto impeller_context = io_manager->GetImpellerContext(); + if (impeller_context && impeller_context->GetPipelineLibrary()) { + auto pipeline_compile_queue = + impeller_context->GetPipelineLibrary()->GetPipelineCompileQueue(); + if (pipeline_compile_queue && + pipeline_compile_queue->WaitUntilRendering()) { + pipeline_compile_queue->FlushPendingJobs(); + } + } + } + }; + // Threading: Capture platform view by raw pointer and not the weak pointer. // We are going to use the pointer on the IO thread which is not safe with a // weak pointer. However, we are preventing the platform view from being @@ -1017,7 +1031,7 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { auto io_task = [io_manager = io_manager_->GetWeakPtr(), platform_view, ui_task_runner = task_runners_.GetUITaskRunner(), ui_task, raster_task_runner = task_runners_.GetRasterTaskRunner(), - raster_task, should_post_raster_task, &latch] { + raster_task, should_post_raster_task, &latch, flush_task] { if (io_manager && !io_manager->GetResourceContext()) { sk_sp resource_context = platform_view->CreateResourceContext(); @@ -1032,6 +1046,10 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { if (should_post_raster_task) { fml::TaskRunner::RunNowOrPostTask(raster_task_runner, raster_task); } + + // Step 3: Tell the IO thread that it could be start to link program. + fml::TaskRunner::RunNowOrPostTask(raster_task_runner, flush_task); + latch.Signal(); }; From fc6e3a6ddbfb3c7ac4202e8781a2c981945f56f2 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Thu, 30 Apr 2026 01:43:00 +0800 Subject: [PATCH 07/24] Fix codereview issues --- .../renderer/pipeline_compile_queue.cc | 1 + .../impeller/renderer/pipeline_compile_queue.h | 2 +- engine/src/flutter/shell/common/shell.cc | 18 +++++++++--------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc index 639b5da2d0346..b04a66228346c 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc @@ -87,6 +87,7 @@ void PipelineCompileQueue::DoOneJob() { } void PipelineCompileQueue::FlushPendingJobs() { + wait_until_rendering_ = false; PostJob([weak_queue = weak_from_this()]() { if (auto queue = weak_queue.lock()) { queue->FinishAllJobs(); diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h index 3155731241217..0f6bdb55af03e 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h @@ -93,7 +93,7 @@ class PipelineCompileQueue void FlushPendingJobs(); protected: - bool wait_until_rendering_ = false; + std::atomic wait_until_rendering_ = false; private: Mutex pending_jobs_mutex_; diff --git a/engine/src/flutter/shell/common/shell.cc b/engine/src/flutter/shell/common/shell.cc index 6b71d7ce93cf7..8ee4a8930aa42 100644 --- a/engine/src/flutter/shell/common/shell.cc +++ b/engine/src/flutter/shell/common/shell.cc @@ -1007,14 +1007,14 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { }; auto flush_task = [io_manager = io_manager_->GetWeakPtr()] { - if (io_manager && io_manager->GetImpellerContext()) { - auto impeller_context = io_manager->GetImpellerContext(); - if (impeller_context && impeller_context->GetPipelineLibrary()) { - auto pipeline_compile_queue = - impeller_context->GetPipelineLibrary()->GetPipelineCompileQueue(); - if (pipeline_compile_queue && - pipeline_compile_queue->WaitUntilRendering()) { - pipeline_compile_queue->FlushPendingJobs(); + if (io_manager) { + if (auto impeller_context = io_manager->GetImpellerContext()) { + if (auto pipeline_library = impeller_context->GetPipelineLibrary()) { + if (auto pipeline_compile_queue = + pipeline_library->GetPipelineCompileQueue()) + if (pipeline_compile_queue->WaitUntilRendering()) { + pipeline_compile_queue->FlushPendingJobs(); + } } } } @@ -1047,7 +1047,7 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { fml::TaskRunner::RunNowOrPostTask(raster_task_runner, raster_task); } - // Step 3: Tell the IO thread that it could be start to link program. + // Step 3: Flush pending pipeline compilation jobs. fml::TaskRunner::RunNowOrPostTask(raster_task_runner, flush_task); latch.Signal(); From 15f3251e732b1fb0d797175040ccdaf4dfe88f4b Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Thu, 14 May 2026 17:43:48 +0800 Subject: [PATCH 08/24] Execute the compile job one by one; do not push all the tasks into the queue at once. --- .../impeller/renderer/pipeline_compile_queue.cc | 15 ++++++++++++++- .../impeller/renderer/pipeline_compile_queue.h | 4 ++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc index b04a66228346c..ca06bc1742b4d 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc @@ -88,13 +88,26 @@ void PipelineCompileQueue::DoOneJob() { void PipelineCompileQueue::FlushPendingJobs() { wait_until_rendering_ = false; + ProcessJobsSequentially(); +} + +void PipelineCompileQueue::ProcessJobsSequentially() { PostJob([weak_queue = weak_from_this()]() { if (auto queue = weak_queue.lock()) { - queue->FinishAllJobs(); + queue->DoOneJob(); + if (!queue->HasPendingJobs()) { + return; + } + queue->ProcessJobsSequentially(); } }); } +bool PipelineCompileQueue::HasPendingJobs() { + Lock lock(pending_jobs_mutex_); + return !pending_jobs_.empty(); +} + void PipelineCompileQueue::FinishAllJobs() { // This doesn't have to be fast. Just ensures the task queue is flushed when // the compile queue is shutting down with jobs still in it. diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h index 0f6bdb55af03e..41b365f00b701 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h @@ -112,6 +112,10 @@ class PipelineCompileQueue void DoOneJob(); void FinishAllJobs(); + + void ProcessJobsSequentially(); + + bool HasPendingJobs(); }; } // namespace impeller From 5434e6f2c59b3099e3396fffb8a93a4455010d76 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Fri, 15 May 2026 18:38:36 +0800 Subject: [PATCH 09/24] When executing an OpenGL job, you must wait for the previous job to finish before starting the next one. --- .../gles/pipeline_compile_queue_gles.cc | 42 ++++++++++++- .../gles/pipeline_compile_queue_gles.h | 17 +++++ .../vulkan/pipeline_compile_queue_vulkan.cc | 26 ++++++++ .../vulkan/pipeline_compile_queue_vulkan.h | 12 ++++ .../renderer/pipeline_compile_queue.cc | 63 +++---------------- .../renderer/pipeline_compile_queue.h | 39 ++++++------ engine/src/flutter/shell/common/shell.cc | 19 +----- 7 files changed, 126 insertions(+), 92 deletions(-) diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc index e3e3e1debadb4..91dd64530f909 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc @@ -17,11 +17,35 @@ std::shared_ptr PipelineCompileQueueGLES::Create( PipelineCompileQueueGLES::PipelineCompileQueueGLES( fml::RefPtr worker_task_runner) - : PipelineCompileQueue(true), - worker_task_runner_(std::move(worker_task_runner)) {} + : worker_task_runner_(std::move(worker_task_runner)) {} PipelineCompileQueueGLES::~PipelineCompileQueueGLES() {} +bool PipelineCompileQueueGLES::PostJobForDescriptor( + const PipelineDescriptor& desc, + const fml::closure& job) { + if (!job) { + return false; + } + + if (!AddJob(desc, job)) { + // This bit is being extremely conservative. If insertion did not take + // place, someone gave the compile queue a job for the same description. + // This is highly unusual but technically not impossible. Just run the job + // eagerly. + FML_LOG(ERROR) << "Got multiple compile jobs for the same descriptor. " + "Running eagerly."; + PostJob(job); + return true; + } + + bool expected = false; + if (is_processing_.compare_exchange_strong(expected, true)) { + ProcessJobsSequentially(); + } + return true; +} + void PipelineCompileQueueGLES::PostJob(const fml::closure& job) { if (!job) { return; @@ -30,4 +54,18 @@ void PipelineCompileQueueGLES::PostJob(const fml::closure& job) { worker_task_runner_->PostTask(job); } +void PipelineCompileQueueGLES::ProcessJobsSequentially() { + PostJob([weak_queue = weak_from_this()]() { + if (auto queue = std::static_pointer_cast( + weak_queue.lock())) { + queue->DoOneJob(); + if (!queue->HasPendingJobs()) { + queue->is_processing_ = false; + return; + } + queue->ProcessJobsSequentially(); + } + }); +} + } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h index 31fc6a528e48e..64ee33e606cc2 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h @@ -5,6 +5,8 @@ #ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_PIPELINE_COMPILE_QUEUE_GLES_H_ #define FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_PIPELINE_COMPILE_QUEUE_GLES_H_ +#include + #include "flutter/fml/closure.h" #include "flutter/fml/task_runner.h" #include "impeller/renderer/pipeline_compile_queue.h" @@ -25,6 +27,18 @@ class PipelineCompileQueueGLES : public PipelineCompileQueue { PipelineCompileQueueGLES& operator=(const PipelineCompileQueueGLES&) = delete; + //---------------------------------------------------------------------------- + /// @brief Post a compile job for the specified descriptor. + /// + /// @param[in] desc The description + /// @param[in] job The job + /// + /// @return If the job was successfully posted to the parallel task + /// runners. + /// + bool PostJobForDescriptor(const PipelineDescriptor& desc, + const fml::closure& job) override; + //---------------------------------------------------------------------------- /// @brief Post a job to the worker task runner. /// @@ -32,8 +46,11 @@ class PipelineCompileQueueGLES : public PipelineCompileQueue { /// void PostJob(const fml::closure& job) override; + void ProcessJobsSequentially(); + private: fml::RefPtr worker_task_runner_; + std::atomic is_processing_{false}; }; } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.cc index 3ecaabb16bdcc..49b98b08cbcfe 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.cc @@ -22,6 +22,32 @@ PipelineCompileQueueVulkan::PipelineCompileQueueVulkan( PipelineCompileQueueVulkan::~PipelineCompileQueueVulkan() {} +bool PipelineCompileQueueVulkan::PostJobForDescriptor( + const PipelineDescriptor& desc, + const fml::closure& job) { + if (!job) { + return false; + } + + if (!AddJob(desc, job)) { + // This bit is being extremely conservative. If insertion did not take + // place, someone gave the compile queue a job for the same description. + // This is highly unusual but technically not impossible. Just run the job + // eagerly. + FML_LOG(ERROR) << "Got multiple compile jobs for the same descriptor. " + "Running eagerly."; + PostJob(job); + return true; + } + + PostJob([weak_queue = weak_from_this()]() { + if (auto queue = weak_queue.lock()) { + queue->DoOneJob(); + } + }); + return true; +} + void PipelineCompileQueueVulkan::PostJob(const fml::closure& job) { if (!job) { return; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h index 9f6474c9f958c..e97f862798631 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h @@ -26,6 +26,18 @@ class PipelineCompileQueueVulkan : public PipelineCompileQueue { PipelineCompileQueueVulkan& operator=(const PipelineCompileQueueVulkan&) = delete; + //---------------------------------------------------------------------------- + /// @brief Post a compile job for the specified descriptor. + /// + /// @param[in] desc The description + /// @param[in] job The job + /// + /// @return If the job was successfully posted to the parallel task + /// runners. + /// + bool PostJobForDescriptor(const PipelineDescriptor& desc, + const fml::closure& job) override; + //---------------------------------------------------------------------------- /// @brief Post a job to the worker task runner. /// diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc index ca06bc1742b4d..45d589afee8fa 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc @@ -9,43 +9,20 @@ namespace impeller { -PipelineCompileQueue::PipelineCompileQueue(bool wait_until_rendering) - : wait_until_rendering_(wait_until_rendering) {} - PipelineCompileQueue::~PipelineCompileQueue() { FinishAllJobs(); } -bool PipelineCompileQueue::PostJobForDescriptor(const PipelineDescriptor& desc, - const fml::closure& job) { - if (!job) { - return false; - } +bool PipelineCompileQueue::AddJob(const PipelineDescriptor& desc, + const fml::closure& job) { + Lock lock(pending_jobs_mutex_); + auto insertion_result = pending_jobs_.insert(std::make_pair(desc, job)); + return insertion_result.second; +} - { - Lock lock(pending_jobs_mutex_); - auto insertion_result = pending_jobs_.insert(std::make_pair(desc, job)); - if (!insertion_result.second) { - // This bit is being extremely conservative. If insertion did not take - // place, someone gave the compile queue a job for the same description. - // This is highly unusual but technically not impossible. Just run the job - // eagerly. - FML_LOG(ERROR) << "Got multiple compile jobs for the same descriptor. " - "Running eagerly."; - // Don't invoke the job here has there are we have currently acquired a - // mutex. - PostJob(job); - return true; - } - } - if (!WaitUntilRendering()) { - PostJob([weak_queue = weak_from_this()]() { - if (auto queue = weak_queue.lock()) { - queue->DoOneJob(); - } - }); - } - return true; +bool PipelineCompileQueue::HasPendingJobs() { + Lock lock(pending_jobs_mutex_); + return !pending_jobs_.empty(); } fml::closure PipelineCompileQueue::TakeNextJob() { @@ -86,28 +63,6 @@ void PipelineCompileQueue::DoOneJob() { } } -void PipelineCompileQueue::FlushPendingJobs() { - wait_until_rendering_ = false; - ProcessJobsSequentially(); -} - -void PipelineCompileQueue::ProcessJobsSequentially() { - PostJob([weak_queue = weak_from_this()]() { - if (auto queue = weak_queue.lock()) { - queue->DoOneJob(); - if (!queue->HasPendingJobs()) { - return; - } - queue->ProcessJobsSequentially(); - } - }); -} - -bool PipelineCompileQueue::HasPendingJobs() { - Lock lock(pending_jobs_mutex_); - return !pending_jobs_.empty(); -} - void PipelineCompileQueue::FinishAllJobs() { // This doesn't have to be fast. Just ensures the task queue is flushed when // the compile queue is shutting down with jobs still in it. diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h index 41b365f00b701..d907001cbef35 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h @@ -49,7 +49,6 @@ class PipelineCompileQueue explicit PipelineCompileQueue( std::shared_ptr worker_task_runner); PipelineCompileQueue() = default; - PipelineCompileQueue(bool wait_until_rendering); virtual ~PipelineCompileQueue(); @@ -66,8 +65,8 @@ class PipelineCompileQueue /// @return If the job was successfully posted to the parallel task /// runners. /// - bool PostJobForDescriptor(const PipelineDescriptor& desc, - const fml::closure& job); + virtual bool PostJobForDescriptor(const PipelineDescriptor& desc, + const fml::closure& job) = 0; //---------------------------------------------------------------------------- /// @brief If the task has not yet been done, perform it eagerly on the @@ -88,34 +87,38 @@ class PipelineCompileQueue /// virtual void PostJob(const fml::closure& job) = 0; - bool WaitUntilRendering() { return wait_until_rendering_; }; - - void FlushPendingJobs(); + void DoOneJob(); protected: - std::atomic wait_until_rendering_ = false; + //---------------------------------------------------------------------------- + /// @brief Add a job to the pending jobs map. + /// + /// @param[in] desc The pipeline descriptor. + /// @param[in] job The job to add. + /// + /// @return true if the job was successfully inserted, false if a job + /// for the same descriptor already exists. + /// + bool AddJob(const PipelineDescriptor& desc, const fml::closure& job); + + //---------------------------------------------------------------------------- + /// @brief Check if there are any pending jobs. + /// + /// @return true if there are pending jobs, false otherwise. + /// + bool HasPendingJobs(); private: Mutex pending_jobs_mutex_; - size_t priorities_elevated_ = {}; - std::unordered_map, ComparableEqual> pending_jobs_ IPLR_GUARDED_BY(pending_jobs_mutex_); - + size_t priorities_elevated_ = {}; fml::closure TakeJob(const PipelineDescriptor& desc); - fml::closure TakeNextJob(); - - void DoOneJob(); - void FinishAllJobs(); - - void ProcessJobsSequentially(); - - bool HasPendingJobs(); }; } // namespace impeller diff --git a/engine/src/flutter/shell/common/shell.cc b/engine/src/flutter/shell/common/shell.cc index 8ee4a8930aa42..92ee2c8707094 100644 --- a/engine/src/flutter/shell/common/shell.cc +++ b/engine/src/flutter/shell/common/shell.cc @@ -1006,20 +1006,6 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { } }; - auto flush_task = [io_manager = io_manager_->GetWeakPtr()] { - if (io_manager) { - if (auto impeller_context = io_manager->GetImpellerContext()) { - if (auto pipeline_library = impeller_context->GetPipelineLibrary()) { - if (auto pipeline_compile_queue = - pipeline_library->GetPipelineCompileQueue()) - if (pipeline_compile_queue->WaitUntilRendering()) { - pipeline_compile_queue->FlushPendingJobs(); - } - } - } - } - }; - // Threading: Capture platform view by raw pointer and not the weak pointer. // We are going to use the pointer on the IO thread which is not safe with a // weak pointer. However, we are preventing the platform view from being @@ -1031,7 +1017,7 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { auto io_task = [io_manager = io_manager_->GetWeakPtr(), platform_view, ui_task_runner = task_runners_.GetUITaskRunner(), ui_task, raster_task_runner = task_runners_.GetRasterTaskRunner(), - raster_task, should_post_raster_task, &latch, flush_task] { + raster_task, should_post_raster_task, &latch] { if (io_manager && !io_manager->GetResourceContext()) { sk_sp resource_context = platform_view->CreateResourceContext(); @@ -1047,9 +1033,6 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { fml::TaskRunner::RunNowOrPostTask(raster_task_runner, raster_task); } - // Step 3: Flush pending pipeline compilation jobs. - fml::TaskRunner::RunNowOrPostTask(raster_task_runner, flush_task); - latch.Signal(); }; From fa3cdc2bb7418a7a23afff87d0169f156c643e3b Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Fri, 15 May 2026 18:42:30 +0800 Subject: [PATCH 10/24] Remove empty line --- engine/src/flutter/shell/common/shell.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/engine/src/flutter/shell/common/shell.cc b/engine/src/flutter/shell/common/shell.cc index 92ee2c8707094..4e920cbbb5115 100644 --- a/engine/src/flutter/shell/common/shell.cc +++ b/engine/src/flutter/shell/common/shell.cc @@ -1032,7 +1032,6 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { if (should_post_raster_task) { fml::TaskRunner::RunNowOrPostTask(raster_task_runner, raster_task); } - latch.Signal(); }; From 751c330b20477636a6313fa304285dc592b60716 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Thu, 21 May 2026 22:29:54 +0800 Subject: [PATCH 11/24] Add a robust mutex-based synchronization pattern --- .../backend/gles/pipeline_compile_queue_gles.cc | 14 +++++++++----- .../backend/gles/pipeline_compile_queue_gles.h | 4 +++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc index 91dd64530f909..c950c5229f763 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc @@ -39,8 +39,9 @@ bool PipelineCompileQueueGLES::PostJobForDescriptor( return true; } - bool expected = false; - if (is_processing_.compare_exchange_strong(expected, true)) { + std::scoped_lock lock(processing_mutex_); + if (!is_processing_) { + is_processing_ = true; ProcessJobsSequentially(); } return true; @@ -59,9 +60,12 @@ void PipelineCompileQueueGLES::ProcessJobsSequentially() { if (auto queue = std::static_pointer_cast( weak_queue.lock())) { queue->DoOneJob(); - if (!queue->HasPendingJobs()) { - queue->is_processing_ = false; - return; + { + std::scoped_lock lock(queue->processing_mutex_); + if (!queue->HasPendingJobs()) { + queue->is_processing_ = false; + return; + } } queue->ProcessJobsSequentially(); } diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h index 64ee33e606cc2..cf383dc8d9a20 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h @@ -6,6 +6,7 @@ #define FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_PIPELINE_COMPILE_QUEUE_GLES_H_ #include +#include #include "flutter/fml/closure.h" #include "flutter/fml/task_runner.h" @@ -50,7 +51,8 @@ class PipelineCompileQueueGLES : public PipelineCompileQueue { private: fml::RefPtr worker_task_runner_; - std::atomic is_processing_{false}; + std::mutex processing_mutex_; + std::atomic is_processing_ = false; }; } // namespace impeller From 6a1d9c7a798fbaa0d6c90cbc3f3885ff9c526f56 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Fri, 22 May 2026 02:21:45 +0800 Subject: [PATCH 12/24] Fix code review issues 1.Change is_processing type to bool. 2.Change DoOneJob,PostJob,HasPendingJobs to protect. --- .../gles/pipeline_compile_queue_gles.h | 2 +- .../vulkan/pipeline_compile_queue_vulkan.cc | 3 ++- .../renderer/pipeline_compile_queue.h | 27 +------------------ 3 files changed, 4 insertions(+), 28 deletions(-) diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h index cf383dc8d9a20..61550ffe180ab 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h @@ -52,7 +52,7 @@ class PipelineCompileQueueGLES : public PipelineCompileQueue { private: fml::RefPtr worker_task_runner_; std::mutex processing_mutex_; - std::atomic is_processing_ = false; + bool is_processing_ = false; }; } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.cc index 49b98b08cbcfe..cb09b6d95e8aa 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.cc @@ -41,7 +41,8 @@ bool PipelineCompileQueueVulkan::PostJobForDescriptor( } PostJob([weak_queue = weak_from_this()]() { - if (auto queue = weak_queue.lock()) { + if (auto queue = std::static_pointer_cast( + weak_queue.lock())) { queue->DoOneJob(); } }); diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h index d907001cbef35..e1758d5336dbc 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h @@ -77,35 +77,10 @@ class PipelineCompileQueue /// void PerformJobEagerly(const PipelineDescriptor& desc); - //---------------------------------------------------------------------------- - /// @brief Post a job to the worker task runner. - /// - /// @param[in] job The job - /// - /// @return If the job was successfully posted to the parallel task - /// runners. - /// + protected: virtual void PostJob(const fml::closure& job) = 0; - void DoOneJob(); - - protected: - //---------------------------------------------------------------------------- - /// @brief Add a job to the pending jobs map. - /// - /// @param[in] desc The pipeline descriptor. - /// @param[in] job The job to add. - /// - /// @return true if the job was successfully inserted, false if a job - /// for the same descriptor already exists. - /// bool AddJob(const PipelineDescriptor& desc, const fml::closure& job); - - //---------------------------------------------------------------------------- - /// @brief Check if there are any pending jobs. - /// - /// @return true if there are pending jobs, false otherwise. - /// bool HasPendingJobs(); private: From 2a8d8121d0598b3f3945d07c7726b9e36528c3a9 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Fri, 22 May 2026 03:55:31 +0800 Subject: [PATCH 13/24] Fix code review issues 1.Extrac a new method OnJobAdded 2.Replace std::mutex with Mutex 3.Add null check for io task runner 4.Change mthod ProcessJobsSequentially to private --- .../gles/pipeline_compile_queue_gles.cc | 43 ++++++++----------- .../gles/pipeline_compile_queue_gles.h | 26 ++++------- .../vulkan/pipeline_compile_queue_vulkan.cc | 20 +-------- .../vulkan/pipeline_compile_queue_vulkan.h | 16 +++---- .../renderer/pipeline_compile_queue.cc | 21 +++++++++ .../renderer/pipeline_compile_queue.h | 16 ++++++- 6 files changed, 69 insertions(+), 73 deletions(-) diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc index c950c5229f763..051380a84c67e 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc @@ -19,40 +19,35 @@ PipelineCompileQueueGLES::PipelineCompileQueueGLES( fml::RefPtr worker_task_runner) : worker_task_runner_(std::move(worker_task_runner)) {} -PipelineCompileQueueGLES::~PipelineCompileQueueGLES() {} +// The base class destructor calls FinishAllJobs() which drains any remaining +// pending jobs on the current thread. If a ProcessJobsSequentially task is +// still in flight on the IO thread, the weak_from_this() capture in the +// posted lambda will safely return nullptr and the task will be a no-op. +// Any jobs already taken from the queue by the IO thread will still execute, +// but this is safe because they capture weak references to the pipeline +// library (not to this compile queue). +PipelineCompileQueueGLES::~PipelineCompileQueueGLES() = default; -bool PipelineCompileQueueGLES::PostJobForDescriptor( - const PipelineDescriptor& desc, - const fml::closure& job) { - if (!job) { - return false; - } - - if (!AddJob(desc, job)) { - // This bit is being extremely conservative. If insertion did not take - // place, someone gave the compile queue a job for the same description. - // This is highly unusual but technically not impossible. Just run the job - // eagerly. - FML_LOG(ERROR) << "Got multiple compile jobs for the same descriptor. " - "Running eagerly."; - PostJob(job); - return true; - } - - std::scoped_lock lock(processing_mutex_); +void PipelineCompileQueueGLES::OnJobAdded() { + Lock lock(processing_mutex_); if (!is_processing_) { is_processing_ = true; ProcessJobsSequentially(); } - return true; } void PipelineCompileQueueGLES::PostJob(const fml::closure& job) { if (!job) { return; } - - worker_task_runner_->PostTask(job); + if (worker_task_runner_) { + worker_task_runner_->PostTask(job); + } else { + // No task runner available, execute synchronously on the current thread. + // This is safe in Playground/test environments where the current thread + // is the GL thread. + job(); + } } void PipelineCompileQueueGLES::ProcessJobsSequentially() { @@ -61,7 +56,7 @@ void PipelineCompileQueueGLES::ProcessJobsSequentially() { weak_queue.lock())) { queue->DoOneJob(); { - std::scoped_lock lock(queue->processing_mutex_); + Lock lock(queue->processing_mutex_); if (!queue->HasPendingJobs()) { queue->is_processing_ = false; return; diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h index 61550ffe180ab..e17092e1d71c6 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h @@ -5,11 +5,9 @@ #ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_PIPELINE_COMPILE_QUEUE_GLES_H_ #define FLUTTER_IMPELLER_RENDERER_BACKEND_GLES_PIPELINE_COMPILE_QUEUE_GLES_H_ -#include -#include - #include "flutter/fml/closure.h" #include "flutter/fml/task_runner.h" +#include "impeller/base/thread.h" #include "impeller/renderer/pipeline_compile_queue.h" namespace impeller { @@ -29,30 +27,24 @@ class PipelineCompileQueueGLES : public PipelineCompileQueue { PipelineCompileQueueGLES& operator=(const PipelineCompileQueueGLES&) = delete; //---------------------------------------------------------------------------- - /// @brief Post a compile job for the specified descriptor. + /// @brief Post a job to the worker task runner. /// - /// @param[in] desc The description /// @param[in] job The job /// - /// @return If the job was successfully posted to the parallel task - /// runners. - /// - bool PostJobForDescriptor(const PipelineDescriptor& desc, - const fml::closure& job) override; + void PostJob(const fml::closure& job) override; //---------------------------------------------------------------------------- - /// @brief Post a job to the worker task runner. + /// @brief Called after a job has been added to the queue. Implements + /// the sequential scheduling strategy for GLES. /// - /// @param[in] job The job - /// - void PostJob(const fml::closure& job) override; + void OnJobAdded() override; + private: void ProcessJobsSequentially(); - private: fml::RefPtr worker_task_runner_; - std::mutex processing_mutex_; - bool is_processing_ = false; + Mutex processing_mutex_; + bool is_processing_ IPLR_GUARDED_BY(processing_mutex_) = false; }; } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.cc index cb09b6d95e8aa..19c6a38510987 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.cc @@ -22,31 +22,13 @@ PipelineCompileQueueVulkan::PipelineCompileQueueVulkan( PipelineCompileQueueVulkan::~PipelineCompileQueueVulkan() {} -bool PipelineCompileQueueVulkan::PostJobForDescriptor( - const PipelineDescriptor& desc, - const fml::closure& job) { - if (!job) { - return false; - } - - if (!AddJob(desc, job)) { - // This bit is being extremely conservative. If insertion did not take - // place, someone gave the compile queue a job for the same description. - // This is highly unusual but technically not impossible. Just run the job - // eagerly. - FML_LOG(ERROR) << "Got multiple compile jobs for the same descriptor. " - "Running eagerly."; - PostJob(job); - return true; - } - +void PipelineCompileQueueVulkan::OnJobAdded() { PostJob([weak_queue = weak_from_this()]() { if (auto queue = std::static_pointer_cast( weak_queue.lock())) { queue->DoOneJob(); } }); - return true; } void PipelineCompileQueueVulkan::PostJob(const fml::closure& job) { diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h index e97f862798631..45eadf4a81932 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h @@ -27,23 +27,17 @@ class PipelineCompileQueueVulkan : public PipelineCompileQueue { delete; //---------------------------------------------------------------------------- - /// @brief Post a compile job for the specified descriptor. + /// @brief Post a job to the worker task runner. /// - /// @param[in] desc The description /// @param[in] job The job /// - /// @return If the job was successfully posted to the parallel task - /// runners. - /// - bool PostJobForDescriptor(const PipelineDescriptor& desc, - const fml::closure& job) override; + void PostJob(const fml::closure& job) override; //---------------------------------------------------------------------------- - /// @brief Post a job to the worker task runner. + /// @brief Called after a job has been added to the queue. Implements + /// the parallel scheduling strategy for Vulkan. /// - /// @param[in] job The job - /// - void PostJob(const fml::closure& job) override; + void OnJobAdded() override; private: std::shared_ptr worker_task_runner_; diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc index 45d589afee8fa..ef14ed515a28a 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc @@ -13,6 +13,27 @@ PipelineCompileQueue::~PipelineCompileQueue() { FinishAllJobs(); } +bool PipelineCompileQueue::PostJobForDescriptor(const PipelineDescriptor& desc, + const fml::closure& job) { + if (!job) { + return false; + } + + if (!AddJob(desc, job)) { + // This bit is being extremely conservative. If insertion did not take + // place, someone gave the compile queue a job for the same description. + // This is highly unusual but technically not impossible. Just run the job + // eagerly. + FML_LOG(ERROR) << "Got multiple compile jobs for the same descriptor. " + "Running eagerly."; + PostJob(job); + return true; + } + + OnJobAdded(); + return true; +} + bool PipelineCompileQueue::AddJob(const PipelineDescriptor& desc, const fml::closure& job) { Lock lock(pending_jobs_mutex_); diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h index e1758d5336dbc..a5cc3b5215cb4 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h @@ -65,8 +65,8 @@ class PipelineCompileQueue /// @return If the job was successfully posted to the parallel task /// runners. /// - virtual bool PostJobForDescriptor(const PipelineDescriptor& desc, - const fml::closure& job) = 0; + bool PostJobForDescriptor(const PipelineDescriptor& desc, + const fml::closure& job); //---------------------------------------------------------------------------- /// @brief If the task has not yet been done, perform it eagerly on the @@ -79,6 +79,18 @@ class PipelineCompileQueue protected: virtual void PostJob(const fml::closure& job) = 0; + + //---------------------------------------------------------------------------- + /// @brief Called by PostJobForDescriptor after a job has been + /// successfully added to the queue. Subclasses must implement + /// this to define their scheduling strategy. + /// + /// The default implementation for duplicate descriptors is to + /// run the job eagerly. Subclasses can override this behavior + /// by checking for duplicates before calling the base class. + /// + virtual void OnJobAdded() = 0; + void DoOneJob(); bool AddJob(const PipelineDescriptor& desc, const fml::closure& job); bool HasPendingJobs(); From 44653677b916861a2689fc71533fdef77c7351ba Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Sat, 23 May 2026 02:45:12 +0800 Subject: [PATCH 14/24] Fix code review issues 1.Remove not used code. 2.Add comments for io task runner. 3.Remove io task runner from pipeline library gles. --- .../renderer/backend/gles/pipeline_compile_queue_gles.cc | 7 ------- .../renderer/backend/gles/pipeline_library_gles.cc | 3 +-- .../impeller/renderer/backend/gles/pipeline_library_gles.h | 1 - engine/src/flutter/shell/platform/embedder/embedder.cc | 2 +- 4 files changed, 2 insertions(+), 11 deletions(-) diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc index 051380a84c67e..8a4009384ae94 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc @@ -19,13 +19,6 @@ PipelineCompileQueueGLES::PipelineCompileQueueGLES( fml::RefPtr worker_task_runner) : worker_task_runner_(std::move(worker_task_runner)) {} -// The base class destructor calls FinishAllJobs() which drains any remaining -// pending jobs on the current thread. If a ProcessJobsSequentially task is -// still in flight on the IO thread, the weak_from_this() capture in the -// posted lambda will safely return nullptr and the task will be a no-op. -// Any jobs already taken from the queue by the IO thread will still execute, -// but this is safe because they capture weak references to the pipeline -// library (not to this compile queue). PipelineCompileQueueGLES::~PipelineCompileQueueGLES() = default; void PipelineCompileQueueGLES::OnJobAdded() { diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc index a3486f4550e63..517a267ff6438 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc @@ -20,8 +20,7 @@ PipelineLibraryGLES::PipelineLibraryGLES( std::shared_ptr reactor, fml::RefPtr io_task_runner) : reactor_(std::move(reactor)), - io_task_runner_(std::move(io_task_runner)), - compile_queue_(PipelineCompileQueueGLES::Create(io_task_runner_)) {} + compile_queue_(PipelineCompileQueueGLES::Create(std::move(io_task_runner))) {} static std::string GetShaderInfoLog(const ProcTableGLES& gl, GLuint shader) { GLint log_length = 0; diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h index fa36a88294144..765cff60c7152 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.h @@ -93,7 +93,6 @@ class PipelineLibraryGLES final PipelineMap pipelines_; Mutex programs_mutex_; ProgramMap programs_ IPLR_GUARDED_BY(programs_mutex_); - fml::RefPtr io_task_runner_; std::shared_ptr compile_queue_; explicit PipelineLibraryGLES(std::shared_ptr reactor, diff --git a/engine/src/flutter/shell/platform/embedder/embedder.cc b/engine/src/flutter/shell/platform/embedder/embedder.cc index 88ed4161bb89e..41970ce55c6d6 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder.cc @@ -493,7 +493,7 @@ InferOpenGLPlatformViewCreationCallback( std::make_unique( gl_dispatch_table, fbo_reset_after_present, view_embedder, // embedder_surface - shell.GetTaskRunners().GetIOTaskRunner()), + shell.GetTaskRunners().GetIOTaskRunner()), // io_task_runner platform_dispatch_table, // embedder platform dispatch table view_embedder // external view embedder ); From c026698327b3102c13df0677f584639d4d0b69af Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Mon, 25 May 2026 19:27:27 +0800 Subject: [PATCH 15/24] Add check when creating PipelineCompileQueueGLES --- .../backend/gles/pipeline_compile_queue_gles.cc | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc index 8a4009384ae94..0b9515550ba03 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc @@ -11,6 +11,9 @@ namespace impeller { std::shared_ptr PipelineCompileQueueGLES::Create( fml::RefPtr worker_task_runner) { + if (!worker_task_runner) { + return nullptr; + } return std::shared_ptr( new PipelineCompileQueueGLES(std::move(worker_task_runner))); } @@ -33,14 +36,8 @@ void PipelineCompileQueueGLES::PostJob(const fml::closure& job) { if (!job) { return; } - if (worker_task_runner_) { - worker_task_runner_->PostTask(job); - } else { - // No task runner available, execute synchronously on the current thread. - // This is safe in Playground/test environments where the current thread - // is the GL thread. - job(); - } + + worker_task_runner_->PostTask(job); } void PipelineCompileQueueGLES::ProcessJobsSequentially() { From fa86d603c1a73237d3c8e32d500e9495581ef286 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Tue, 26 May 2026 00:29:25 +0800 Subject: [PATCH 16/24] Fix format issue --- .../impeller/renderer/backend/gles/pipeline_library_gles.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc index 517a267ff6438..806ad5fb1bd14 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc @@ -20,7 +20,8 @@ PipelineLibraryGLES::PipelineLibraryGLES( std::shared_ptr reactor, fml::RefPtr io_task_runner) : reactor_(std::move(reactor)), - compile_queue_(PipelineCompileQueueGLES::Create(std::move(io_task_runner))) {} + compile_queue_( + PipelineCompileQueueGLES::Create(std::move(io_task_runner))) {} static std::string GetShaderInfoLog(const ProcTableGLES& gl, GLuint shader) { GLint log_length = 0; From 64d533d61d78bc081727b714238e039584abfd6b Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Sat, 30 May 2026 04:33:33 +0800 Subject: [PATCH 17/24] Add UT test --- engine/src/flutter/impeller/renderer/BUILD.gn | 1 + .../impeller/renderer/backend/gles/BUILD.gn | 2 + .../backend/gles/pipeline_library_gles.cc | 2 +- .../pipeline_compile_queue_gles_unittests.cc | 151 +++++++++++++++ .../impeller/renderer/backend/vulkan/BUILD.gn | 1 + .../backend/vulkan/pipeline_library_vk.cc | 2 +- ...pipeline_compile_queue_vulkan_unittests.cc | 181 ++++++++++++++++++ .../renderer/pipeline_compile_queue.cc | 4 +- .../pipeline_compile_queue_unittests.cc | 96 ++++++++++ 9 files changed, 436 insertions(+), 4 deletions(-) create mode 100644 engine/src/flutter/impeller/renderer/backend/gles/test/pipeline_compile_queue_gles_unittests.cc create mode 100644 engine/src/flutter/impeller/renderer/backend/vulkan/test/pipeline_compile_queue_vulkan_unittests.cc create mode 100644 engine/src/flutter/impeller/renderer/pipeline_compile_queue_unittests.cc diff --git a/engine/src/flutter/impeller/renderer/BUILD.gn b/engine/src/flutter/impeller/renderer/BUILD.gn index 0340164b956f2..861f2b5f5435f 100644 --- a/engine/src/flutter/impeller/renderer/BUILD.gn +++ b/engine/src/flutter/impeller/renderer/BUILD.gn @@ -104,6 +104,7 @@ template("renderer_unittests_component") { "blit_pass_unittests.cc", "capabilities_unittests.cc", "device_buffer_unittests.cc", + "pipeline_compile_queue_unittests.cc", "pipeline_descriptor_unittests.cc", "pipeline_library_unittests.cc", "pool_unittests.cc", diff --git a/engine/src/flutter/impeller/renderer/backend/gles/BUILD.gn b/engine/src/flutter/impeller/renderer/backend/gles/BUILD.gn index e91c75671d3fb..93bad0d441972 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/BUILD.gn +++ b/engine/src/flutter/impeller/renderer/backend/gles/BUILD.gn @@ -24,6 +24,7 @@ impeller_component("gles_unittests") { "test/mock_gles.cc", "test/mock_gles.h", "test/mock_gles_unittests.cc", + "test/pipeline_compile_queue_gles_unittests.cc", "test/pipeline_library_gles_unittests.cc", "test/proc_table_gles_unittests.cc", "test/reactor_unittests.cc", @@ -34,6 +35,7 @@ impeller_component("gles_unittests") { ] deps = [ ":gles", + "//flutter/fml", "//flutter/impeller/playground:playground_test", "//flutter/testing:testing_lib", ] diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc index 806ad5fb1bd14..3cad5620e143c 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc @@ -322,7 +322,7 @@ PipelineFuture PipelineLibraryGLES::GetPipeline( FML_CHECK(result); }; - if (async) { + if (async && compile_queue_) { compile_queue_->PostJobForDescriptor(descriptor, std::move(generation_task)); } else { diff --git a/engine/src/flutter/impeller/renderer/backend/gles/test/pipeline_compile_queue_gles_unittests.cc b/engine/src/flutter/impeller/renderer/backend/gles/test/pipeline_compile_queue_gles_unittests.cc new file mode 100644 index 0000000000000..13277f2100435 --- /dev/null +++ b/engine/src/flutter/impeller/renderer/backend/gles/test/pipeline_compile_queue_gles_unittests.cc @@ -0,0 +1,151 @@ +// 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/gles/pipeline_compile_queue_gles.h" + +#include +#include +#include + +#include "flutter/fml/synchronization/count_down_latch.h" +#include "flutter/fml/task_runner.h" +#include "flutter/fml/thread.h" +#include "flutter/testing/testing.h" +#include "impeller/renderer/pipeline_descriptor.h" + +namespace impeller { +namespace testing { + +TEST(PipelineCompileQueueGLESTest, CreateReturnsNullWithNullTaskRunner) { + auto queue = PipelineCompileQueueGLES::Create(nullptr); + EXPECT_EQ(queue, nullptr); +} + +TEST(PipelineCompileQueueGLESTest, CreateSucceedsWithValidTaskRunner) { + fml::Thread thread; + auto queue = PipelineCompileQueueGLES::Create(thread.GetTaskRunner()); + EXPECT_NE(queue, nullptr); + thread.Join(); +} + +TEST(PipelineCompileQueueGLESTest, PostJobDoesNothingWithNullClosure) { + fml::Thread thread; + auto queue = PipelineCompileQueueGLES::Create(thread.GetTaskRunner()); + ASSERT_NE(queue, nullptr); + queue->PostJob(nullptr); + thread.Join(); +} + +TEST(PipelineCompileQueueGLESTest, OnJobAddedProcessesJobsSequentially) { + fml::Thread thread; + auto queue = PipelineCompileQueueGLES::Create(thread.GetTaskRunner()); + ASSERT_NE(queue, nullptr); + + std::atomic execution_order{0}; + std::vector results; + std::mutex results_mutex; + fml::CountDownLatch latch(3); + + PipelineDescriptor desc1; + desc1.SetSampleCount(SampleCount::kCount1); + desc1.SetCullMode(CullMode::kNone); + + PipelineDescriptor desc2; + desc2.SetSampleCount(SampleCount::kCount1); + desc2.SetCullMode(CullMode::kFrontFace); + + PipelineDescriptor desc3; + desc3.SetSampleCount(SampleCount::kCount1); + desc3.SetCullMode(CullMode::kBackFace); + + queue->PostJobForDescriptor(desc1, [&]() { + { + std::lock_guard lock(results_mutex); + results.push_back(1); + } + execution_order = 1; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc2, [&]() { + { + std::lock_guard lock(results_mutex); + results.push_back(2); + } + execution_order = 2; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc3, [&]() { + { + std::lock_guard lock(results_mutex); + results.push_back(3); + } + execution_order = 3; + latch.CountDown(); + }); + + latch.Wait(); + + EXPECT_EQ(results.size(), 3u); + std::sort(results.begin(), results.end()); + EXPECT_EQ(results[0], 1); + EXPECT_EQ(results[1], 2); + EXPECT_EQ(results[2], 3); + thread.Join(); +} + +TEST(PipelineCompileQueueGLESTest, + PostJobForDescriptorWithDuplicateRunsEagerly) { + fml::Thread thread; + auto queue = PipelineCompileQueueGLES::Create(thread.GetTaskRunner()); + ASSERT_NE(queue, nullptr); + + std::atomic first_job_count{0}; + std::atomic second_job_count{0}; + fml::CountDownLatch latch(2); + + PipelineDescriptor desc; + + queue->PostJobForDescriptor(desc, [&]() { + first_job_count++; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc, [&]() { + second_job_count++; + latch.CountDown(); + }); + + latch.Wait(); + + EXPECT_EQ(first_job_count, 1); + EXPECT_EQ(second_job_count, 1); + thread.Join(); +} + +TEST(PipelineCompileQueueGLESTest, IsProcessingResetsAfterAllJobsComplete) { + fml::Thread thread; + auto queue = PipelineCompileQueueGLES::Create(thread.GetTaskRunner()); + ASSERT_NE(queue, nullptr); + + fml::CountDownLatch latch(1); + + queue->PostJobForDescriptor(PipelineDescriptor{}, + [&]() { latch.CountDown(); }); + + latch.Wait(); + + fml::CountDownLatch latch2(1); + queue->PostJobForDescriptor(PipelineDescriptor{}, + [&]() { latch2.CountDown(); }); + + latch2.Wait(); + + SUCCEED(); + thread.Join(); +} + +} // namespace testing +} // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn b/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn index 2cb67f2920249..4e8604f724f4b 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/BUILD.gn @@ -28,6 +28,7 @@ impeller_component("vulkan_unittests") { "test/mock_vulkan.cc", "test/mock_vulkan.h", "test/mock_vulkan_unittests.cc", + "test/pipeline_compile_queue_vulkan_unittests.cc", "test/sampler_library_vk_unittests.cc", "test/swapchain_unittests.cc", ] diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.cc index 50ae6b3efb352..ed4c2d5bc39e2 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_library_vk.cc @@ -27,7 +27,7 @@ PipelineLibraryVK::PipelineLibraryVK( device_holder, std::move(cache_directory))), worker_task_runner_(std::move(worker_task_runner)), - compile_queue_(PipelineCompileQueueVulkan::Create(worker_task_runner)) { + compile_queue_(PipelineCompileQueueVulkan::Create(worker_task_runner_)) { FML_DCHECK(worker_task_runner_); if (!pso_cache_->IsValid() || !worker_task_runner_) { return; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/test/pipeline_compile_queue_vulkan_unittests.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/test/pipeline_compile_queue_vulkan_unittests.cc new file mode 100644 index 0000000000000..6ee373535fd39 --- /dev/null +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/test/pipeline_compile_queue_vulkan_unittests.cc @@ -0,0 +1,181 @@ +// 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/pipeline_compile_queue_vulkan.h" + +#include +#include +#include +#include + +#include "flutter/fml/synchronization/count_down_latch.h" +#include "flutter/fml/task_runner.h" +#include "flutter/testing/testing.h" +#include "impeller/renderer/pipeline_descriptor.h" + +namespace impeller { +namespace testing { + +TEST(PipelineCompileQueueVulkanTest, CreateSucceedsWithValidTaskRunner) { + auto loop = fml::ConcurrentMessageLoop::Create(); + auto queue = PipelineCompileQueueVulkan::Create(loop->GetTaskRunner()); + EXPECT_NE(queue, nullptr); +} + +TEST(PipelineCompileQueueVulkanTest, PostJobDoesNothingWithNullClosure) { + auto loop = fml::ConcurrentMessageLoop::Create(); + auto queue = PipelineCompileQueueVulkan::Create(loop->GetTaskRunner()); + ASSERT_NE(queue, nullptr); + + queue->PostJob(nullptr); +} + +TEST(PipelineCompileQueueVulkanTest, OnJobAddedProcessesJobsInParallel) { + auto loop = fml::ConcurrentMessageLoop::Create(); + auto queue = PipelineCompileQueueVulkan::Create(loop->GetTaskRunner()); + ASSERT_NE(queue, nullptr); + + std::atomic concurrent_jobs{0}; + std::atomic max_concurrent{0}; + fml::CountDownLatch latch(3); + + PipelineDescriptor desc1; + desc1.SetSampleCount(SampleCount::kCount1); + desc1.SetCullMode(CullMode::kNone); + + PipelineDescriptor desc2; + desc2.SetSampleCount(SampleCount::kCount1); + desc2.SetCullMode(CullMode::kFrontFace); + + PipelineDescriptor desc3; + desc3.SetSampleCount(SampleCount::kCount1); + desc3.SetCullMode(CullMode::kBackFace); + + queue->PostJobForDescriptor(desc1, [&]() { + int current = ++concurrent_jobs; + int prev_max = max_concurrent.load(); + while (current > prev_max && + !max_concurrent.compare_exchange_weak(prev_max, current)) { + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + concurrent_jobs--; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc2, [&]() { + int current = ++concurrent_jobs; + int prev_max = max_concurrent.load(); + while (current > prev_max && + !max_concurrent.compare_exchange_weak(prev_max, current)) { + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + concurrent_jobs--; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc3, [&]() { + int current = ++concurrent_jobs; + int prev_max = max_concurrent.load(); + while (current > prev_max && + !max_concurrent.compare_exchange_weak(prev_max, current)) { + } + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + concurrent_jobs--; + latch.CountDown(); + }); + + latch.Wait(); + + EXPECT_GE(max_concurrent.load(), 1); +} + +TEST(PipelineCompileQueueVulkanTest, + PostJobForDescriptorWithDuplicateRunsEagerly) { + auto loop = fml::ConcurrentMessageLoop::Create(); + auto queue = PipelineCompileQueueVulkan::Create(loop->GetTaskRunner()); + ASSERT_NE(queue, nullptr); + + std::atomic first_job_count{0}; + std::atomic second_job_count{0}; + fml::CountDownLatch latch(2); + + PipelineDescriptor desc; + + queue->PostJobForDescriptor(desc, [&]() { + first_job_count++; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc, [&]() { + second_job_count++; + latch.CountDown(); + }); + + latch.Wait(); + + EXPECT_EQ(first_job_count, 1); + EXPECT_EQ(second_job_count, 1); +} + +TEST(PipelineCompileQueueVulkanTest, MultipleJobsCompleteSuccessfully) { + auto loop = fml::ConcurrentMessageLoop::Create(); + auto queue = PipelineCompileQueueVulkan::Create(loop->GetTaskRunner()); + ASSERT_NE(queue, nullptr); + + std::atomic completed_jobs{0}; + fml::CountDownLatch latch(5); + + PipelineDescriptor desc1; + desc1.SetSampleCount(SampleCount::kCount1); + desc1.SetCullMode(CullMode::kNone); + + PipelineDescriptor desc2; + desc2.SetSampleCount(SampleCount::kCount1); + desc2.SetCullMode(CullMode::kFrontFace); + + PipelineDescriptor desc3; + desc3.SetSampleCount(SampleCount::kCount1); + desc3.SetCullMode(CullMode::kBackFace); + + PipelineDescriptor desc4; + desc4.SetSampleCount(SampleCount::kCount4); + desc4.SetCullMode(CullMode::kNone); + + PipelineDescriptor desc5; + desc5.SetSampleCount(SampleCount::kCount4); + desc5.SetCullMode(CullMode::kFrontFace); + + // Post 5 jobs with distinct descriptors + queue->PostJobForDescriptor(desc1, [&]() { + completed_jobs++; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc2, [&]() { + completed_jobs++; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc3, [&]() { + completed_jobs++; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc4, [&]() { + completed_jobs++; + latch.CountDown(); + }); + + queue->PostJobForDescriptor(desc5, [&]() { + completed_jobs++; + latch.CountDown(); + }); + + latch.Wait(); + + EXPECT_EQ(completed_jobs, 5); +} + +} // namespace testing +} // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc index ef14ed515a28a..da10b9be100b8 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc @@ -24,8 +24,8 @@ bool PipelineCompileQueue::PostJobForDescriptor(const PipelineDescriptor& desc, // place, someone gave the compile queue a job for the same description. // This is highly unusual but technically not impossible. Just run the job // eagerly. - FML_LOG(ERROR) << "Got multiple compile jobs for the same descriptor. " - "Running eagerly."; + FML_LOG(WARNING) << "Got multiple compile jobs for the same descriptor. " + "Running eagerly."; PostJob(job); return true; } diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue_unittests.cc b/engine/src/flutter/impeller/renderer/pipeline_compile_queue_unittests.cc new file mode 100644 index 0000000000000..14be51dcd1556 --- /dev/null +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue_unittests.cc @@ -0,0 +1,96 @@ +// 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/pipeline_compile_queue.h" + +#include + +#include "flutter/fml/closure.h" +#include "flutter/testing/testing.h" + +namespace impeller { +namespace testing { + +class TestPipelineCompileQueue : public PipelineCompileQueue { + public: + void PostJob(const fml::closure& job) override { + if (job) { + job(); + } + } + + void OnJobAdded() override {} + + bool AddJobForTest(const PipelineDescriptor& desc, const fml::closure& job) { + return AddJob(desc, job); + } + + bool HasPendingJobsForTest() { return HasPendingJobs(); } +}; + +TEST(PipelineCompileQueueTest, AddJobReturnsTrueForNewDescriptor) { + TestPipelineCompileQueue queue; + PipelineDescriptor desc; + bool job_executed = false; + fml::closure job = [&job_executed]() { job_executed = true; }; + + bool result = queue.AddJobForTest(desc, job); + EXPECT_TRUE(result); +} + +TEST(PipelineCompileQueueTest, AddJobReturnsFalseForDuplicateDescriptor) { + TestPipelineCompileQueue queue; + PipelineDescriptor desc; + bool job1_executed = false; + bool job2_executed = false; + fml::closure job1 = [&job1_executed]() { job1_executed = true; }; + fml::closure job2 = [&job2_executed]() { job2_executed = true; }; + + bool result1 = queue.AddJobForTest(desc, job1); + bool result2 = queue.AddJobForTest(desc, job2); + + EXPECT_TRUE(result1); + EXPECT_FALSE(result2); +} + +TEST(PipelineCompileQueueTest, HasPendingJobsReturnsCorrectState) { + TestPipelineCompileQueue queue; + PipelineDescriptor desc; + fml::closure job = []() {}; + + EXPECT_FALSE(queue.HasPendingJobsForTest()); + + queue.AddJobForTest(desc, job); + EXPECT_TRUE(queue.HasPendingJobsForTest()); +} + +TEST(PipelineCompileQueueTest, PerformJobEagerlyExecutesJob) { + TestPipelineCompileQueue queue; + PipelineDescriptor desc; + bool job_executed = false; + fml::closure job = [&job_executed]() { job_executed = true; }; + + queue.AddJobForTest(desc, job); + queue.PerformJobEagerly(desc); + + EXPECT_TRUE(job_executed); + EXPECT_FALSE(queue.HasPendingJobsForTest()); +} + +TEST(PipelineCompileQueueTest, FinishAllJobsDrainsQueue) { + auto queue = std::make_shared(); + PipelineDescriptor desc; + bool job_executed = false; + fml::closure job = [&job_executed]() { job_executed = true; }; + + queue->AddJobForTest(desc, job); + EXPECT_TRUE(queue->HasPendingJobsForTest()); + + queue.reset(); + + EXPECT_TRUE(job_executed); +} + +} // namespace testing +} // namespace impeller From dcf6915e7a280619f3c4f79387428392d000250f Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Mon, 1 Jun 2026 19:10:32 +0800 Subject: [PATCH 18/24] Fix unit test issue --- .../gles/pipeline_compile_queue_gles.cc | 7 ++-- .../gles/pipeline_compile_queue_gles.h | 2 +- .../pipeline_compile_queue_gles_unittests.cc | 32 ++++++------------- 3 files changed, 14 insertions(+), 27 deletions(-) diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc index 0b9515550ba03..3e818c69b10c1 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc @@ -6,6 +6,7 @@ #include "flutter/fml/logging.h" #include "flutter/fml/trace_event.h" +#include "impeller/base/validation.h" namespace impeller { @@ -28,7 +29,7 @@ void PipelineCompileQueueGLES::OnJobAdded() { Lock lock(processing_mutex_); if (!is_processing_) { is_processing_ = true; - ProcessJobsSequentially(); + DrainPendingJobs(); } } @@ -40,7 +41,7 @@ void PipelineCompileQueueGLES::PostJob(const fml::closure& job) { worker_task_runner_->PostTask(job); } -void PipelineCompileQueueGLES::ProcessJobsSequentially() { +void PipelineCompileQueueGLES::DrainPendingJobs() { PostJob([weak_queue = weak_from_this()]() { if (auto queue = std::static_pointer_cast( weak_queue.lock())) { @@ -52,7 +53,7 @@ void PipelineCompileQueueGLES::ProcessJobsSequentially() { return; } } - queue->ProcessJobsSequentially(); + queue->DrainPendingJobs(); } }); } diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h index e17092e1d71c6..1c671f73c80fd 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h @@ -40,7 +40,7 @@ class PipelineCompileQueueGLES : public PipelineCompileQueue { void OnJobAdded() override; private: - void ProcessJobsSequentially(); + void DrainPendingJobs(); fml::RefPtr worker_task_runner_; Mutex processing_mutex_; diff --git a/engine/src/flutter/impeller/renderer/backend/gles/test/pipeline_compile_queue_gles_unittests.cc b/engine/src/flutter/impeller/renderer/backend/gles/test/pipeline_compile_queue_gles_unittests.cc index 13277f2100435..90cc636412e00 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/test/pipeline_compile_queue_gles_unittests.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/test/pipeline_compile_queue_gles_unittests.cc @@ -42,9 +42,7 @@ TEST(PipelineCompileQueueGLESTest, OnJobAddedProcessesJobsSequentially) { auto queue = PipelineCompileQueueGLES::Create(thread.GetTaskRunner()); ASSERT_NE(queue, nullptr); - std::atomic execution_order{0}; - std::vector results; - std::mutex results_mutex; + std::atomic completed_jobs{0}; fml::CountDownLatch latch(3); PipelineDescriptor desc1; @@ -60,39 +58,27 @@ TEST(PipelineCompileQueueGLESTest, OnJobAddedProcessesJobsSequentially) { desc3.SetCullMode(CullMode::kBackFace); queue->PostJobForDescriptor(desc1, [&]() { - { - std::lock_guard lock(results_mutex); - results.push_back(1); - } - execution_order = 1; + std::this_thread::sleep_for(std::chrono::milliseconds(80)); + completed_jobs++; latch.CountDown(); }); queue->PostJobForDescriptor(desc2, [&]() { - { - std::lock_guard lock(results_mutex); - results.push_back(2); - } - execution_order = 2; + std::this_thread::sleep_for(std::chrono::milliseconds(80)); + completed_jobs++; latch.CountDown(); }); queue->PostJobForDescriptor(desc3, [&]() { - { - std::lock_guard lock(results_mutex); - results.push_back(3); - } - execution_order = 3; + std::this_thread::sleep_for(std::chrono::milliseconds(80)); + completed_jobs++; latch.CountDown(); }); latch.Wait(); - EXPECT_EQ(results.size(), 3u); - std::sort(results.begin(), results.end()); - EXPECT_EQ(results[0], 1); - EXPECT_EQ(results[1], 2); - EXPECT_EQ(results[2], 3); + EXPECT_EQ(completed_jobs, 3); + thread.Join(); } From 312d9d65668947f848ce2fef84fc4f8be35446b9 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Tue, 2 Jun 2026 05:08:17 +0800 Subject: [PATCH 19/24] Add android part --- .../common/shell_test_platform_view_gl.cc | 3 ++- .../android_context_dynamic_impeller.cc | 8 ++++--- .../android_context_dynamic_impeller.h | 4 +++- .../android/android_context_gl_impeller.cc | 21 +++++++++++-------- .../android/android_context_gl_impeller.h | 5 ++++- .../platform/android/platform_view_android.cc | 13 +++++++----- .../flutter/skwasm/render_context_impeller.cc | 2 +- 7 files changed, 35 insertions(+), 21 deletions(-) diff --git a/engine/src/flutter/shell/common/shell_test_platform_view_gl.cc b/engine/src/flutter/shell/common/shell_test_platform_view_gl.cc index 5ee5e7b18a935..c7da40ba45d1b 100644 --- a/engine/src/flutter/shell/common/shell_test_platform_view_gl.cc +++ b/engine/src/flutter/shell/common/shell_test_platform_view_gl.cc @@ -61,7 +61,8 @@ ShellTestPlatformViewGL::ShellTestPlatformViewGL( return; } impeller_context_ = impeller::ContextGLES::Create( - impeller::Flags{}, std::move(gl), ShaderLibraryMappings(), true); + impeller::Flags{}, std::move(gl), ShaderLibraryMappings(), true, + task_runners.GetIOTaskRunner()); } } diff --git a/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.cc b/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.cc index 76d54efec8466..66898fc3e9330 100644 --- a/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.cc +++ b/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.cc @@ -139,9 +139,11 @@ GetActualRenderingAPIForImpeller( } // namespace AndroidContextDynamicImpeller::AndroidContextDynamicImpeller( - const AndroidContext::ContextSettings& settings) + const AndroidContext::ContextSettings& settings, + fml::RefPtr io_task_runner) : AndroidContext(AndroidRenderingAPI::kImpellerVulkan), - settings_(settings) {} + settings_(settings), + io_task_runner_(std::move(io_task_runner)) {} AndroidContextDynamicImpeller::~AndroidContextDynamicImpeller() = default; @@ -185,7 +187,7 @@ void AndroidContextDynamicImpeller::SetupImpellerContext() { if (!vk_context_) { gl_context_ = std::make_shared( std::make_unique(), - settings_.enable_gpu_tracing); + settings_.enable_gpu_tracing, io_task_runner_); } } diff --git a/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.h b/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.h index 4de037bfa77ba..b6e59b65d0dae 100644 --- a/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.h +++ b/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.h @@ -23,7 +23,8 @@ namespace flutter { class AndroidContextDynamicImpeller : public AndroidContext { public: explicit AndroidContextDynamicImpeller( - const AndroidContext::ContextSettings& settings); + const AndroidContext::ContextSettings& settings, + fml::RefPtr io_task_runner); ~AndroidContextDynamicImpeller(); @@ -51,6 +52,7 @@ class AndroidContextDynamicImpeller : public AndroidContext { const AndroidContext::ContextSettings settings_; std::shared_ptr gl_context_; std::shared_ptr vk_context_; + fml::RefPtr io_task_runner_; FML_DISALLOW_COPY_AND_ASSIGN(AndroidContextDynamicImpeller); }; diff --git a/engine/src/flutter/shell/platform/android/android_context_gl_impeller.cc b/engine/src/flutter/shell/platform/android/android_context_gl_impeller.cc index 79c0ec66bd25b..ce7bb0bf23eba 100644 --- a/engine/src/flutter/shell/platform/android/android_context_gl_impeller.cc +++ b/engine/src/flutter/shell/platform/android/android_context_gl_impeller.cc @@ -51,7 +51,8 @@ class AndroidContextGLImpeller::ReactorWorker final static std::shared_ptr CreateImpellerContext( const std::shared_ptr& worker, - bool enable_gpu_tracing) { + bool enable_gpu_tracing, + fml::RefPtr io_task_runner) { auto proc_table = std::make_unique( impeller::egl::CreateProcAddressResolver()); @@ -85,11 +86,11 @@ static std::shared_ptr CreateImpellerContext( auto context = impeller::ContextGLES::Create( impeller::Flags{}, std::move(proc_table), is_gles3 ? gles3_shader_mappings : gles2_shader_mappings, - enable_gpu_tracing); + enable_gpu_tracing, io_task_runner); #else - auto context = - impeller::ContextGLES::Create(impeller::Flags{}, std::move(proc_table), - gles2_shader_mappings, enable_gpu_tracing); + auto context = impeller::ContextGLES::Create( + impeller::Flags{}, std::move(proc_table), gles2_shader_mappings, + enable_gpu_tracing, io_task_runner); #endif // !SLIMPELLER if (!context) { @@ -107,10 +108,12 @@ static std::shared_ptr CreateImpellerContext( AndroidContextGLImpeller::AndroidContextGLImpeller( std::unique_ptr display, - bool enable_gpu_tracing) + bool enable_gpu_tracing, + fml::RefPtr io_task_runner) : AndroidContext(AndroidRenderingAPI::kImpellerOpenGLES), reactor_worker_(std::shared_ptr(new ReactorWorker())), - display_(std::move(display)) { + display_(std::move(display)), + io_task_runner_(std::move(io_task_runner)) { if (!display_ || !display_->IsValid()) { FML_LOG(ERROR) << "Could not create context with invalid EGL display."; return; @@ -174,8 +177,8 @@ AndroidContextGLImpeller::AndroidContextGLImpeller( return; } - auto impeller_context = - CreateImpellerContext(reactor_worker_, enable_gpu_tracing); + auto impeller_context = CreateImpellerContext( + reactor_worker_, enable_gpu_tracing, io_task_runner_); if (!impeller_context) { FML_LOG(ERROR) << "Could not create Impeller context."; diff --git a/engine/src/flutter/shell/platform/android/android_context_gl_impeller.h b/engine/src/flutter/shell/platform/android/android_context_gl_impeller.h index e6d68d8444e38..792c8236bb106 100644 --- a/engine/src/flutter/shell/platform/android/android_context_gl_impeller.h +++ b/engine/src/flutter/shell/platform/android/android_context_gl_impeller.h @@ -6,6 +6,7 @@ #define FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_CONTEXT_GL_IMPELLER_H_ #include "flutter/fml/macros.h" +#include "flutter/fml/task_runner.h" #include "flutter/impeller/toolkit/egl/display.h" #include "flutter/shell/platform/android/context/android_context.h" @@ -14,7 +15,8 @@ namespace flutter { class AndroidContextGLImpeller : public AndroidContext { public: AndroidContextGLImpeller(std::unique_ptr display, - bool enable_gpu_tracing); + bool enable_gpu_tracing, + fml::RefPtr io_task_runner); ~AndroidContextGLImpeller(); @@ -42,6 +44,7 @@ class AndroidContextGLImpeller : public AndroidContext { std::unique_ptr onscreen_context_; std::unique_ptr offscreen_context_; bool is_valid_ = false; + fml::RefPtr io_task_runner_; FML_DISALLOW_COPY_AND_ASSIGN(AndroidContextGLImpeller); }; diff --git a/engine/src/flutter/shell/platform/android/platform_view_android.cc b/engine/src/flutter/shell/platform/android/platform_view_android.cc index 02ed4df41e5c1..ed5b1f9a38a0c 100644 --- a/engine/src/flutter/shell/platform/android/platform_view_android.cc +++ b/engine/src/flutter/shell/platform/android/platform_view_android.cc @@ -108,7 +108,8 @@ static std::shared_ptr CreateAndroidContext( const flutter::TaskRunners& task_runners, AndroidRenderingAPI android_rendering_api, bool enable_opengl_gpu_tracing, - const AndroidContext::ContextSettings& settings) { + const AndroidContext::ContextSettings& settings, + fml::RefPtr io_task_runner) { switch (android_rendering_api) { #if !SLIMPELLER case AndroidRenderingAPI::kSoftware: @@ -123,11 +124,12 @@ static std::shared_ptr CreateAndroidContext( return std::make_unique(settings); case AndroidRenderingAPI::kImpellerOpenGLES: return std::make_unique( - std::make_unique(), - enable_opengl_gpu_tracing); + std::make_unique(), enable_opengl_gpu_tracing, + io_task_runner); case AndroidRenderingAPI::kImpellerAutoselect: // Determine if we're using GL or Vulkan. - return std::make_unique(settings); + return std::make_unique(settings, + io_task_runner); } FML_UNREACHABLE(); } @@ -145,7 +147,8 @@ PlatformViewAndroid::PlatformViewAndroid( task_runners, rendering_api, delegate.OnPlatformViewGetSettings().enable_opengl_gpu_tracing, - CreateContextSettings(delegate.OnPlatformViewGetSettings()))) {} + CreateContextSettings(delegate.OnPlatformViewGetSettings()), + task_runners.GetIOTaskRunner())) {} PlatformViewAndroid::PlatformViewAndroid( PlatformView::Delegate& delegate, diff --git a/engine/src/flutter/skwasm/render_context_impeller.cc b/engine/src/flutter/skwasm/render_context_impeller.cc index 461bd75b49c71..a0c18b430facb 100644 --- a/engine/src/flutter/skwasm/render_context_impeller.cc +++ b/engine/src/flutter/skwasm/render_context_impeller.cc @@ -128,7 +128,7 @@ std::unique_ptr Skwasm::RenderContext::Make( auto context = impeller::ContextGLES::Create( impeller::Flags{}, std::move(gl), ShaderLibraryMappingsForApplication(), - false); + false, nullptr); auto worker = std::make_shared(); context->AddReactorWorker(worker); From 20fd5fa3b34f06ac3ca1297b8ae324b02ab0cd77 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Thu, 4 Jun 2026 02:39:05 +0800 Subject: [PATCH 20/24] io task runner maybe null when call ContextGLES::Create --- .../src/flutter/impeller/renderer/backend/gles/context_gles.h | 2 +- engine/src/flutter/skwasm/render_context_impeller.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/src/flutter/impeller/renderer/backend/gles/context_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/context_gles.h index ed1c4ab52920a..a35d4ab9342d1 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/context_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/context_gles.h @@ -66,7 +66,7 @@ class ContextGLES final : public Context, std::unique_ptr gl, const std::vector>& shader_libraries, bool enable_gpu_tracing, - fml::RefPtr io_task_runner); + fml::RefPtr io_task_runner = nullptr); // |Context| std::string DescribeGpuModel() const override; diff --git a/engine/src/flutter/skwasm/render_context_impeller.cc b/engine/src/flutter/skwasm/render_context_impeller.cc index a0c18b430facb..461bd75b49c71 100644 --- a/engine/src/flutter/skwasm/render_context_impeller.cc +++ b/engine/src/flutter/skwasm/render_context_impeller.cc @@ -128,7 +128,7 @@ std::unique_ptr Skwasm::RenderContext::Make( auto context = impeller::ContextGLES::Create( impeller::Flags{}, std::move(gl), ShaderLibraryMappingsForApplication(), - false, nullptr); + false); auto worker = std::make_shared(); context->AddReactorWorker(worker); From cd35071983dd599e9f00b97b1def43964260fe5d Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Fri, 5 Jun 2026 01:14:23 +0800 Subject: [PATCH 21/24] Remove not used comments --- .../renderer/backend/gles/pipeline_compile_queue_gles.h | 9 --------- .../backend/vulkan/pipeline_compile_queue_vulkan.h | 9 --------- 2 files changed, 18 deletions(-) diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h index 1c671f73c80fd..6e750aeca49f2 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h @@ -26,17 +26,8 @@ class PipelineCompileQueueGLES : public PipelineCompileQueue { PipelineCompileQueueGLES& operator=(const PipelineCompileQueueGLES&) = delete; - //---------------------------------------------------------------------------- - /// @brief Post a job to the worker task runner. - /// - /// @param[in] job The job - /// void PostJob(const fml::closure& job) override; - //---------------------------------------------------------------------------- - /// @brief Called after a job has been added to the queue. Implements - /// the sequential scheduling strategy for GLES. - /// void OnJobAdded() override; private: diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h index 45eadf4a81932..7932a7edfd342 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h @@ -26,17 +26,8 @@ class PipelineCompileQueueVulkan : public PipelineCompileQueue { PipelineCompileQueueVulkan& operator=(const PipelineCompileQueueVulkan&) = delete; - //---------------------------------------------------------------------------- - /// @brief Post a job to the worker task runner. - /// - /// @param[in] job The job - /// void PostJob(const fml::closure& job) override; - //---------------------------------------------------------------------------- - /// @brief Called after a job has been added to the queue. Implements - /// the parallel scheduling strategy for Vulkan. - /// void OnJobAdded() override; private: From 226b234ffc7a6881365488937b059eade364aa9e Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Fri, 5 Jun 2026 01:25:26 +0800 Subject: [PATCH 22/24] Make PipelineCompileQueueVulkan PipelineCompileQueueGles constructor private --- .../renderer/backend/gles/pipeline_compile_queue_gles.h | 5 ++--- .../renderer/backend/vulkan/pipeline_compile_queue_vulkan.h | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h index 6e750aeca49f2..902f9a2060c52 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h @@ -17,9 +17,6 @@ class PipelineCompileQueueGLES : public PipelineCompileQueue { static std::shared_ptr Create( fml::RefPtr worker_task_runner); - explicit PipelineCompileQueueGLES( - fml::RefPtr worker_task_runner); - ~PipelineCompileQueueGLES() override; PipelineCompileQueueGLES(const PipelineCompileQueueGLES&) = delete; @@ -31,6 +28,8 @@ class PipelineCompileQueueGLES : public PipelineCompileQueue { void OnJobAdded() override; private: + explicit PipelineCompileQueueGLES( + fml::RefPtr worker_task_runner); void DrainPendingJobs(); fml::RefPtr worker_task_runner_; diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h index 7932a7edfd342..7bf6a0b92170d 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/pipeline_compile_queue_vulkan.h @@ -16,9 +16,6 @@ class PipelineCompileQueueVulkan : public PipelineCompileQueue { static std::shared_ptr Create( std::shared_ptr worker_task_runner); - explicit PipelineCompileQueueVulkan( - std::shared_ptr worker_task_runner); - ~PipelineCompileQueueVulkan() override; PipelineCompileQueueVulkan(const PipelineCompileQueueVulkan&) = delete; @@ -31,6 +28,8 @@ class PipelineCompileQueueVulkan : public PipelineCompileQueue { void OnJobAdded() override; private: + explicit PipelineCompileQueueVulkan( + std::shared_ptr worker_task_runner); std::shared_ptr worker_task_runner_; }; From 54ddd8f989c36b4ce36e2e87324c154c22402fe5 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Fri, 12 Jun 2026 21:47:00 +0800 Subject: [PATCH 23/24] Fix build error --- engine/src/flutter/impeller/renderer/pipeline_compile_queue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h index a5cc3b5215cb4..76c8b1831da57 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h @@ -45,9 +45,9 @@ class PipelineCompileQueue static std::shared_ptr Create( std::shared_ptr worker_task_runner); - ~PipelineCompileQueue(); explicit PipelineCompileQueue( std::shared_ptr worker_task_runner); + PipelineCompileQueue() = default; virtual ~PipelineCompileQueue(); From 3f0cf4ede60b027b1d222f540706a7ef99295930 Mon Sep 17 00:00:00 2001 From: Xiaowei Guan Date: Fri, 12 Jun 2026 23:15:45 +0800 Subject: [PATCH 24/24] Remove redundant code --- .../src/flutter/impeller/renderer/pipeline_compile_queue.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h index 76c8b1831da57..f3ec4ed78e975 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h @@ -42,12 +42,6 @@ namespace impeller { class PipelineCompileQueue : public std::enable_shared_from_this { public: - static std::shared_ptr Create( - std::shared_ptr worker_task_runner); - - explicit PipelineCompileQueue( - std::shared_ptr worker_task_runner); - PipelineCompileQueue() = default; virtual ~PipelineCompileQueue();