diff --git a/engine/src/flutter/impeller/base/flags.h b/engine/src/flutter/impeller/base/flags.h index cb581c34fe5a2..2bc904e4d5efa 100644 --- a/engine/src/flutter/impeller/base/flags.h +++ b/engine/src/flutter/impeller/base/flags.h @@ -7,9 +7,6 @@ namespace impeller { struct Flags { - /// Whether to defer PSO construction until first use. Usage Will introduce - /// raster jank. - bool lazy_shader_mode = false; /// When turned on DrawLine will use the experimental antialiased path. bool antialiased_lines = false; }; diff --git a/engine/src/flutter/impeller/entity/contents/content_context.cc b/engine/src/flutter/impeller/entity/contents/content_context.cc index 6db866d3cd04f..2dcdc09165ef7 100644 --- a/engine/src/flutter/impeller/entity/contents/content_context.cc +++ b/engine/src/flutter/impeller/entity/contents/content_context.cc @@ -128,12 +128,8 @@ class Variants : public GenericVariants { context.GetPipelineLibrary()->LogPipelineCreation(*desc); options.ApplyToPipelineDescriptor(*desc); desc_ = desc; - if (context.GetFlags().lazy_shader_mode) { - SetDefault(options, nullptr); - } else { - SetDefault(options, std::make_unique(context, desc_, - /*async=*/true)); - } + SetDefault(options, std::make_unique(context, desc_, + /*async=*/true)); } PipelineHandleT* Get(const ContentContextOptions& options) const { @@ -164,7 +160,8 @@ template RenderPipelineHandleT* CreateIfNeeded( const ContentContext* context, Variants& container, - ContentContextOptions opts) { + ContentContextOptions opts, + PipelineCompileQueue* compile_queue) { if (!context->IsValid()) { return nullptr; } @@ -183,7 +180,7 @@ RenderPipelineHandleT* CreateIfNeeded( FML_CHECK(default_handle != nullptr); const std::shared_ptr>& pipeline = - default_handle->WaitAndGet(); + default_handle->WaitAndGet(compile_queue); if (!pipeline) { return nullptr; } @@ -204,11 +201,14 @@ template PipelineRef GetPipeline(const ContentContext* context, Variants& container, ContentContextOptions opts) { - TypedPipeline* pipeline = CreateIfNeeded(context, container, opts); + auto compile_queue = + context->GetContext()->GetPipelineLibrary()->GetPipelineCompileQueue(); + TypedPipeline* pipeline = + CreateIfNeeded(context, container, opts, compile_queue); if (!pipeline) { return raw_ptr>(); } - return raw_ptr(pipeline->WaitAndGet()); + return raw_ptr(pipeline->WaitAndGet(compile_queue)); } } // namespace @@ -692,14 +692,9 @@ ContentContext::ContentContext( } clip_pipeline_descriptor->SetColorAttachmentDescriptors( std::move(clip_color_attachments)); - if (GetContext()->GetFlags().lazy_shader_mode) { - pipelines_->clip.SetDefaultDescriptor(clip_pipeline_descriptor); - pipelines_->clip.SetDefault(options, nullptr); - } else { - pipelines_->clip.SetDefault( - options, - std::make_unique(*context_, clip_pipeline_descriptor)); - } + pipelines_->clip.SetDefault( + options, + std::make_unique(*context_, clip_pipeline_descriptor)); pipelines_->texture_downsample.CreateDefault( *context_, options_no_msaa_no_depth_stencil); pipelines_->texture_downsample_bounded.CreateDefault( @@ -1018,9 +1013,6 @@ void ContentContext::ResetTransientsBuffers() { } void ContentContext::InitializeCommonlyUsedShadersIfNeeded() const { - if (GetContext()->GetFlags().lazy_shader_mode) { - return; - } GetContext()->InitializeCommonlyUsedShadersIfNeeded(); } diff --git a/engine/src/flutter/impeller/renderer/BUILD.gn b/engine/src/flutter/impeller/renderer/BUILD.gn index 953baf89f3376..0340164b956f2 100644 --- a/engine/src/flutter/impeller/renderer/BUILD.gn +++ b/engine/src/flutter/impeller/renderer/BUILD.gn @@ -52,6 +52,8 @@ impeller_component("renderer") { "pipeline.h", "pipeline_builder.cc", "pipeline_builder.h", + "pipeline_compile_queue.cc", + "pipeline_compile_queue.h", "pipeline_descriptor.cc", "pipeline_descriptor.h", "pipeline_library.cc", 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/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_compile_queue_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.cc new file mode 100644 index 0000000000000..e3e3e1debadb4 --- /dev/null +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.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/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 worker_task_runner) { + return std::shared_ptr( + new PipelineCompileQueueGLES(std::move(worker_task_runner))); +} + +PipelineCompileQueueGLES::PipelineCompileQueueGLES( + fml::RefPtr worker_task_runner) + : PipelineCompileQueue(true), + worker_task_runner_(std::move(worker_task_runner)) {} + +PipelineCompileQueueGLES::~PipelineCompileQueueGLES() {} + +void PipelineCompileQueueGLES::PostJob(const fml::closure& job) { + if (!job) { + return; + } + + 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 new file mode 100644 index 0000000000000..31fc6a528e48e --- /dev/null +++ b/engine/src/flutter/impeller/renderer/backend/gles/pipeline_compile_queue_gles.h @@ -0,0 +1,41 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_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 worker_task_runner); + + explicit PipelineCompileQueueGLES( + fml::RefPtr worker_task_runner); + + ~PipelineCompileQueueGLES() override; + + PipelineCompileQueueGLES(const PipelineCompileQueueGLES&) = delete; + + 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; + + private: + fml::RefPtr worker_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 240891dfc4b60..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 @@ -16,8 +16,12 @@ 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)), + compile_queue_(PipelineCompileQueueGLES::Create(io_task_runner_)) {} static std::string GetShaderInfoLog(const ProcTableGLES& gl, GLuint shader) { GLint log_length = 0; @@ -296,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; } @@ -373,4 +394,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 25c51c8113d38..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 @@ -9,7 +9,9 @@ #include #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" @@ -91,8 +93,11 @@ 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); + explicit PipelineLibraryGLES(std::shared_ptr reactor, + fml::RefPtr io_task_runner); // |PipelineLibrary| bool IsValid() const override; @@ -127,6 +132,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/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 02c4e6087a4c8..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 @@ -26,7 +26,8 @@ PipelineLibraryVK::PipelineLibraryVK( pso_cache_(std::make_shared(std::move(caps), device_holder, std::move(cache_directory))), - worker_task_runner_(std::move(worker_task_runner)) { + worker_task_runner_(std::move(worker_task_runner)), + compile_queue_(PipelineCompileQueueVulkan::Create(worker_task_runner)) { FML_DCHECK(worker_task_runner_); if (!pso_cache_->IsValid() || !worker_task_runner_) { return; @@ -179,8 +180,6 @@ PipelineFuture PipelineLibraryVK::GetPipeline( auto thiz = weak_this.lock(); if (!thiz) { promise->set_value(nullptr); - VALIDATION_LOG << "Pipeline library was collected before the pipeline " - "could be created."; return; } @@ -193,7 +192,8 @@ PipelineFuture PipelineLibraryVK::GetPipeline( }; if (async) { - worker_task_runner_->PostTask(generation_task); + compile_queue_->PostJobForDescriptor(descriptor, + std::move(generation_task)); } else { generation_task(); } @@ -304,4 +304,8 @@ PipelineLibraryVK::GetWorkerTaskRunner() const { return worker_task_runner_; } +PipelineCompileQueue* PipelineLibraryVK::GetPipelineCompileQueue() const { + return compile_queue_.get(); +} + } // namespace impeller 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 407b871dd0827..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,6 +13,7 @@ #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" @@ -48,6 +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_; PipelineLibraryVK( const std::shared_ptr& device_holder, @@ -76,6 +78,9 @@ class PipelineLibraryVK final void RemovePipelinesWithEntryPoint( std::shared_ptr function) override; + // |PipelineLibrary| + PipelineCompileQueue* GetPipelineCompileQueue() const override; + std::unique_ptr CreateComputePipeline( const ComputePipelineDescriptor& desc, PipelineKey pipeline_key); diff --git a/engine/src/flutter/impeller/renderer/pipeline.h b/engine/src/flutter/impeller/renderer/pipeline.h index 989c33998425a..ec410896dd562 100644 --- a/engine/src/flutter/impeller/renderer/pipeline.h +++ b/engine/src/flutter/impeller/renderer/pipeline.h @@ -13,6 +13,7 @@ #include "impeller/renderer/compute_pipeline_descriptor.h" #include "impeller/renderer/context.h" #include "impeller/renderer/pipeline_builder.h" +#include "impeller/renderer/pipeline_compile_queue.h" #include "impeller/renderer/pipeline_descriptor.h" #include "impeller/renderer/shader_stage_compatibility_checker.h" @@ -125,12 +126,16 @@ class GenericRenderPipelineHandle { virtual ~GenericRenderPipelineHandle() = default; - std::shared_ptr> WaitAndGet() { + std::shared_ptr> WaitAndGet( + PipelineCompileQueue* queue) { if (did_wait_) { return pipeline_; } did_wait_ = true; if (pipeline_future_.IsValid()) { + if (queue != nullptr && pipeline_future_.descriptor.has_value()) { + queue->PerformJobEagerly(pipeline_future_.descriptor.value()); + } pipeline_ = pipeline_future_.Get(); } return pipeline_; diff --git a/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc new file mode 100644 index 0000000000000..b04a66228346c --- /dev/null +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.cc @@ -0,0 +1,121 @@ +// 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 "flutter/fml/logging.h" +#include "flutter/fml/trace_event.h" + +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; + } + + { + 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; +} + +fml::closure PipelineCompileQueue::TakeNextJob() { + Lock lock(pending_jobs_mutex_); + if (pending_jobs_.empty()) { + return nullptr; + } + auto job_iterator = pending_jobs_.begin(); + auto job = job_iterator->second; + pending_jobs_.erase(job_iterator); + return job; +} + +fml::closure PipelineCompileQueue::TakeJob(const PipelineDescriptor& desc) { + Lock lock(pending_jobs_mutex_); + auto found = pending_jobs_.find(desc); + if (found == pending_jobs_.end()) { + return nullptr; + } + // The pipeline compile job was somewhere in the task queue. However, a + // rendering operation needed the job to be done ASAP. Instead of waiting for + // the pipeline compile queue to eventually get to finishing job, the thread + // waiting on the job just decided to take the job from the queue and do it + // itself. If there were jobs ahead of this one, it means that they were + // mis-prioritized. This counter dumps the number of job re-prioritizations. + priorities_elevated_++; + FML_TRACE_COUNTER("impeller", "PipelineCompileQueue", + reinterpret_cast(this), // Trace Counter ID + "PrioritiesElevated", priorities_elevated_); + auto job = found->second; + pending_jobs_.erase(found); + return job; +} + +void PipelineCompileQueue::DoOneJob() { + if (auto job = TakeNextJob()) { + job(); + } +} + +void PipelineCompileQueue::FlushPendingJobs() { + wait_until_rendering_ = false; + 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. + while (true) { + bool has_jobs = false; + { + Lock lock(pending_jobs_mutex_); + has_jobs = !pending_jobs_.empty(); + } + if (!has_jobs) { + return; + } + // Allow any remaining worker threads to take jobs from this queue. + DoOneJob(); + } +} + +void PipelineCompileQueue::PerformJobEagerly(const PipelineDescriptor& desc) { + if (auto job = TakeJob(desc)) { + 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 new file mode 100644 index 0000000000000..e3e64e5289073 --- /dev/null +++ b/engine/src/flutter/impeller/renderer/pipeline_compile_queue.h @@ -0,0 +1,113 @@ +// 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_PIPELINE_COMPILE_QUEUE_H_ +#define FLUTTER_IMPELLER_RENDERER_PIPELINE_COMPILE_QUEUE_H_ + +#include + +#include "flutter/fml/closure.h" +#include "flutter/fml/concurrent_message_loop.h" +#include "impeller/base/thread.h" +#include "impeller/renderer/pipeline_descriptor.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief A task queue designed for managing compilation of pipeline state +/// objects. +/// +/// The task queue attempts to perform compile jobs as quickly as +/// possible by dispatching tasks to a concurrent task runner. These +/// tasks are dispatched during renderer creation and usually +/// complete before the first frame is rendered. In this ideal case, +/// this queue is entirely unnecessary and and serves as a thin +/// wrapper around just posting the compile jobs to a concurrent +/// task runner. +/// +/// If however, usually on lower end device, the compile jobs cannot +/// be completed before the first frame is rendered, the implicit +/// act of waiting for the compile job to be done can instead be +/// augmented to take the pending job and perform it eagerly on the +/// waiters thread. This effectively turns an idle wait into the job +/// skipping to the front of the line and being done on the callers +/// thread. +/// +/// Again, the entire point of this class is the reduce startup +/// times on the lowest end devices. On high end device, a queue is +/// entirely optional. The queue skipping mechanism all assume the +/// optional availability of a compile queue. +/// +class PipelineCompileQueue + : public std::enable_shared_from_this { + public: + PipelineCompileQueue() = default; + PipelineCompileQueue(bool wait_until_rendering); + + virtual ~PipelineCompileQueue(); + + PipelineCompileQueue(const PipelineCompileQueue&) = delete; + + PipelineCompileQueue& operator=(const PipelineCompileQueue&) = 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); + + //---------------------------------------------------------------------------- + /// @brief If the task has not yet been done, perform it eagerly on the + /// calling thread. This can be used in lieu of an idle wait for + /// the task completion on the calling thread. + /// + /// @param[in] desc The description + /// + 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) = 0; + + bool WaitUntilRendering() { return wait_until_rendering_; }; + + void FlushPendingJobs(); + + protected: + std::atomic wait_until_rendering_ = false; + + private: + Mutex pending_jobs_mutex_; + size_t priorities_elevated_ = {}; + + std::unordered_map, + ComparableEqual> + pending_jobs_ IPLR_GUARDED_BY(pending_jobs_mutex_); + + fml::closure TakeJob(const PipelineDescriptor& desc); + + fml::closure TakeNextJob(); + + void DoOneJob(); + + void FinishAllJobs(); +}; + +} // namespace impeller + +#endif // FLUTTER_IMPELLER_RENDERER_PIPELINE_COMPILE_QUEUE_H_ diff --git a/engine/src/flutter/impeller/renderer/pipeline_library.cc b/engine/src/flutter/impeller/renderer/pipeline_library.cc index 0a39a35d4b0c6..28647af867549 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_library.cc +++ b/engine/src/flutter/impeller/renderer/pipeline_library.cc @@ -74,4 +74,8 @@ PipelineLibrary::GetPipelineUseCounts() const { return counts; } +PipelineCompileQueue* PipelineLibrary::GetPipelineCompileQueue() const { + return nullptr; +} + } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/pipeline_library.h b/engine/src/flutter/impeller/renderer/pipeline_library.h index 6126aa6363399..c9f563d3d6d5c 100644 --- a/engine/src/flutter/impeller/renderer/pipeline_library.h +++ b/engine/src/flutter/impeller/renderer/pipeline_library.h @@ -12,6 +12,7 @@ #include "impeller/base/thread.h" #include "impeller/base/thread_safety.h" #include "impeller/renderer/pipeline.h" +#include "impeller/renderer/pipeline_compile_queue.h" #include "impeller/renderer/pipeline_descriptor.h" namespace impeller { @@ -86,6 +87,14 @@ class PipelineLibrary : public std::enable_shared_from_this { ComparableEqual> GetPipelineUseCounts() const; + //---------------------------------------------------------------------------- + /// @brief If this library has a configurable compile queue, return a + /// pointer to it. + /// + /// @return The pipeline compile queue if one is present. + /// + virtual PipelineCompileQueue* GetPipelineCompileQueue() const; + protected: PipelineLibrary(); diff --git a/engine/src/flutter/shell/common/shell.cc b/engine/src/flutter/shell/common/shell.cc index 762663b061d1c..b1871e1e30b49 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) { + 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 @@ -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: Flush pending pipeline compilation jobs. + fml::TaskRunner::RunNowOrPostTask(raster_task_runner, flush_task); + latch.Signal(); }; 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 2e84fb09a372f..8c24261a54f8a 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 @@ -126,7 +126,6 @@ GetActualRenderingAPIForImpeller( .enable_surface_control = settings.enable_surface_control, .impeller_flags = { - .lazy_shader_mode = settings.impeller_flags.lazy_shader_mode, .antialiased_lines = settings.impeller_flags.antialiased_lines, }, 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 86aa18fcf6c5b..02ed4df41e5c1 100644 --- a/engine/src/flutter/shell/platform/android/platform_view_android.cc +++ b/engine/src/flutter/shell/platform/android/platform_view_android.cc @@ -59,8 +59,6 @@ AndroidContext::ContextSettings CreateContextSettings( settings.enable_gpu_tracing = p_settings.enable_vulkan_gpu_tracing; settings.enable_validation = p_settings.enable_vulkan_validation; settings.enable_surface_control = p_settings.enable_surface_control; - settings.impeller_flags.lazy_shader_mode = - p_settings.impeller_enable_lazy_shader_mode; settings.impeller_flags.antialiased_lines = p_settings.impeller_antialiased_lines; return settings; diff --git a/engine/src/flutter/shell/platform/embedder/embedder.cc b/engine/src/flutter/shell/platform/embedder/embedder.cc index 27390d7239127..016fe5747e51a 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 779c37b482a0d..3df93096b62f1 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 @@ -45,7 +45,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)), @@ -82,7 +83,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 52febc2b378ee..205aeb1c3d73b 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 @@ -25,7 +25,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;