Flutter Impeller
pipeline_library_vk.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
6 
7 #include <cstdint>
8 
9 #include "flutter/fml/container.h"
10 #include "flutter/fml/trace_event.h"
11 #include "impeller/base/promise.h"
17 
18 namespace impeller {
19 
20 PipelineLibraryVK::PipelineLibraryVK(
21  const std::shared_ptr<DeviceHolderVK>& device_holder,
22  std::shared_ptr<const Capabilities> caps,
23  fml::UniqueFD cache_directory,
24  std::shared_ptr<fml::ConcurrentTaskRunner> worker_task_runner)
25  : device_holder_(device_holder),
26  pso_cache_(std::make_shared<PipelineCacheVK>(std::move(caps),
27  device_holder,
28  std::move(cache_directory))),
29  worker_task_runner_(std::move(worker_task_runner)),
30  compile_queue_(PipelineCompileQueue::Create(worker_task_runner_)) {
31  FML_DCHECK(worker_task_runner_);
32  if (!pso_cache_->IsValid() || !worker_task_runner_) {
33  return;
34  }
35 
36  is_valid_ = true;
37 }
38 
39 PipelineLibraryVK::~PipelineLibraryVK() = default;
40 
41 // |PipelineLibrary|
42 bool PipelineLibraryVK::IsValid() const {
43  return is_valid_;
44 }
45 
46 std::unique_ptr<ComputePipelineVK> PipelineLibraryVK::CreateComputePipeline(
47  const ComputePipelineDescriptor& desc,
48  PipelineKey pipeline_key) {
49  TRACE_EVENT0("flutter", __FUNCTION__);
50  vk::ComputePipelineCreateInfo pipeline_info;
51 
52  //----------------------------------------------------------------------------
53  /// Shader Stage
54  ///
55  const auto entrypoint = desc.GetStageEntrypoint();
56  if (!entrypoint) {
57  VALIDATION_LOG << "Compute shader is missing an entrypoint.";
58  return nullptr;
59  }
60 
61  std::shared_ptr<DeviceHolderVK> strong_device = device_holder_.lock();
62  if (!strong_device) {
63  return nullptr;
64  }
65  auto device_properties = strong_device->GetPhysicalDevice().getProperties();
66  auto max_wg_size = device_properties.limits.maxComputeWorkGroupSize;
67 
68  // Specialization constant 0 carries the workgroup size. Set it to the device
69  // maximum. This only affects shaders that declare their size with
70  // `local_size_x_id = 0`. A shader with a literal `local_size` has no such
71  // constant, so Vulkan ignores this and uses the size baked into the module.
72  vk::SpecializationMapEntry specialization_map_entry[1];
73 
74  uint32_t workgroup_size_x = max_wg_size[0];
75  specialization_map_entry[0].constantID = 0;
76  specialization_map_entry[0].offset = 0;
77  specialization_map_entry[0].size = sizeof(uint32_t);
78 
79  vk::SpecializationInfo specialization_info;
80  specialization_info.mapEntryCount = 1;
81  specialization_info.pMapEntries = &specialization_map_entry[0];
82  specialization_info.dataSize = sizeof(uint32_t);
83  specialization_info.pData = &workgroup_size_x;
84 
85  vk::PipelineShaderStageCreateInfo info;
86  info.setStage(vk::ShaderStageFlagBits::eCompute);
87  info.setPName("main");
88  info.setModule(ShaderFunctionVK::Cast(entrypoint.get())->GetModule());
89  info.setPSpecializationInfo(&specialization_info);
90  pipeline_info.setStage(info);
91 
92  //----------------------------------------------------------------------------
93  /// Pipeline Layout a.k.a the descriptor sets and uniforms.
94  ///
95  std::vector<vk::DescriptorSetLayoutBinding> desc_bindings;
96 
97  for (auto layout : desc.GetDescriptorSetLayouts()) {
98  auto vk_desc_layout = ToVKDescriptorSetLayoutBinding(layout);
99  desc_bindings.push_back(vk_desc_layout);
100  }
101 
102  vk::DescriptorSetLayoutCreateInfo descs_layout_info;
103  descs_layout_info.setBindings(desc_bindings);
104 
105  auto [descs_result, descs_layout] =
106  strong_device->GetDevice().createDescriptorSetLayoutUnique(
107  descs_layout_info);
108  if (descs_result != vk::Result::eSuccess) {
109  VALIDATION_LOG << "unable to create uniform descriptors";
110  return nullptr;
111  }
112 
113  ContextVK::SetDebugName(strong_device->GetDevice(), descs_layout.get(),
114  "Descriptor Set Layout " + desc.GetLabel());
115 
116  //----------------------------------------------------------------------------
117  /// Create the pipeline layout.
118  ///
119  vk::PipelineLayoutCreateInfo pipeline_layout_info;
120  pipeline_layout_info.setSetLayouts(descs_layout.get());
121  auto pipeline_layout = strong_device->GetDevice().createPipelineLayoutUnique(
122  pipeline_layout_info);
123  if (pipeline_layout.result != vk::Result::eSuccess) {
124  VALIDATION_LOG << "Could not create pipeline layout for pipeline "
125  << desc.GetLabel() << ": "
126  << vk::to_string(pipeline_layout.result);
127  return nullptr;
128  }
129  pipeline_info.setLayout(pipeline_layout.value.get());
130 
131  //----------------------------------------------------------------------------
132  /// Finally, all done with the setup info. Create the pipeline itself.
133  ///
134  auto pipeline = pso_cache_->CreatePipeline(pipeline_info);
135  if (!pipeline) {
136  VALIDATION_LOG << "Could not create graphics pipeline: " << desc.GetLabel();
137  return nullptr;
138  }
139 
140  ContextVK::SetDebugName(strong_device->GetDevice(), *pipeline_layout.value,
141  "Pipeline Layout " + desc.GetLabel());
142  ContextVK::SetDebugName(strong_device->GetDevice(), *pipeline,
143  "Pipeline " + desc.GetLabel());
144 
145  return std::make_unique<ComputePipelineVK>(
146  device_holder_,
147  weak_from_this(), //
148  desc, //
149  std::move(pipeline), //
150  std::move(pipeline_layout.value), //
151  std::move(descs_layout), //
152  pipeline_key);
153 }
154 
155 // |PipelineLibrary|
156 PipelineFuture<PipelineDescriptor> PipelineLibraryVK::GetPipeline(
157  PipelineDescriptor descriptor,
158  bool async,
159  bool threadsafe) {
160  Lock lock(pipelines_mutex_);
161  if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) {
162  return found->second;
163  }
164 
165  cache_dirty_ = true;
166  if (!IsValid()) {
167  return {
168  descriptor,
169  RealizedFuture<std::shared_ptr<Pipeline<PipelineDescriptor>>>(nullptr)};
170  }
171 
172  auto promise = std::make_shared<
173  NoExceptionPromise<std::shared_ptr<Pipeline<PipelineDescriptor>>>>();
174  auto pipeline_future =
175  PipelineFuture<PipelineDescriptor>{descriptor, promise->get_future()};
176  pipelines_[descriptor] = pipeline_future;
177 
178  auto weak_this = weak_from_this();
179 
180  PipelineKey next_key = pipeline_key_++;
181  auto generation_task = [descriptor, weak_this, promise, next_key]() {
182  auto thiz = weak_this.lock();
183  if (!thiz) {
184  promise->set_value(nullptr);
185  return;
186  }
187 
188  promise->set_value(PipelineVK::Create(
189  descriptor, //
190  PipelineLibraryVK::Cast(*thiz).device_holder_.lock(), //
191  weak_this, //
192  next_key //
193  ));
194  };
195 
196  if (async) {
197  compile_queue_->PostJobForDescriptor(descriptor,
198  std::move(generation_task));
199  } else {
200  generation_task();
201  }
202 
203  return pipeline_future;
204 }
205 
206 // |PipelineLibrary|
207 PipelineFuture<ComputePipelineDescriptor> PipelineLibraryVK::GetPipeline(
208  ComputePipelineDescriptor descriptor,
209  bool async) {
210  Lock lock(pipelines_mutex_);
211  if (auto found = compute_pipelines_.find(descriptor);
212  found != compute_pipelines_.end()) {
213  return found->second;
214  }
215 
216  cache_dirty_ = true;
217  if (!IsValid()) {
218  return {
219  descriptor,
220  RealizedFuture<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>(
221  nullptr)};
222  }
223 
224  auto promise = std::make_shared<
225  std::promise<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>>();
226  auto pipeline_future = PipelineFuture<ComputePipelineDescriptor>{
227  descriptor, promise->get_future()};
228  compute_pipelines_[descriptor] = pipeline_future;
229 
230  auto weak_this = weak_from_this();
231 
232  PipelineKey next_key = pipeline_key_++;
233  auto generation_task = [descriptor, weak_this, promise, next_key]() {
234  auto self = weak_this.lock();
235  if (!self) {
236  promise->set_value(nullptr);
237  VALIDATION_LOG << "Pipeline library was collected before the pipeline "
238  "could be created.";
239  return;
240  }
241 
242  auto pipeline = PipelineLibraryVK::Cast(*self).CreateComputePipeline(
243  descriptor, next_key);
244  if (!pipeline) {
245  promise->set_value(nullptr);
246  VALIDATION_LOG << "Could not create pipeline: " << descriptor.GetLabel();
247  return;
248  }
249 
250  promise->set_value(std::move(pipeline));
251  };
252 
253  if (async) {
254  worker_task_runner_->PostTask(generation_task);
255  } else {
256  generation_task();
257  }
258 
259  return pipeline_future;
260 }
261 
262 // |PipelineLibrary|
263 bool PipelineLibraryVK::HasPipeline(const PipelineDescriptor& descriptor) {
264  Lock lock(pipelines_mutex_);
265  return pipelines_.find(descriptor) != pipelines_.end();
266 }
267 
268 // |PipelineLibrary|
269 void PipelineLibraryVK::RemovePipelinesWithEntryPoint(
270  std::shared_ptr<const ShaderFunction> function) {
271  Lock lock(pipelines_mutex_);
272 
273  fml::erase_if(pipelines_, [&](auto item) {
274  return item->first.GetEntrypointForStage(function->GetStage())
275  ->IsEqual(*function);
276  });
277 }
278 
279 void PipelineLibraryVK::DidAcquireSurfaceFrame() {
280  if (++frames_acquired_ == 50u) {
281  if (cache_dirty_) {
282  cache_dirty_ = false;
283  PersistPipelineCacheToDisk();
284  }
285  frames_acquired_ = 0;
286  }
287 }
288 
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();
293  if (!cache) {
294  return;
295  }
296  cache->PersistCacheToDisk();
297  });
298 }
299 
300 const std::shared_ptr<PipelineCacheVK>& PipelineLibraryVK::GetPSOCache() const {
301  return pso_cache_;
302 }
303 
304 const std::shared_ptr<fml::ConcurrentTaskRunner>&
305 PipelineLibraryVK::GetWorkerTaskRunner() const {
306  return worker_task_runner_;
307 }
308 
309 PipelineCompileQueue* PipelineLibraryVK::GetPipelineCompileQueue() const {
310  return compile_queue_.get();
311 }
312 
313 } // namespace impeller
A task queue designed for managing compilation of pipeline state objects.
std::vector< std::pair< uint64_t, std::unique_ptr< GenericRenderPipelineHandle > > > pipelines_
ScopedObject< Object > Create(CtorArgs &&... args)
Definition: object.h:161
constexpr vk::DescriptorSetLayoutBinding ToVKDescriptorSetLayoutBinding(const DescriptorSetLayout &layout)
Definition: formats_vk.h:332
int64_t PipelineKey
Definition: pipeline.h:22
Definition: comparable.h:93
std::shared_ptr< PipelineGLES > pipeline
#define VALIDATION_LOG
Definition: validation.h:91