Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
descriptor_allocator.cpp
Go to the documentation of this file.
1
2
4
5 namespace vke {
6
7 namespace
8 {
9
10 inline bool IsMemoryError(const VkResult errorResult) noexcept
11 {
12 return errorResult == VK_ERROR_FRAGMENTED_POOL ||
13 errorResult == VK_ERROR_OUT_OF_POOL_MEMORY;
14 }
15 }
16
18 {
20 impl->_device = device;
21 impl->_frameIndex = 0;
22 impl->_maxFrames = nFrames;
23 for ( Divide::I32 i = 0; i < nFrames; i++)
24 {
25 impl->_descriptorPools.push_back(std::make_unique<PoolStorage>());
26 }
27
28 return impl;
29 }
30
32 {
34 if (implPool)
35 {
36 implPool->ReturnAllocator(*this, false);
37 }
38 }
39
41 {
42 Return();
43
44 vkPool = other.vkPool;
45 poolIdx = other.poolIdx;
46 ownerPool = other.ownerPool;
47
48 other.ownerPool = nullptr;
49 other.poolIdx = -1;
50 other.vkPool = {};
51 }
52
54 {
55 Return();
56
57 vkPool = other.vkPool;
58 poolIdx = other.poolIdx;
59 ownerPool = other.ownerPool;
60
61 other.ownerPool = nullptr;
62 other.poolIdx = -1;
63 other.vkPool = {};
64
65 return *this;
66 }
67
69 {
71
72 if (implPool)
73 {
74 implPool->ReturnAllocator(*this, false);
75 }
76
77 vkPool = {};
78 poolIdx = -1;
79 ownerPool = nullptr;
80 }
81
82 bool DescriptorAllocatorHandle::Allocate(const VkDescriptorSetLayout& layout, VkDescriptorSet& builtSet, Divide::U8 retryCount)
83 {
85
87
88 VkDescriptorSetAllocateInfo allocInfo{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
89 allocInfo.descriptorPool = vkPool;
90 allocInfo.descriptorSetCount = 1;
91 allocInfo.pSetLayouts = &layout;
92
93 const VkResult result = vkAllocateDescriptorSets(implPool->_device, &allocInfo, &builtSet);
94 if (result != VK_SUCCESS)
95 {
96 //we reallocate pools on memory error
97 if (IsMemoryError(result))
98 {
99 //out of space need reallocate
100 implPool->ReturnAllocator(*this, true);
101
102 DescriptorAllocatorHandle newHandle = implPool->GetAllocator();
103
104 vkPool = newHandle.vkPool;
105 poolIdx = newHandle.poolIdx;
106
107 newHandle.vkPool = {};
108 newHandle.poolIdx = -1;
109 newHandle.ownerPool = nullptr;
110
111 Divide::DIVIDE_ASSERT( retryCount < (Divide::U8_MAX - 1u), "DescriptorAllocatorHandle::Allocate failed to allocate descriptor sets: memory error!");
112 //could be good idea to avoid infinite loop here
113 return Allocate(layout, builtSet, retryCount + 1);
114 }
115 else
116 {
117 //stuff is truly broken
118 Divide::DIVIDE_UNEXPECTED_CALL();
119 return false;
120 }
121 }
122
123 return true;
124 }
125
126 VkDescriptorPool DescriptorAllocatorPool::createPool( Divide::I32 count, VkDescriptorPoolCreateFlags flags)
127 {
129
130 thread_local Divide::vector<VkDescriptorPoolSize> sizes;
131
132 sizes.clear();
133 sizes.reserve(_poolSizes.sizes.size());
134
135 for (auto sz : _poolSizes.sizes)
136 {
137 const Divide::U32 targetSize = Divide::to_U32( sz.multiplier * count );
138 if ( targetSize > 0u )
139 {
140 sizes.push_back( { sz.type, targetSize } );
141 }
142 }
143
144 VkDescriptorPoolCreateInfo pool_info = {.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
145 pool_info.flags = flags;
146 pool_info.maxSets = count;
147 pool_info.poolSizeCount = Divide::to_U32(sizes.size());
148 pool_info.pPoolSizes = sizes.data();
149
150 VkDescriptorPool descriptorPool;
151 vkCreateDescriptorPool(_device, &pool_info, nullptr, &descriptorPool);
152
153 return descriptorPool;
154 }
155
157 {
158 for (DescriptorAllocator allocator : _clearAllocators)
159 {
160 vkDestroyDescriptorPool(_device, allocator.pool, nullptr);
161 }
162
163 for (auto&& storage : _descriptorPools)
164 {
165 for (DescriptorAllocator allocator : storage->_fullAllocators)
166 {
167 vkDestroyDescriptorPool(_device, allocator.pool, nullptr);
168 }
169 for (DescriptorAllocator allocator : storage->_usableAllocators)
170 {
171 vkDestroyDescriptorPool(_device, allocator.pool, nullptr);
172 }
173 }
174 }
175
177 {
179
181 {
182 PROFILE_SCOPE( "Flip full allocators", Divide::Profiler::Category::Graphics );
183 for (auto al : _descriptorPools[_frameIndex]->_fullAllocators )
184 {
185
186 vkResetDescriptorPool(_device, al.pool, VkDescriptorPoolResetFlags{ 0 });
187
188 _clearAllocators.push_back(al);
189 }
190 }
191 {
192 PROFILE_SCOPE( "Flip usable allocators", Divide::Profiler::Category::Graphics );
193 for (auto al : _descriptorPools[_frameIndex]->_usableAllocators)
194 {
195 vkResetDescriptorPool(_device, al.pool, VkDescriptorPoolResetFlags{ 0 });
196 _clearAllocators.push_back(al);
197 }
198 }
199 _descriptorPools[_frameIndex]->_fullAllocators.clear();
200 _descriptorPools[_frameIndex]->_usableAllocators.clear();
201 }
202
203 void DescriptorAllocatorPool::SetPoolSizeMultiplier(VkDescriptorType type, float multiplier)
204 {
205 for (auto& s : _poolSizes.sizes)
206 {
207 if (s.type == type)
208 {
209 s.multiplier = multiplier;
210 return;
211 }
212 }
213
214 //not found, so add it
215 _poolSizes.sizes.emplace_back(PoolSize{type, multiplier});
216 }
217
219 {
221
222 if (bIsFull)
223 {
224 _descriptorPools[handle.poolIdx]->_fullAllocators.push_back(DescriptorAllocator{ handle.vkPool });
225 }
226 else
227 {
228 _descriptorPools[handle.poolIdx]->_usableAllocators.push_back(DescriptorAllocator{ handle.vkPool });
229 }
230 }
231
233 {
235
237
238 bool foundAllocator = false;
239
240 Divide::I32 poolIndex = _frameIndex;
241
242 DescriptorAllocator allocator{};
243 //try reuse an allocated pool
244 if (_clearAllocators.size() != 0)
245 {
246 allocator = _clearAllocators.back();
247 _clearAllocators.pop_back();
248 foundAllocator = true;
249 }
250 else if (_descriptorPools[poolIndex]->_usableAllocators.size() > 0)
251 {
252 allocator = _descriptorPools[poolIndex]->_usableAllocators.back();
253 _descriptorPools[poolIndex]->_usableAllocators.pop_back();
254 foundAllocator = 1;
255 }
256
257 //need a new pool
258 if (!foundAllocator)
259 {
260 //static pool has to be free-able
261 allocator.pool = createPool(2000, poolIndex == 0 ? VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT : 0 );
262 foundAllocator = true;
263 }
264
266 ret.ownerPool = this;
267 ret.vkPool = allocator.pool;
268 ret.poolIdx = poolIndex;
269
270 return ret;
271 }
272}
#define PROFILE_SCOPE_AUTO(CATEGORY)
Definition: Profiler.h:87
#define PROFILE_SCOPE(NAME, CATEGORY)
Definition: Profiler.h:86
static DescriptorAllocatorPool * Create(const VkDevice &device, Divide::I32 nFrames=3)
void SetPoolSizeMultiplier(VkDescriptorType type, float multiplier)
Not thread safe! Override the pool size for a specific descriptor type. This will be used new pools a...
Divide::vector< DescriptorAllocator > _clearAllocators
VkDescriptorPool createPool(Divide::I32 count, VkDescriptorPoolCreateFlags flags)
void Flip()
Not thread safe! Switches default allocators to the next frame. When frames loop it will reset the de...
void ReturnAllocator(DescriptorAllocatorHandle &handle, bool bIsFull)
Divide::vector< std::unique_ptr< PoolStorage > > _descriptorPools
DescriptorAllocatorHandle GetAllocator()
constexpr Optick::Category::Type Graphics
Definition: Profiler.h:60
std::lock_guard< mutex > LockGuard
Definition: SharedMutex.h:55
constexpr U32 to_U32(const T value)
int32_t I32
uint8_t U8
eastl::vector< Type > vector
Definition: Vector.h:42
constexpr U8 U8_MAX
uint32_t U32
bool IsMemoryError(const VkResult errorResult) noexcept
DescriptorAllocatorHandle & operator=(const DescriptorAllocatorHandle &)=delete
bool Allocate(const VkDescriptorSetLayout &layout, VkDescriptorSet &builtSet, Divide::U8 retryCount=0u)
Allocate new descriptor. handle has to be valid returns true if allocation succeeded,...
void Return()
Return this handle to the pool. Will make this handle orphaned.
DescriptorAllocatorPool * ownerPool
Divide::vector< PoolSize > sizes