| 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 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
impeller::PipelineCompileQueue
A task queue designed for managing compilation of pipeline state objects.
Definition: pipeline_compile_queue.h:43
pipelines_
std::vector< std::pair< uint64_t, std::unique_ptr< GenericRenderPipelineHandle > > > pipelines_
Definition: content_context.cc:80
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:298
impeller::PipelineKey
int64_t PipelineKey
Definition: pipeline.h:22
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