| 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
5 #include "impeller/renderer/backend/vulkan/pipeline_library_vk.h"
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"
16 #include "impeller/base/validation.h"
17 #include "impeller/renderer/backend/vulkan/context_vk.h"
18 #include "impeller/renderer/backend/vulkan/formats_vk.h"
19 #include "impeller/renderer/backend/vulkan/pipeline_vk.h"
20 #include "impeller/renderer/backend/vulkan/shader_function_vk.h"
21 #include "impeller/renderer/backend/vulkan/vertex_descriptor_vk.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
45 PipelineLibraryVK::~PipelineLibraryVK() = default;
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 bool async) {
162 Lock lock(pipelines_mutex_);
163 if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) {
164 return found->second;
165 }
166
167 cache_dirty_ = true;
168 if (!IsValid()) {
169 return {
170 descriptor,
171 RealizedFuture<std::shared_ptr<Pipeline<PipelineDescriptor>>>(nullptr)};
172 }
173
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;
179
180 auto weak_this = weak_from_this();
181
182 auto generation_task = [descriptor, weak_this, promise]() {
183 auto thiz = weak_this.lock();
184 if (!thiz) {
185 promise->set_value(nullptr);
186 VALIDATION_LOG << "Pipeline library was collected before the pipeline "
187 "could be created.";
188 return;
189 }
190
191 promise->set_value(PipelineVK::Create(
192 descriptor, //
193 PipelineLibraryVK::Cast(*thiz).device_holder_.lock(), //
194 weak_this //
195 ));
196 };
197
198 if (async) {
199 worker_task_runner_->PostTask(generation_task);
200 } else {
201 generation_task();
202 }
203
204 return pipeline_future;
205 }
206
207 // |PipelineLibrary|
208 PipelineFuture<ComputePipelineDescriptor> PipelineLibraryVK::GetPipeline(
209 ComputePipelineDescriptor descriptor,
210 bool async) {
211 Lock lock(compute_pipelines_mutex_);
212 if (auto found = compute_pipelines_.find(descriptor);
213 found != compute_pipelines_.end()) {
214 return found->second;
215 }
216
217 cache_dirty_ = true;
218 if (!IsValid()) {
219 return {
220 descriptor,
221 RealizedFuture<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>(
222 nullptr)};
223 }
224
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;
230
231 auto weak_this = weak_from_this();
232
233 auto generation_task = [descriptor, weak_this, promise]() {
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 =
243 PipelineLibraryVK::Cast(*self).CreateComputePipeline(descriptor);
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 } // 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:464
formats_vk.h
pipeline_vk.h
validation.h
impeller::PipelineLibraryVK::DidAcquireSurfaceFrame
void DidAcquireSurfaceFrame()
Definition: pipeline_library_vk.cc:279
impeller::PipelineLibraryVK::GetPSOCache
const std::shared_ptr< PipelineCacheVK > & GetPSOCache() const
Definition: pipeline_library_vk.cc:300
pipeline_library_vk.h
impeller::PipelineLibraryVK::GetWorkerTaskRunner
const std::shared_ptr< fml::ConcurrentTaskRunner > & GetWorkerTaskRunner() const
Definition: pipeline_library_vk.cc:305
impeller::ToVKDescriptorSetLayoutBinding
constexpr vk::DescriptorSetLayoutBinding ToVKDescriptorSetLayoutBinding(const DescriptorSetLayout &layout)
Definition: formats_vk.h:296
impeller::ContextVK::SetDebugName
bool SetDebugName(T handle, std::string_view label) const
Definition: context_vk.h:150
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:91
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: allocation.cc:12