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