| Flutter Impeller |

descriptor_pool_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/descriptor_pool_vk.h"
6
7 #include <optional>
8
9 #include "impeller/base/validation.h"
10 #include "impeller/renderer/backend/vulkan/resource_manager_vk.h"
11 #include "vulkan/vulkan_enums.hpp"
12 #include "vulkan/vulkan_handles.hpp"
13
14 namespace impeller {
15
16 struct DescriptorPoolSize {
17 size_t buffer_bindings;
18 size_t texture_bindings;
19 size_t storage_bindings;
20 size_t subpass_bindings;
21 };
22
23 /// Descriptor pools are always allocated with the following sizes.
24 static const constexpr DescriptorPoolSize kDefaultBindingSize =
25 DescriptorPoolSize{
26 .buffer_bindings = 512u, // Buffer Bindings
27 .texture_bindings = 256u, // Texture Bindings
28 .storage_bindings = 32,
29 .subpass_bindings = 4u // Subpass Bindings
30 };
31
32 // Holds the command pool in a background thread, recyling it when not in use.
33 class BackgroundDescriptorPoolVK final {
34 public:
35 BackgroundDescriptorPoolVK(BackgroundDescriptorPoolVK&&) = default;
36
37 explicit BackgroundDescriptorPoolVK(
38 vk::UniqueDescriptorPool&& pool,
39 std::weak_ptr<DescriptorPoolRecyclerVK> recycler)
40 : pool_(std::move(pool)), recycler_(std::move(recycler)) {}
41
42 ~BackgroundDescriptorPoolVK() {
43 auto const recycler = recycler_.lock();
44
45 // Not only does this prevent recycling when the context is being destroyed,
46 // but it also prevents the destructor from effectively being called twice;
47 // once for the original BackgroundCommandPoolVK() and once for the moved
48 // BackgroundCommandPoolVK().
49 if (!recycler) {
50 return;
51 }
52
53 recycler->Reclaim(std::move(pool_));
54 }
55
56 private:
57 BackgroundDescriptorPoolVK(const BackgroundDescriptorPoolVK&) = delete;
58
59 BackgroundDescriptorPoolVK& operator=(const BackgroundDescriptorPoolVK&) =
60 delete;
61
62 vk::UniqueDescriptorPool pool_;
63 uint32_t allocated_capacity_;
64 std::weak_ptr<DescriptorPoolRecyclerVK> recycler_;
65 };
66
67 DescriptorPoolVK::DescriptorPoolVK(std::weak_ptr<const ContextVK> context)
68 : context_(std::move(context)) {}
69
70 DescriptorPoolVK::~DescriptorPoolVK() {
71 if (pools_.empty()) {
72 return;
73 }
74
75 auto const context = context_.lock();
76 if (!context) {
77 return;
78 }
79 auto const recycler = context->GetDescriptorPoolRecycler();
80 if (!recycler) {
81 return;
82 }
83
84 for (auto i = 0u; i < pools_.size(); i++) {
85 auto reset_pool_when_dropped =
86 BackgroundDescriptorPoolVK(std::move(pools_[i]), recycler);
87
88 UniqueResourceVKT<BackgroundDescriptorPoolVK> pool(
89 context->GetResourceManager(), std::move(reset_pool_when_dropped));
90 }
91 pools_.clear();
92 }
93
94 fml::StatusOr<vk::DescriptorSet> DescriptorPoolVK::AllocateDescriptorSets(
95 const vk::DescriptorSetLayout& layout,
96 const ContextVK& context_vk) {
97 if (pools_.empty()) {
98 CreateNewPool(context_vk);
99 }
100
101 vk::DescriptorSetAllocateInfo set_info;
102 set_info.setDescriptorPool(pools_.back().get());
103 set_info.setPSetLayouts(&layout);
104 set_info.setDescriptorSetCount(1);
105
106 vk::DescriptorSet set;
107 auto result = context_vk.GetDevice().allocateDescriptorSets(&set_info, &set);
108 if (result == vk::Result::eErrorOutOfPoolMemory) {
109 // If the pool ran out of memory, we need to create a new pool.
110 CreateNewPool(context_vk);
111 set_info.setDescriptorPool(pools_.back().get());
112 result = context_vk.GetDevice().allocateDescriptorSets(&set_info, &set);
113 }
114
115 if (result != vk::Result::eSuccess) {
116 VALIDATION_LOG << "Could not allocate descriptor sets: "
117 << vk::to_string(result);
118 return fml::Status(fml::StatusCode::kUnknown, "");
119 }
120 return set;
121 }
122
123 fml::Status DescriptorPoolVK::CreateNewPool(const ContextVK& context_vk) {
124 auto new_pool = context_vk.GetDescriptorPoolRecycler()->Get();
125 if (!new_pool) {
126 return fml::Status(fml::StatusCode::kUnknown,
127 "Failed to create descriptor pool");
128 }
129 pools_.emplace_back(std::move(new_pool));
130 return fml::Status();
131 }
132
133 void DescriptorPoolRecyclerVK::Reclaim(vk::UniqueDescriptorPool&& pool) {
134 // Reset the pool on a background thread.
135 auto strong_context = context_.lock();
136 if (!strong_context) {
137 return;
138 }
139 auto device = strong_context->GetDevice();
140 device.resetDescriptorPool(pool.get());
141
142 // Move the pool to the recycled list.
143 Lock recycled_lock(recycled_mutex_);
144
145 if (recycled_.size() < kMaxRecycledPools) {
146 recycled_.push_back(std::move(pool));
147 return;
148 }
149 }
150
151 vk::UniqueDescriptorPool DescriptorPoolRecyclerVK::Get() {
152 // Recycle a pool with a matching minumum capcity if it is available.
153 auto recycled_pool = Reuse();
154 if (recycled_pool.has_value()) {
155 return std::move(recycled_pool.value());
156 }
157 return Create();
158 }
159
160 vk::UniqueDescriptorPool DescriptorPoolRecyclerVK::Create() {
161 auto strong_context = context_.lock();
162 if (!strong_context) {
163 VALIDATION_LOG << "Unable to create a descriptor pool";
164 return {};
165 }
166
167 std::vector<vk::DescriptorPoolSize> pools = {
168 vk::DescriptorPoolSize{vk::DescriptorType::eCombinedImageSampler,
169 kDefaultBindingSize.texture_bindings},
170 vk::DescriptorPoolSize{vk::DescriptorType::eUniformBuffer,
171 kDefaultBindingSize.buffer_bindings},
172 vk::DescriptorPoolSize{vk::DescriptorType::eStorageBuffer,
173 kDefaultBindingSize.storage_bindings},
174 vk::DescriptorPoolSize{vk::DescriptorType::eInputAttachment,
175 kDefaultBindingSize.subpass_bindings}};
176 vk::DescriptorPoolCreateInfo pool_info;
177 pool_info.setMaxSets(kDefaultBindingSize.texture_bindings +
178 kDefaultBindingSize.buffer_bindings +
179 kDefaultBindingSize.storage_bindings +
180 kDefaultBindingSize.subpass_bindings);
181 pool_info.setPoolSizes(pools);
182 auto [result, pool] =
183 strong_context->GetDevice().createDescriptorPoolUnique(pool_info);
184 if (result != vk::Result::eSuccess) {
185 VALIDATION_LOG << "Unable to create a descriptor pool";
186 }
187 return std::move(pool);
188 }
189
190 std::optional<vk::UniqueDescriptorPool> DescriptorPoolRecyclerVK::Reuse() {
191 Lock lock(recycled_mutex_);
192 if (recycled_.empty()) {
193 return std::nullopt;
194 }
195
196 auto recycled = std::move(recycled_[recycled_.size() - 1]);
197 recycled_.pop_back();
198 return recycled;
199 }
200
201 } // namespace impeller
impeller::DescriptorPoolRecyclerVK::Reclaim
void Reclaim(vk::UniqueDescriptorPool &&pool)
Returns the descriptor pool to be reset on a background thread.
Definition: descriptor_pool_vk.cc:133
impeller::DescriptorPoolVK::DescriptorPoolVK
DescriptorPoolVK(std::weak_ptr< const ContextVK > context)
Definition: descriptor_pool_vk.cc:67
impeller::UniqueResourceVKT
A unique handle to a resource which will be reclaimed by the specified resource manager.
Definition: resource_manager_vk.h:143
impeller::DescriptorPoolVK::~DescriptorPoolVK
~DescriptorPoolVK()
Definition: descriptor_pool_vk.cc:70
impeller::Lock
Definition: thread.h:72
impeller::BackgroundDescriptorPoolVK::BackgroundDescriptorPoolVK
BackgroundDescriptorPoolVK(BackgroundDescriptorPoolVK &&)=default
validation.h
impeller::DescriptorPoolSize::buffer_bindings
size_t buffer_bindings
Definition: descriptor_pool_vk.cc:17
impeller::DescriptorPoolRecyclerVK::kMaxRecycledPools
static constexpr size_t kMaxRecycledPools
The maximum number of descriptor pools this recycler will hold onto.
Definition: descriptor_pool_vk.h:57
impeller::DescriptorPoolSize::subpass_bindings
size_t subpass_bindings
Definition: descriptor_pool_vk.cc:20
impeller::BackgroundDescriptorPoolVK::~BackgroundDescriptorPoolVK
~BackgroundDescriptorPoolVK()
Definition: descriptor_pool_vk.cc:42
impeller::ContextVK::GetDescriptorPoolRecycler
std::shared_ptr< DescriptorPoolRecyclerVK > GetDescriptorPoolRecycler() const
Definition: context_vk.cc:584
impeller::DescriptorPoolRecyclerVK::Get
vk::UniqueDescriptorPool Get()
Gets a descriptor pool.
Definition: descriptor_pool_vk.cc:151
impeller::ContextVK
Definition: context_vk.h:42
impeller::DescriptorPoolSize
Definition: descriptor_pool_vk.cc:16
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:91
impeller::BackgroundDescriptorPoolVK::BackgroundDescriptorPoolVK
BackgroundDescriptorPoolVK(vk::UniqueDescriptorPool &&pool, std::weak_ptr< DescriptorPoolRecyclerVK > recycler)
Definition: descriptor_pool_vk.cc:37
impeller::DescriptorPoolSize::storage_bindings
size_t storage_bindings
Definition: descriptor_pool_vk.cc:19
resource_manager_vk.h
std
Definition: comparable.h:95
impeller::ContextVK::GetDevice
const vk::Device & GetDevice() const
Definition: context_vk.cc:530
descriptor_pool_vk.h
impeller::DescriptorPoolVK::AllocateDescriptorSets
fml::StatusOr< vk::DescriptorSet > AllocateDescriptorSets(const vk::DescriptorSetLayout &layout, const ContextVK &context_vk)
Definition: descriptor_pool_vk.cc:94
impeller::DescriptorPoolSize::texture_bindings
size_t texture_bindings
Definition: descriptor_pool_vk.cc:18
impeller
Definition: allocation.cc:12
impeller::BackgroundDescriptorPoolVK
Definition: descriptor_pool_vk.cc:33
impeller::kDefaultBindingSize
static const constexpr DescriptorPoolSize kDefaultBindingSize
Descriptor pools are always allocated with the following sizes.
Definition: descriptor_pool_vk.cc:24