12 #include "flutter/fml/container.h"
13 #include "flutter/fml/trace_event.h"
22 #include "vulkan/vulkan_core.h"
23 #include "vulkan/vulkan_enums.hpp"
27 PipelineLibraryVK::PipelineLibraryVK(
28 const std::shared_ptr<DeviceHolderVK>& device_holder,
29 std::shared_ptr<const Capabilities> caps,
30 fml::UniqueFD cache_directory,
31 std::shared_ptr<fml::ConcurrentTaskRunner> worker_task_runner)
32 : device_holder_(device_holder),
33 pso_cache_(
std::make_shared<PipelineCacheVK>(
std::move(caps),
35 std::move(cache_directory))),
36 worker_task_runner_(
std::move(worker_task_runner)) {
37 FML_DCHECK(worker_task_runner_);
38 if (!pso_cache_->IsValid() || !worker_task_runner_) {
48 bool PipelineLibraryVK::IsValid()
const {
52 std::unique_ptr<ComputePipelineVK> PipelineLibraryVK::CreateComputePipeline(
53 const ComputePipelineDescriptor& desc) {
54 TRACE_EVENT0(
"flutter", __FUNCTION__);
55 vk::ComputePipelineCreateInfo pipeline_info;
60 const auto entrypoint = desc.GetStageEntrypoint();
66 std::shared_ptr<DeviceHolderVK> strong_device = device_holder_.lock();
70 auto device_properties = strong_device->GetPhysicalDevice().getProperties();
71 auto max_wg_size = device_properties.limits.maxComputeWorkGroupSize;
75 vk::SpecializationMapEntry specialization_map_entry[1];
77 uint32_t workgroup_size_x = max_wg_size[0];
78 specialization_map_entry[0].constantID = 0;
79 specialization_map_entry[0].offset = 0;
80 specialization_map_entry[0].size =
sizeof(uint32_t);
82 vk::SpecializationInfo specialization_info;
83 specialization_info.mapEntryCount = 1;
84 specialization_info.pMapEntries = &specialization_map_entry[0];
85 specialization_info.dataSize =
sizeof(uint32_t);
86 specialization_info.pData = &workgroup_size_x;
88 vk::PipelineShaderStageCreateInfo info;
89 info.setStage(vk::ShaderStageFlagBits::eCompute);
90 info.setPName(
"main");
92 info.setPSpecializationInfo(&specialization_info);
93 pipeline_info.setStage(info);
98 std::vector<vk::DescriptorSetLayoutBinding> desc_bindings;
100 for (
auto layout : desc.GetDescriptorSetLayouts()) {
102 desc_bindings.push_back(vk_desc_layout);
105 vk::DescriptorSetLayoutCreateInfo descs_layout_info;
106 descs_layout_info.setBindings(desc_bindings);
108 auto [descs_result, descs_layout] =
109 strong_device->GetDevice().createDescriptorSetLayoutUnique(
111 if (descs_result != vk::Result::eSuccess) {
117 "Descriptor Set Layout " + desc.GetLabel());
122 vk::PipelineLayoutCreateInfo pipeline_layout_info;
123 pipeline_layout_info.setSetLayouts(descs_layout.get());
124 auto pipeline_layout = strong_device->GetDevice().createPipelineLayoutUnique(
125 pipeline_layout_info);
126 if (pipeline_layout.result != vk::Result::eSuccess) {
127 VALIDATION_LOG <<
"Could not create pipeline layout for pipeline "
128 << desc.GetLabel() <<
": "
129 << vk::to_string(pipeline_layout.result);
132 pipeline_info.setLayout(pipeline_layout.value.get());
137 auto pipeline = pso_cache_->CreatePipeline(pipeline_info);
139 VALIDATION_LOG <<
"Could not create graphics pipeline: " << desc.GetLabel();
144 "Pipeline Layout " + desc.GetLabel());
146 "Pipeline " + desc.GetLabel());
148 return std::make_unique<ComputePipelineVK>(
153 std::move(pipeline_layout.value),
154 std::move(descs_layout)
159 PipelineFuture<PipelineDescriptor> PipelineLibraryVK::GetPipeline(
160 PipelineDescriptor descriptor,
162 Lock lock(pipelines_mutex_);
163 if (
auto found = pipelines_.find(descriptor); found != pipelines_.end()) {
164 return found->second;
171 RealizedFuture<std::shared_ptr<Pipeline<PipelineDescriptor>>>(
nullptr)};
174 auto promise = std::make_shared<
175 NoExceptionPromise<std::shared_ptr<Pipeline<PipelineDescriptor>>>>();
176 auto pipeline_future =
177 PipelineFuture<PipelineDescriptor>{descriptor, promise->get_future()};
178 pipelines_[descriptor] = pipeline_future;
180 auto weak_this = weak_from_this();
182 auto generation_task = [descriptor, weak_this, promise]() {
183 auto thiz = weak_this.lock();
185 promise->set_value(
nullptr);
186 VALIDATION_LOG <<
"Pipeline library was collected before the pipeline "
199 worker_task_runner_->PostTask(generation_task);
204 return pipeline_future;
208 PipelineFuture<ComputePipelineDescriptor> PipelineLibraryVK::GetPipeline(
209 ComputePipelineDescriptor descriptor,
211 Lock lock(compute_pipelines_mutex_);
212 if (
auto found = compute_pipelines_.find(descriptor);
213 found != compute_pipelines_.end()) {
214 return found->second;
221 RealizedFuture<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>(
225 auto promise = std::make_shared<
226 std::promise<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>>();
227 auto pipeline_future = PipelineFuture<ComputePipelineDescriptor>{
228 descriptor, promise->get_future()};
229 compute_pipelines_[descriptor] = pipeline_future;
231 auto weak_this = weak_from_this();
233 auto generation_task = [descriptor, weak_this, promise]() {
234 auto self = weak_this.lock();
236 promise->set_value(
nullptr);
237 VALIDATION_LOG <<
"Pipeline library was collected before the pipeline "
245 promise->set_value(
nullptr);
246 VALIDATION_LOG <<
"Could not create pipeline: " << descriptor.GetLabel();
250 promise->set_value(std::move(pipeline));
254 worker_task_runner_->PostTask(generation_task);
259 return pipeline_future;
263 bool PipelineLibraryVK::HasPipeline(
const PipelineDescriptor& descriptor) {
264 Lock lock(pipelines_mutex_);
265 return pipelines_.find(descriptor) != pipelines_.end();
269 void PipelineLibraryVK::RemovePipelinesWithEntryPoint(
270 std::shared_ptr<const ShaderFunction>
function) {
271 Lock lock(pipelines_mutex_);
273 fml::erase_if(pipelines_, [&](
auto item) {
274 return item->first.GetEntrypointForStage(function->GetStage())
275 ->IsEqual(*
function);
280 if (++frames_acquired_ == 50u) {
282 cache_dirty_ =
false;
283 PersistPipelineCacheToDisk();
285 frames_acquired_ = 0;
289 void PipelineLibraryVK::PersistPipelineCacheToDisk() {
290 worker_task_runner_->PostTask(
291 [weak_cache = decltype(pso_cache_)::weak_type(pso_cache_)]() {
292 auto cache = weak_cache.lock();
296 cache->PersistCacheToDisk();
304 const std::shared_ptr<fml::ConcurrentTaskRunner>&
306 return worker_task_runner_;