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 <chrono>
8 #include <cstdint>
9 #include <optional>
10 #include <sstream>
11 
12 #include "flutter/fml/container.h"
13 #include "flutter/fml/trace_event.h"
14 #include "impeller/base/promise.h"
15 #include "impeller/base/timing.h"
22 #include "vulkan/vulkan_core.h"
23 #include "vulkan/vulkan_enums.hpp"
24 
25 namespace impeller {
26 
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),
34  device_holder,
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_) {
39  return;
40  }
41 
42  is_valid_ = true;
43 }
44 
46 
47 // |PipelineLibrary|
48 bool PipelineLibraryVK::IsValid() const {
49  return is_valid_;
50 }
51 
52 std::unique_ptr<ComputePipelineVK> PipelineLibraryVK::CreateComputePipeline(
53  const ComputePipelineDescriptor& desc) {
54  TRACE_EVENT0("flutter", __FUNCTION__);
55  vk::ComputePipelineCreateInfo pipeline_info;
56 
57  //----------------------------------------------------------------------------
58  /// Shader Stage
59  ///
60  const auto entrypoint = desc.GetStageEntrypoint();
61  if (!entrypoint) {
62  VALIDATION_LOG << "Compute shader is missing an entrypoint.";
63  return nullptr;
64  }
65 
66  std::shared_ptr<DeviceHolderVK> strong_device = device_holder_.lock();
67  if (!strong_device) {
68  return nullptr;
69  }
70  auto device_properties = strong_device->GetPhysicalDevice().getProperties();
71  auto max_wg_size = device_properties.limits.maxComputeWorkGroupSize;
72 
73  // Give all compute shaders a specialization constant entry for the
74  // workgroup/threadgroup size.
75  vk::SpecializationMapEntry specialization_map_entry[1];
76 
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);
81 
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;
87 
88  vk::PipelineShaderStageCreateInfo info;
89  info.setStage(vk::ShaderStageFlagBits::eCompute);
90  info.setPName("main");
91  info.setModule(ShaderFunctionVK::Cast(entrypoint.get())->GetModule());
92  info.setPSpecializationInfo(&specialization_info);
93  pipeline_info.setStage(info);
94 
95  //----------------------------------------------------------------------------
96  /// Pipeline Layout a.k.a the descriptor sets and uniforms.
97  ///
98  std::vector<vk::DescriptorSetLayoutBinding> desc_bindings;
99 
100  for (auto layout : desc.GetDescriptorSetLayouts()) {
101  auto vk_desc_layout = ToVKDescriptorSetLayoutBinding(layout);
102  desc_bindings.push_back(vk_desc_layout);
103  }
104 
105  vk::DescriptorSetLayoutCreateInfo descs_layout_info;
106  descs_layout_info.setBindings(desc_bindings);
107 
108  auto [descs_result, descs_layout] =
109  strong_device->GetDevice().createDescriptorSetLayoutUnique(
110  descs_layout_info);
111  if (descs_result != vk::Result::eSuccess) {
112  VALIDATION_LOG << "unable to create uniform descriptors";
113  return nullptr;
114  }
115 
116  ContextVK::SetDebugName(strong_device->GetDevice(), descs_layout.get(),
117  "Descriptor Set Layout " + desc.GetLabel());
118 
119  //----------------------------------------------------------------------------
120  /// Create the pipeline layout.
121  ///
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);
130  return nullptr;
131  }
132  pipeline_info.setLayout(pipeline_layout.value.get());
133 
134  //----------------------------------------------------------------------------
135  /// Finally, all done with the setup info. Create the pipeline itself.
136  ///
137  auto pipeline = pso_cache_->CreatePipeline(pipeline_info);
138  if (!pipeline) {
139  VALIDATION_LOG << "Could not create graphics pipeline: " << desc.GetLabel();
140  return nullptr;
141  }
142 
143  ContextVK::SetDebugName(strong_device->GetDevice(), *pipeline_layout.value,
144  "Pipeline Layout " + desc.GetLabel());
145  ContextVK::SetDebugName(strong_device->GetDevice(), *pipeline,
146  "Pipeline " + desc.GetLabel());
147 
148  return std::make_unique<ComputePipelineVK>(
149  device_holder_,
150  weak_from_this(), //
151  desc, //
152  std::move(pipeline), //
153  std::move(pipeline_layout.value), //
154  std::move(descs_layout) //
155  );
156 }
157 
158 // |PipelineLibrary|
159 PipelineFuture<PipelineDescriptor> PipelineLibraryVK::GetPipeline(
160  PipelineDescriptor descriptor) {
161  Lock lock(pipelines_mutex_);
162  if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) {
163  return found->second;
164  }
165 
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  worker_task_runner_->PostTask([descriptor, weak_this, promise]() {
181  auto thiz = weak_this.lock();
182  if (!thiz) {
183  promise->set_value(nullptr);
184  VALIDATION_LOG << "Pipeline library was collected before the pipeline "
185  "could be created.";
186  return;
187  }
188 
189  promise->set_value(PipelineVK::Create(
190  descriptor, //
191  PipelineLibraryVK::Cast(*thiz).device_holder_.lock(), //
192  weak_this //
193  ));
194  });
195 
196  return pipeline_future;
197 }
198 
199 // |PipelineLibrary|
200 PipelineFuture<ComputePipelineDescriptor> PipelineLibraryVK::GetPipeline(
201  ComputePipelineDescriptor descriptor) {
202  Lock lock(compute_pipelines_mutex_);
203  if (auto found = compute_pipelines_.find(descriptor);
204  found != compute_pipelines_.end()) {
205  return found->second;
206  }
207 
208  if (!IsValid()) {
209  return {
210  descriptor,
211  RealizedFuture<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>(
212  nullptr)};
213  }
214 
215  auto promise = std::make_shared<
216  std::promise<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>>();
217  auto pipeline_future = PipelineFuture<ComputePipelineDescriptor>{
218  descriptor, promise->get_future()};
219  compute_pipelines_[descriptor] = pipeline_future;
220 
221  auto weak_this = weak_from_this();
222 
223  worker_task_runner_->PostTask([descriptor, weak_this, promise]() {
224  auto self = weak_this.lock();
225  if (!self) {
226  promise->set_value(nullptr);
227  VALIDATION_LOG << "Pipeline library was collected before the pipeline "
228  "could be created.";
229  return;
230  }
231 
232  auto pipeline =
233  PipelineLibraryVK::Cast(*self).CreateComputePipeline(descriptor);
234  if (!pipeline) {
235  promise->set_value(nullptr);
236  VALIDATION_LOG << "Could not create pipeline: " << descriptor.GetLabel();
237  return;
238  }
239 
240  promise->set_value(std::move(pipeline));
241  });
242 
243  return pipeline_future;
244 }
245 
246 // |PipelineLibrary|
247 void PipelineLibraryVK::RemovePipelinesWithEntryPoint(
248  std::shared_ptr<const ShaderFunction> function) {
249  Lock lock(pipelines_mutex_);
250 
251  fml::erase_if(pipelines_, [&](auto item) {
252  return item->first.GetEntrypointForStage(function->GetStage())
253  ->IsEqual(*function);
254  });
255 }
256 
258  if (++frames_acquired_ == 50u) {
259  PersistPipelineCacheToDisk();
260  }
261 }
262 
263 void PipelineLibraryVK::PersistPipelineCacheToDisk() {
264  worker_task_runner_->PostTask(
265  [weak_cache = decltype(pso_cache_)::weak_type(pso_cache_)]() {
266  auto cache = weak_cache.lock();
267  if (!cache) {
268  return;
269  }
270  cache->PersistCacheToDisk();
271  });
272 }
273 
274 const std::shared_ptr<PipelineCacheVK>& PipelineLibraryVK::GetPSOCache() const {
275  return pso_cache_;
276 }
277 
278 const std::shared_ptr<fml::ConcurrentTaskRunner>&
280  return worker_task_runner_;
281 }
282 
283 } // namespace impeller
timing.h
impeller::PipelineLibraryVK::~PipelineLibraryVK
~PipelineLibraryVK() override
impeller::PipelineVK::Create
static std::unique_ptr< PipelineVK > Create(const PipelineDescriptor &desc, const std::shared_ptr< DeviceHolderVK > &device_holder, const std::weak_ptr< PipelineLibrary > &weak_library, std::shared_ptr< SamplerVK > immutable_sampler={})
Definition: pipeline_vk.cc:167
formats_vk.h
pipeline_vk.h
validation.h
impeller::PipelineLibraryVK::DidAcquireSurfaceFrame
void DidAcquireSurfaceFrame()
Definition: pipeline_library_vk.cc:257
impeller::PipelineLibraryVK::GetPSOCache
const std::shared_ptr< PipelineCacheVK > & GetPSOCache() const
Definition: pipeline_library_vk.cc:274
pipeline_library_vk.h
impeller::PipelineLibraryVK::GetWorkerTaskRunner
const std::shared_ptr< fml::ConcurrentTaskRunner > & GetWorkerTaskRunner() const
Definition: pipeline_library_vk.cc:279
impeller::ToVKDescriptorSetLayoutBinding
constexpr vk::DescriptorSetLayoutBinding ToVKDescriptorSetLayoutBinding(const DescriptorSetLayout &layout)
Definition: formats_vk.h:291
impeller::ContextVK::SetDebugName
bool SetDebugName(T handle, std::string_view label) const
Definition: context_vk.h:106
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
promise.h
std
Definition: comparable.h:95
impeller::BackendCast< ShaderFunctionVK, ShaderFunction >::Cast
static ShaderFunctionVK & Cast(ShaderFunction &base)
Definition: backend_cast.h:13
vertex_descriptor_vk.h
context_vk.h
shader_function_vk.h
impeller
Definition: aiks_blur_unittests.cc:20