12 #include "flutter/fml/container.h"
13 #include "flutter/fml/trace_event.h"
22 #include "vulkan/vulkan_core.h"
23 #include "vulkan/vulkan_enums.hpp"
27 PipelineLibraryVK::PipelineLibraryVK(
28 const std::shared_ptr<DeviceHolder>& 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 supports_framebuffer_fetch_(caps->SupportsFramebufferFetch()),
34 pso_cache_(
std::make_shared<PipelineCacheVK>(
std::move(caps),
36 std::move(cache_directory))),
37 worker_task_runner_(
std::move(worker_task_runner)) {
38 FML_DCHECK(worker_task_runner_);
39 if (!pso_cache_->IsValid() || !worker_task_runner_) {
49 bool PipelineLibraryVK::IsValid()
const {
70 vk::ImageLayout::eUndefined,
84 const vk::Device& device,
86 bool supports_framebuffer_fetch) {
87 std::vector<vk::AttachmentDescription> attachments;
89 std::vector<vk::AttachmentReference> color_refs;
90 std::vector<vk::AttachmentReference> subpass_color_ref;
99 color_refs[bind_point] =
100 vk::AttachmentReference{
static_cast<uint32_t
>(attachments.size()),
101 vk::ImageLayout::eColorAttachmentOptimal};
102 attachments.emplace_back(
105 subpass_color_ref.push_back(vk::AttachmentReference{
106 static_cast<uint32_t
>(0), vk::ImageLayout::eColorAttachmentOptimal});
110 depth_stencil_ref = vk::AttachmentReference{
111 static_cast<uint32_t
>(attachments.size()),
112 vk::ImageLayout::eDepthStencilAttachmentOptimal};
117 depth_stencil_ref = vk::AttachmentReference{
118 static_cast<uint32_t
>(attachments.size()),
119 vk::ImageLayout::eDepthStencilAttachmentOptimal};
124 vk::SubpassDescription subpass_desc;
125 subpass_desc.pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
131 std::vector<vk::SubpassDependency> subpass_dependencies;
132 if (supports_framebuffer_fetch) {
133 subpass_desc.setFlags(vk::SubpassDescriptionFlagBits::
134 eRasterizationOrderAttachmentColorAccessARM);
135 subpass_desc.setInputAttachments(subpass_color_ref);
137 subpass_desc.setColorAttachments(color_refs);
138 subpass_desc.setPDepthStencilAttachment(&depth_stencil_ref);
140 vk::RenderPassCreateInfo render_pass_desc;
141 render_pass_desc.setAttachments(attachments);
142 render_pass_desc.setPSubpasses(&subpass_desc);
143 render_pass_desc.setSubpassCount(1u);
144 render_pass_desc.setDependencies(subpass_dependencies);
146 auto [result, pass] = device.createRenderPassUnique(render_pass_desc);
147 if (result != vk::Result::eSuccess) {
149 << desc.
GetLabel() <<
"'. Error: " << vk::to_string(result);
158 "Compat Render Pass: " + desc.
GetLabel());
160 return std::move(pass);
166 return vk::FrontFace::eClockwise;
168 return vk::FrontFace::eCounterClockwise;
174 vk::PipelineCreationFeedbackEXT feedback;
178 feedback.flags = vk::PipelineCreationFeedbackFlagBits::eValid;
183 std::stringstream& stream,
184 const vk::PipelineCreationFeedbackEXT& feedback) {
185 const auto pipeline_cache_hit =
187 vk::PipelineCreationFeedbackFlagBits::eApplicationPipelineCacheHit;
188 const auto base_pipeline_accl =
190 vk::PipelineCreationFeedbackFlagBits::eBasePipelineAcceleration;
191 auto duration = std::chrono::duration_cast<MillisecondsF>(
192 std::chrono::nanoseconds{feedback.duration});
193 stream <<
"Time: " << duration.count() <<
"ms"
194 <<
" Cache Hit: " <<
static_cast<bool>(pipeline_cache_hit)
195 <<
" Base Accel: " <<
static_cast<bool>(base_pipeline_accl)
196 <<
" Thread: " << std::this_thread::get_id();
201 const vk::PipelineCreationFeedbackCreateInfoEXT& feedback) {
202 std::stringstream stream;
203 stream << std::fixed << std::showpoint << std::setprecision(2);
204 stream << std::endl <<
">>>>>>" << std::endl;
205 stream <<
"Pipeline '" << desc.
GetLabel() <<
"' ";
207 *feedback.pPipelineCreationFeedback);
208 if (feedback.pipelineStageCreationFeedbackCount != 0) {
211 for (
size_t i = 0, count = feedback.pipelineStageCreationFeedbackCount;
213 stream <<
"\tStage " << i + 1 <<
": ";
215 stream, feedback.pPipelineStageCreationFeedbacks[i]);
216 if (i != count - 1) {
220 stream << std::endl <<
"<<<<<<" << std::endl;
221 FML_LOG(ERROR) << stream.str();
226 const vk::PipelineCreationFeedbackCreateInfoEXT& feedback) {
227 static int64_t gPipelineCacheHits = 0;
228 static int64_t gPipelineCacheMisses = 0;
229 static int64_t gPipelines = 0;
230 if (feedback.pPipelineCreationFeedback->flags &
231 vk::PipelineCreationFeedbackFlagBits::eApplicationPipelineCacheHit) {
232 gPipelineCacheHits++;
234 gPipelineCacheMisses++;
237 static constexpr int64_t kImpellerPipelineTraceID = 1988;
238 FML_TRACE_COUNTER(
"impeller",
240 kImpellerPipelineTraceID,
241 "PipelineCacheHits", gPipelineCacheHits,
242 "PipelineCacheMisses", gPipelineCacheMisses,
243 "TotalPipelines", gPipelines
249 const vk::PipelineCreationFeedbackCreateInfoEXT& feedback) {
250 constexpr
bool kReportPipelineCreationFeedbackToLogs =
false;
251 constexpr
bool kReportPipelineCreationFeedbackToTraces =
true;
252 if (kReportPipelineCreationFeedbackToLogs) {
255 if (kReportPipelineCreationFeedbackToTraces) {
260 std::unique_ptr<PipelineVK> PipelineLibraryVK::CreatePipeline(
261 const PipelineDescriptor& desc) {
262 TRACE_EVENT0(
"flutter", __FUNCTION__);
263 vk::StructureChain<vk::GraphicsPipelineCreateInfo,
264 vk::PipelineCreationFeedbackCreateInfoEXT>
267 const auto& supports_pipeline_creation_feedback =
268 pso_cache_->GetCapabilities()->HasOptionalDeviceExtension(
270 if (!supports_pipeline_creation_feedback) {
271 chain.unlink<vk::PipelineCreationFeedbackCreateInfoEXT>();
274 auto& pipeline_info = chain.get<vk::GraphicsPipelineCreateInfo>();
279 vk::PipelineDynamicStateCreateInfo dynamic_create_state_info;
280 std::vector<vk::DynamicState> dynamic_states = {
281 vk::DynamicState::eViewport,
282 vk::DynamicState::eScissor,
283 vk::DynamicState::eStencilReference,
285 dynamic_create_state_info.setDynamicStates(dynamic_states);
286 pipeline_info.setPDynamicState(&dynamic_create_state_info);
291 vk::PipelineViewportStateCreateInfo viewport_state;
292 viewport_state.setViewportCount(1u);
293 viewport_state.setScissorCount(1u);
296 pipeline_info.setPViewportState(&viewport_state);
301 const auto& constants = desc.GetSpecializationConstants();
303 std::vector<std::vector<vk::SpecializationMapEntry>> map_entries(
304 desc.GetStageEntrypoints().size());
305 std::vector<vk::SpecializationInfo> specialization_infos(
306 desc.GetStageEntrypoints().size());
307 std::vector<vk::PipelineShaderStageCreateInfo> shader_stages;
309 size_t entrypoint_count = 0;
310 for (
const auto& entrypoint : desc.GetStageEntrypoints()) {
312 if (!stage.has_value()) {
318 std::vector<vk::SpecializationMapEntry>& entries =
319 map_entries[entrypoint_count];
320 for (
auto i = 0u; i < constants.size(); i++) {
321 vk::SpecializationMapEntry entry;
322 entry.offset = (i *
sizeof(
Scalar));
323 entry.size =
sizeof(
Scalar);
324 entry.constantID = i;
325 entries.emplace_back(entry);
328 vk::SpecializationInfo& specialization_info =
329 specialization_infos[entrypoint_count];
330 specialization_info.setMapEntries(map_entries[entrypoint_count]);
331 specialization_info.setPData(constants.data());
332 specialization_info.setDataSize(
sizeof(
Scalar) * constants.size());
334 vk::PipelineShaderStageCreateInfo info;
335 info.setStage(stage.value());
336 info.setPName(
"main");
339 info.setPSpecializationInfo(&specialization_info);
340 shader_stages.push_back(info);
343 pipeline_info.setStages(shader_stages);
348 vk::PipelineRasterizationStateCreateInfo rasterization_state;
349 rasterization_state.setFrontFace(
ToVKFrontFace(desc.GetWindingOrder()));
351 rasterization_state.setPolygonMode(
ToVKPolygonMode(desc.GetPolygonMode()));
352 rasterization_state.setLineWidth(1.0f);
353 rasterization_state.setDepthClampEnable(
false);
354 rasterization_state.setRasterizerDiscardEnable(
false);
355 pipeline_info.setPRasterizationState(&rasterization_state);
360 vk::PipelineMultisampleStateCreateInfo multisample_state;
361 multisample_state.setRasterizationSamples(
363 pipeline_info.setPMultisampleState(&multisample_state);
367 vk::PipelineInputAssemblyStateCreateInfo input_assembly;
369 input_assembly.setTopology(topology);
370 pipeline_info.setPInputAssemblyState(&input_assembly);
374 std::vector<vk::PipelineColorBlendAttachmentState> attachment_blend_state;
375 for (
const auto& color_desc : desc.GetColorAttachmentDescriptors()) {
379 attachment_blend_state.push_back(
382 vk::PipelineColorBlendStateCreateInfo blend_state;
383 blend_state.setAttachments(attachment_blend_state);
384 pipeline_info.setPColorBlendState(&blend_state);
386 std::shared_ptr<DeviceHolder> strong_device = device_holder_.lock();
387 if (!strong_device) {
392 strong_device->GetDevice(), desc, supports_framebuffer_fetch_);
394 pipeline_info.setBasePipelineHandle(VK_NULL_HANDLE);
395 pipeline_info.setSubpass(0);
396 pipeline_info.setRenderPass(render_pass.get());
404 std::vector<vk::VertexInputAttributeDescription> attr_descs;
405 std::vector<vk::VertexInputBindingDescription> buffer_descs;
407 const auto& stage_inputs = desc.GetVertexDescriptor()->GetStageInputs();
408 const auto& stage_buffer_layouts =
409 desc.GetVertexDescriptor()->GetStageLayouts();
410 for (
const ShaderStageIOSlot& stage_in : stage_inputs) {
411 vk::VertexInputAttributeDescription attr_desc;
412 attr_desc.setBinding(stage_in.binding);
413 attr_desc.setLocation(stage_in.location);
415 attr_desc.setOffset(stage_in.offset);
416 attr_descs.push_back(attr_desc);
418 for (
const ShaderStageBufferLayout& layout : stage_buffer_layouts) {
419 vk::VertexInputBindingDescription binding_description;
420 binding_description.setBinding(layout.binding);
421 binding_description.setInputRate(vk::VertexInputRate::eVertex);
422 binding_description.setStride(layout.stride);
423 buffer_descs.push_back(binding_description);
426 vk::PipelineVertexInputStateCreateInfo vertex_input_state;
427 vertex_input_state.setVertexAttributeDescriptions(attr_descs);
428 vertex_input_state.setVertexBindingDescriptions(buffer_descs);
430 pipeline_info.setPVertexInputState(&vertex_input_state);
435 std::vector<vk::DescriptorSetLayoutBinding> desc_bindings;
437 for (
auto layout : desc.GetVertexDescriptor()->GetDescriptorSetLayouts()) {
439 desc_bindings.push_back(vk_desc_layout);
442 vk::DescriptorSetLayoutCreateInfo descs_layout_info;
443 descs_layout_info.setBindings(desc_bindings);
445 auto [descs_result, descs_layout] =
446 strong_device->GetDevice().createDescriptorSetLayoutUnique(
448 if (descs_result != vk::Result::eSuccess) {
454 "Descriptor Set Layout " + desc.GetLabel());
459 vk::PipelineLayoutCreateInfo pipeline_layout_info;
460 pipeline_layout_info.setSetLayouts(descs_layout.get());
461 auto pipeline_layout = strong_device->GetDevice().createPipelineLayoutUnique(
462 pipeline_layout_info);
463 if (pipeline_layout.result != vk::Result::eSuccess) {
464 VALIDATION_LOG <<
"Could not create pipeline layout for pipeline "
465 << desc.GetLabel() <<
": "
466 << vk::to_string(pipeline_layout.result);
469 pipeline_info.setLayout(pipeline_layout.value.get());
475 desc.GetDepthStencilAttachmentDescriptor(),
476 desc.GetFrontStencilAttachmentDescriptor(),
477 desc.GetBackStencilAttachmentDescriptor());
478 pipeline_info.setPDepthStencilState(&depth_stencil_state);
484 auto& feedback = chain.get<vk::PipelineCreationFeedbackCreateInfoEXT>();
486 std::vector<vk::PipelineCreationFeedbackEXT> stage_feedbacks(
488 feedback.setPPipelineCreationFeedback(&pipeline_feedback);
489 feedback.setPipelineStageCreationFeedbacks(stage_feedbacks);
494 auto pipeline = pso_cache_->CreatePipeline(pipeline_info);
496 VALIDATION_LOG <<
"Could not create graphics pipeline: " << desc.GetLabel();
500 if (supports_pipeline_creation_feedback) {
505 "Pipeline Layout " + desc.GetLabel());
507 "Pipeline " + desc.GetLabel());
509 return std::make_unique<PipelineVK>(device_holder_,
513 std::move(render_pass),
514 std::move(pipeline_layout.value),
515 std::move(descs_layout)
519 std::unique_ptr<ComputePipelineVK> PipelineLibraryVK::CreateComputePipeline(
520 const ComputePipelineDescriptor& desc) {
521 TRACE_EVENT0(
"flutter", __FUNCTION__);
522 vk::ComputePipelineCreateInfo pipeline_info;
527 const auto entrypoint = desc.GetStageEntrypoint();
533 std::shared_ptr<DeviceHolder> strong_device = device_holder_.lock();
534 if (!strong_device) {
537 auto device_properties = strong_device->GetPhysicalDevice().getProperties();
538 auto max_wg_size = device_properties.limits.maxComputeWorkGroupSize;
542 vk::SpecializationMapEntry specialization_map_entry[1];
544 uint32_t workgroup_size_x = max_wg_size[0];
545 specialization_map_entry[0].constantID = 0;
546 specialization_map_entry[0].offset = 0;
547 specialization_map_entry[0].size =
sizeof(uint32_t);
549 vk::SpecializationInfo specialization_info;
550 specialization_info.mapEntryCount = 1;
551 specialization_info.pMapEntries = &specialization_map_entry[0];
552 specialization_info.dataSize =
sizeof(uint32_t);
553 specialization_info.pData = &workgroup_size_x;
555 vk::PipelineShaderStageCreateInfo info;
556 info.setStage(vk::ShaderStageFlagBits::eCompute);
557 info.setPName(
"main");
559 info.setPSpecializationInfo(&specialization_info);
560 pipeline_info.setStage(info);
565 std::vector<vk::DescriptorSetLayoutBinding> desc_bindings;
567 for (
auto layout : desc.GetDescriptorSetLayouts()) {
569 desc_bindings.push_back(vk_desc_layout);
572 vk::DescriptorSetLayoutCreateInfo descs_layout_info;
573 descs_layout_info.setBindings(desc_bindings);
575 auto [descs_result, descs_layout] =
576 strong_device->GetDevice().createDescriptorSetLayoutUnique(
578 if (descs_result != vk::Result::eSuccess) {
584 "Descriptor Set Layout " + desc.GetLabel());
589 vk::PipelineLayoutCreateInfo pipeline_layout_info;
590 pipeline_layout_info.setSetLayouts(descs_layout.get());
591 auto pipeline_layout = strong_device->GetDevice().createPipelineLayoutUnique(
592 pipeline_layout_info);
593 if (pipeline_layout.result != vk::Result::eSuccess) {
594 VALIDATION_LOG <<
"Could not create pipeline layout for pipeline "
595 << desc.GetLabel() <<
": "
596 << vk::to_string(pipeline_layout.result);
599 pipeline_info.setLayout(pipeline_layout.value.get());
604 auto pipeline = pso_cache_->CreatePipeline(pipeline_info);
606 VALIDATION_LOG <<
"Could not create graphics pipeline: " << desc.GetLabel();
611 "Pipeline Layout " + desc.GetLabel());
613 "Pipeline " + desc.GetLabel());
615 return std::make_unique<ComputePipelineVK>(
620 std::move(pipeline_layout.value),
621 std::move(descs_layout)
626 PipelineFuture<PipelineDescriptor> PipelineLibraryVK::GetPipeline(
627 PipelineDescriptor descriptor) {
628 Lock lock(pipelines_mutex_);
629 if (
auto found = pipelines_.find(descriptor); found != pipelines_.end()) {
630 return found->second;
636 RealizedFuture<std::shared_ptr<Pipeline<PipelineDescriptor>>>(
nullptr)};
639 auto promise = std::make_shared<
640 std::promise<std::shared_ptr<Pipeline<PipelineDescriptor>>>>();
641 auto pipeline_future =
642 PipelineFuture<PipelineDescriptor>{descriptor, promise->get_future()};
643 pipelines_[descriptor] = pipeline_future;
645 auto weak_this = weak_from_this();
647 worker_task_runner_->PostTask([descriptor, weak_this, promise]() {
648 auto thiz = weak_this.lock();
650 promise->set_value(
nullptr);
651 VALIDATION_LOG <<
"Pipeline library was collected before the pipeline "
658 promise->set_value(
nullptr);
659 VALIDATION_LOG <<
"Could not create pipeline: " << descriptor.GetLabel();
663 promise->set_value(std::move(pipeline));
666 return pipeline_future;
670 PipelineFuture<ComputePipelineDescriptor> PipelineLibraryVK::GetPipeline(
671 ComputePipelineDescriptor descriptor) {
672 Lock lock(compute_pipelines_mutex_);
673 if (
auto found = compute_pipelines_.find(descriptor);
674 found != compute_pipelines_.end()) {
675 return found->second;
681 RealizedFuture<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>(
685 auto promise = std::make_shared<
686 std::promise<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>>();
687 auto pipeline_future = PipelineFuture<ComputePipelineDescriptor>{
688 descriptor, promise->get_future()};
689 compute_pipelines_[descriptor] = pipeline_future;
691 auto weak_this = weak_from_this();
693 worker_task_runner_->PostTask([descriptor, weak_this, promise]() {
694 auto self = weak_this.lock();
696 promise->set_value(
nullptr);
697 VALIDATION_LOG <<
"Pipeline library was collected before the pipeline "
705 promise->set_value(
nullptr);
706 VALIDATION_LOG <<
"Could not create pipeline: " << descriptor.GetLabel();
710 promise->set_value(std::move(pipeline));
713 return pipeline_future;
717 void PipelineLibraryVK::RemovePipelinesWithEntryPoint(
718 std::shared_ptr<const ShaderFunction>
function) {
719 Lock lock(pipelines_mutex_);
721 fml::erase_if(pipelines_, [&](
auto item) {
722 return item->first.GetEntrypointForStage(function->GetStage())
723 ->IsEqual(*
function);
728 if (++frames_acquired_ == 50u) {
729 PersistPipelineCacheToDisk();
733 void PipelineLibraryVK::PersistPipelineCacheToDisk() {
734 worker_task_runner_->PostTask(
735 [weak_cache = decltype(pso_cache_)::weak_type(pso_cache_)]() {
736 auto cache = weak_cache.lock();
740 cache->PersistCacheToDisk();