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  // Give all compute shaders a specialization constant entry for the
69  // workgroup/threadgroup size.
70  vk::SpecializationMapEntry specialization_map_entry[1];
71 
72  uint32_t workgroup_size_x = max_wg_size[0];
73  specialization_map_entry[0].constantID = 0;
74  specialization_map_entry[0].offset = 0;
75  specialization_map_entry[0].size = sizeof(uint32_t);
76 
77  vk::SpecializationInfo specialization_info;
78  specialization_info.mapEntryCount = 1;
79  specialization_info.pMapEntries = &specialization_map_entry[0];
80  specialization_info.dataSize = sizeof(uint32_t);
81  specialization_info.pData = &workgroup_size_x;
82 
83  vk::PipelineShaderStageCreateInfo info;
84  info.setStage(vk::ShaderStageFlagBits::eCompute);
85  info.setPName("main");
86  info.setModule(ShaderFunctionVK::Cast(entrypoint.get())->GetModule());
87  info.setPSpecializationInfo(&specialization_info);
88  pipeline_info.setStage(info);
89 
90  //----------------------------------------------------------------------------
91  /// Pipeline Layout a.k.a the descriptor sets and uniforms.
92  ///
93  std::vector<vk::DescriptorSetLayoutBinding> desc_bindings;
94 
95  for (auto layout : desc.GetDescriptorSetLayouts()) {
96  auto vk_desc_layout = ToVKDescriptorSetLayoutBinding(layout);
97  desc_bindings.push_back(vk_desc_layout);
98  }
99 
100  vk::DescriptorSetLayoutCreateInfo descs_layout_info;
101  descs_layout_info.setBindings(desc_bindings);
102 
103  auto [descs_result, descs_layout] =
104  strong_device->GetDevice().createDescriptorSetLayoutUnique(
105  descs_layout_info);
106  if (descs_result != vk::Result::eSuccess) {
107  VALIDATION_LOG << "unable to create uniform descriptors";
108  return nullptr;
109  }
110 
111  ContextVK::SetDebugName(strong_device->GetDevice(), descs_layout.get(),
112  "Descriptor Set Layout " + desc.GetLabel());
113 
114  //----------------------------------------------------------------------------
115  /// Create the pipeline layout.
116  ///
117  vk::PipelineLayoutCreateInfo pipeline_layout_info;
118  pipeline_layout_info.setSetLayouts(descs_layout.get());
119  auto pipeline_layout = strong_device->GetDevice().createPipelineLayoutUnique(
120  pipeline_layout_info);
121  if (pipeline_layout.result != vk::Result::eSuccess) {
122  VALIDATION_LOG << "Could not create pipeline layout for pipeline "
123  << desc.GetLabel() << ": "
124  << vk::to_string(pipeline_layout.result);
125  return nullptr;
126  }
127  pipeline_info.setLayout(pipeline_layout.value.get());
128 
129  //----------------------------------------------------------------------------
130  /// Finally, all done with the setup info. Create the pipeline itself.
131  ///
132  auto pipeline = pso_cache_->CreatePipeline(pipeline_info);
133  if (!pipeline) {
134  VALIDATION_LOG << "Could not create graphics pipeline: " << desc.GetLabel();
135  return nullptr;
136  }
137 
138  ContextVK::SetDebugName(strong_device->GetDevice(), *pipeline_layout.value,
139  "Pipeline Layout " + desc.GetLabel());
140  ContextVK::SetDebugName(strong_device->GetDevice(), *pipeline,
141  "Pipeline " + desc.GetLabel());
142 
143  return std::make_unique<ComputePipelineVK>(
144  device_holder_,
145  weak_from_this(), //
146  desc, //
147  std::move(pipeline), //
148  std::move(pipeline_layout.value), //
149  std::move(descs_layout), //
150  pipeline_key);
151 }
152 
153 // |PipelineLibrary|
154 PipelineFuture<PipelineDescriptor> PipelineLibraryVK::GetPipeline(
155  PipelineDescriptor descriptor,
156  bool async,
157  bool threadsafe) {
158  Lock lock(pipelines_mutex_);
159  if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) {
160  return found->second;
161  }
162 
163  cache_dirty_ = true;
164  if (!IsValid()) {
165  return {
166  descriptor,
167  RealizedFuture<std::shared_ptr<Pipeline<PipelineDescriptor>>>(nullptr)};
168  }
169 
170  auto promise = std::make_shared<
171  NoExceptionPromise<std::shared_ptr<Pipeline<PipelineDescriptor>>>>();
172  auto pipeline_future =
173  PipelineFuture<PipelineDescriptor>{descriptor, promise->get_future()};
174  pipelines_[descriptor] = pipeline_future;
175 
176  auto weak_this = weak_from_this();
177 
178  PipelineKey next_key = pipeline_key_++;
179  auto generation_task = [descriptor, weak_this, promise, next_key]() {
180  auto thiz = weak_this.lock();
181  if (!thiz) {
182  promise->set_value(nullptr);
183  return;
184  }
185 
186  promise->set_value(PipelineVK::Create(
187  descriptor, //
188  PipelineLibraryVK::Cast(*thiz).device_holder_.lock(), //
189  weak_this, //
190  next_key //
191  ));
192  };
193 
194  if (async) {
195  compile_queue_->PostJobForDescriptor(descriptor,
196  std::move(generation_task));
197  } else {
198  generation_task();
199  }
200 
201  return pipeline_future;
202 }
203 
204 // |PipelineLibrary|
205 PipelineFuture<ComputePipelineDescriptor> PipelineLibraryVK::GetPipeline(
206  ComputePipelineDescriptor descriptor,
207  bool async) {
208  Lock lock(pipelines_mutex_);
209  if (auto found = compute_pipelines_.find(descriptor);
210  found != compute_pipelines_.end()) {
211  return found->second;
212  }
213 
214  cache_dirty_ = true;
215  if (!IsValid()) {
216  return {
217  descriptor,
218  RealizedFuture<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>(
219  nullptr)};
220  }
221 
222  auto promise = std::make_shared<
223  std::promise<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>>();
224  auto pipeline_future = PipelineFuture<ComputePipelineDescriptor>{
225  descriptor, promise->get_future()};
226  compute_pipelines_[descriptor] = pipeline_future;
227 
228  auto weak_this = weak_from_this();
229 
230  PipelineKey next_key = pipeline_key_++;
231  auto generation_task = [descriptor, weak_this, promise, next_key]() {
232  auto self = weak_this.lock();
233  if (!self) {
234  promise->set_value(nullptr);
235  VALIDATION_LOG << "Pipeline library was collected before the pipeline "
236  "could be created.";
237  return;
238  }
239 
240  auto pipeline = PipelineLibraryVK::Cast(*self).CreateComputePipeline(
241  descriptor, next_key);
242  if (!pipeline) {
243  promise->set_value(nullptr);
244  VALIDATION_LOG << "Could not create pipeline: " << descriptor.GetLabel();
245  return;
246  }
247 
248  promise->set_value(std::move(pipeline));
249  };
250 
251  if (async) {
252  worker_task_runner_->PostTask(generation_task);
253  } else {
254  generation_task();
255  }
256 
257  return pipeline_future;
258 }
259 
260 // |PipelineLibrary|
261 bool PipelineLibraryVK::HasPipeline(const PipelineDescriptor& descriptor) {
262  Lock lock(pipelines_mutex_);
263  return pipelines_.find(descriptor) != pipelines_.end();
264 }
265 
266 // |PipelineLibrary|
267 void PipelineLibraryVK::RemovePipelinesWithEntryPoint(
268  std::shared_ptr<const ShaderFunction> function) {
269  Lock lock(pipelines_mutex_);
270 
271  fml::erase_if(pipelines_, [&](auto item) {
272  return item->first.GetEntrypointForStage(function->GetStage())
273  ->IsEqual(*function);
274  });
275 }
276 
277 void PipelineLibraryVK::DidAcquireSurfaceFrame() {
278  if (++frames_acquired_ == 50u) {
279  if (cache_dirty_) {
280  cache_dirty_ = false;
281  PersistPipelineCacheToDisk();
282  }
283  frames_acquired_ = 0;
284  }
285 }
286 
287 void PipelineLibraryVK::PersistPipelineCacheToDisk() {
288  worker_task_runner_->PostTask(
289  [weak_cache = decltype(pso_cache_)::weak_type(pso_cache_)]() {
290  auto cache = weak_cache.lock();
291  if (!cache) {
292  return;
293  }
294  cache->PersistCacheToDisk();
295  });
296 }
297 
298 const std::shared_ptr<PipelineCacheVK>& PipelineLibraryVK::GetPSOCache() const {
299  return pso_cache_;
300 }
301 
302 const std::shared_ptr<fml::ConcurrentTaskRunner>&
303 PipelineLibraryVK::GetWorkerTaskRunner() const {
304  return worker_task_runner_;
305 }
306 
307 PipelineCompileQueue* PipelineLibraryVK::GetPipelineCompileQueue() const {
308  return compile_queue_.get();
309 }
310 
311 } // 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:298
int64_t PipelineKey
Definition: pipeline.h:22
Definition: comparable.h:95
#define VALIDATION_LOG
Definition: validation.h:91