| 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 <cstdint>
8
9 #include "flutter/fml/container.h"
10 #include "flutter/fml/trace_event.h"
11 #include "impeller/base/promise.h"
12 #include "impeller/base/validation.h"
13 #include "impeller/renderer/backend/vulkan/context_vk.h"
14 #include "impeller/renderer/backend/vulkan/formats_vk.h"
15 #include "impeller/renderer/backend/vulkan/pipeline_vk.h"
16 #include "impeller/renderer/backend/vulkan/shader_function_vk.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
formats_vk.h
impeller::interop::Create
ScopedObject< Object > Create(CtorArgs &&... args)
Definition: object.h:161
impeller
Definition: allocation.cc:12
impeller::ToVKDescriptorSetLayoutBinding
constexpr vk::DescriptorSetLayoutBinding ToVKDescriptorSetLayoutBinding(const DescriptorSetLayout &layout)
Definition: formats_vk.h:296
impeller::PipelineKey
int64_t PipelineKey
Definition: pipeline.h:21
std
Definition: comparable.h:95
pipeline_library_vk.h
pipeline_vk.h
promise.h
context_vk.h
shader_function_vk.h
validation.h
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:91