• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2021 The Android Open Source Project
2 // Copyright (C) 2021 Google Inc.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 #include "DescriptorSetVirtualization.h"
16 #include "Resources.h"
17 
18 namespace goldfish_vk {
19 
clearReifiedDescriptorSet(ReifiedDescriptorSet * set)20 void clearReifiedDescriptorSet(ReifiedDescriptorSet* set) {
21     set->pool = VK_NULL_HANDLE;
22     set->setLayout = VK_NULL_HANDLE;
23     set->poolId = -1;
24     set->allocationPending = false;
25     set->allWrites.clear();
26     set->pendingWriteArrayRanges.clear();
27 }
28 
initDescriptorWriteTable(const std::vector<VkDescriptorSetLayoutBinding> & layoutBindings,DescriptorWriteTable & table)29 void initDescriptorWriteTable(const std::vector<VkDescriptorSetLayoutBinding>& layoutBindings, DescriptorWriteTable& table) {
30     uint32_t highestBindingNumber = 0;
31 
32     for (uint32_t i = 0; i < layoutBindings.size(); ++i) {
33         if (layoutBindings[i].binding > highestBindingNumber) {
34             highestBindingNumber = layoutBindings[i].binding;
35         }
36     }
37 
38     std::vector<uint32_t> countsEachBinding(highestBindingNumber + 1, 0);
39 
40     for (uint32_t i = 0; i < layoutBindings.size(); ++i) {
41         countsEachBinding[layoutBindings[i].binding] =
42             layoutBindings[i].descriptorCount;
43     }
44 
45     table.resize(countsEachBinding.size());
46 
47     for (uint32_t i = 0; i < table.size(); ++i) {
48         table[i].resize(countsEachBinding[i]);
49 
50         for (uint32_t j = 0; j < countsEachBinding[i]; ++j) {
51             table[i][j].type = DescriptorWriteType::Empty;
52             table[i][j].dstArrayElement = 0;
53         }
54     }
55 }
56 
initializeReifiedDescriptorSet(VkDescriptorPool pool,VkDescriptorSetLayout setLayout,ReifiedDescriptorSet * set)57 static void initializeReifiedDescriptorSet(VkDescriptorPool pool, VkDescriptorSetLayout setLayout, ReifiedDescriptorSet* set) {
58 
59     set->pendingWriteArrayRanges.clear();
60 
61     const auto& layoutInfo = *(as_goldfish_VkDescriptorSetLayout(setLayout)->layoutInfo);
62 
63     initDescriptorWriteTable(layoutInfo.bindings, set->allWrites);
64 
65     for (size_t i = 0; i < layoutInfo.bindings.size(); ++i) {
66         // Bindings can be sparsely defined
67         const auto& binding = layoutInfo.bindings[i];
68         uint32_t bindingIndex = binding.binding;
69         if (set->bindingIsImmutableSampler.size() <= bindingIndex) {
70             set->bindingIsImmutableSampler.resize(bindingIndex + 1, false);
71         }
72         set->bindingIsImmutableSampler[bindingIndex] =
73             binding.descriptorCount > 0 &&
74             (binding.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER ||
75              binding.descriptorType ==
76              VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) &&
77             binding.pImmutableSamplers;
78     }
79 
80     set->pool = pool;
81     set->setLayout = setLayout;
82     set->allocationPending = true;
83     set->bindings = layoutInfo.bindings;
84 }
85 
isDescriptorTypeImageInfo(VkDescriptorType descType)86 bool isDescriptorTypeImageInfo(VkDescriptorType descType) {
87     return (descType == VK_DESCRIPTOR_TYPE_SAMPLER) ||
88            (descType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) ||
89            (descType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE) ||
90            (descType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) ||
91            (descType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT);
92 }
93 
isDescriptorTypeBufferInfo(VkDescriptorType descType)94 bool isDescriptorTypeBufferInfo(VkDescriptorType descType) {
95     return (descType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER) ||
96            (descType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) ||
97            (descType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) ||
98            (descType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC);
99 }
100 
isDescriptorTypeBufferView(VkDescriptorType descType)101 bool isDescriptorTypeBufferView(VkDescriptorType descType) {
102     return (descType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER) ||
103            (descType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER);
104 }
105 
isDescriptorTypeInlineUniformBlock(VkDescriptorType descType)106 bool isDescriptorTypeInlineUniformBlock(VkDescriptorType descType) {
107     return descType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT;
108 }
109 
isDescriptorTypeAccelerationStructure(VkDescriptorType descType)110 bool isDescriptorTypeAccelerationStructure(VkDescriptorType descType) {
111     return descType == VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
112 }
113 
doEmulatedDescriptorWrite(const VkWriteDescriptorSet * write,ReifiedDescriptorSet * toWrite)114 void doEmulatedDescriptorWrite(const VkWriteDescriptorSet* write, ReifiedDescriptorSet* toWrite) {
115     VkDescriptorType descType = write->descriptorType;
116     uint32_t dstBinding = write->dstBinding;
117     uint32_t dstArrayElement = write->dstArrayElement;
118     uint32_t descriptorCount = write->descriptorCount;
119 
120     DescriptorWriteTable& table = toWrite->allWrites;
121 
122     uint32_t arrOffset = dstArrayElement;
123 
124     if (isDescriptorTypeImageInfo(descType)) {
125         for (uint32_t i = 0; i < descriptorCount; ++i, ++arrOffset) {
126             if (arrOffset >= table[dstBinding].size()) {
127                 ++dstBinding;
128                 arrOffset = 0;
129             }
130             auto& entry = table[dstBinding][arrOffset];
131             entry.imageInfo = write->pImageInfo[i];
132             entry.type = DescriptorWriteType::ImageInfo;
133             entry.descriptorType = descType;
134         }
135     } else if (isDescriptorTypeBufferInfo(descType)) {
136         for (uint32_t i = 0; i < descriptorCount; ++i, ++arrOffset) {
137             if (arrOffset >= table[dstBinding].size()) {
138                 ++dstBinding;
139                 arrOffset = 0;
140             }
141             auto& entry = table[dstBinding][arrOffset];
142             entry.bufferInfo = write->pBufferInfo[i];
143             entry.type = DescriptorWriteType::BufferInfo;
144             entry.descriptorType = descType;
145         }
146     } else if (isDescriptorTypeBufferView(descType)) {
147         for (uint32_t i = 0; i < descriptorCount; ++i, ++arrOffset) {
148             if (arrOffset >= table[dstBinding].size()) {
149                 ++dstBinding;
150                 arrOffset = 0;
151             }
152             auto& entry = table[dstBinding][arrOffset];
153             entry.bufferView = write->pTexelBufferView[i];
154             entry.type = DescriptorWriteType::BufferView;
155             entry.descriptorType = descType;
156         }
157     } else if (isDescriptorTypeInlineUniformBlock(descType)) {
158         // TODO
159         // Look for pNext inline uniform block
160         // Append new DescriptorWrite entry that holds the buffer
161     } else if (isDescriptorTypeAccelerationStructure(descType)) {
162         // TODO
163         // Look for pNext acceleration structure
164         // Append new DescriptorWrite entry that holds it
165     } else {
166         return;
167     }
168 }
169 
doEmulatedDescriptorCopy(const VkCopyDescriptorSet * copy,const ReifiedDescriptorSet * src,ReifiedDescriptorSet * dst)170 void doEmulatedDescriptorCopy(const VkCopyDescriptorSet* copy, const ReifiedDescriptorSet* src, ReifiedDescriptorSet* dst) {
171     const DescriptorWriteTable& srcTable = src->allWrites;
172     DescriptorWriteTable& dstTable = dst->allWrites;
173 
174     // src/dst may be the same descriptor set, so we need to create a temporary array for that case.
175     // (TODO: Maybe just notice the pointers are the same? can aliasing in any other way happen?)
176 
177     std::vector<DescriptorWrite> toCopy;
178     uint32_t currBinding = copy->srcBinding;
179     uint32_t arrOffset = copy->srcArrayElement;
180     for (uint32_t i = 0; i < copy->descriptorCount; ++i, ++arrOffset) {
181         if (arrOffset >= srcTable[currBinding].size()) {
182             ++currBinding;
183             arrOffset = 0;
184         }
185         toCopy.push_back(srcTable[currBinding][arrOffset]);
186     }
187 
188     currBinding = copy->dstBinding;
189     arrOffset = copy->dstArrayElement;
190     for (uint32_t i = 0; i < copy->descriptorCount; ++i, ++arrOffset) {
191         if (arrOffset >= dstTable[currBinding].size()) {
192             ++currBinding;
193             arrOffset = 0;
194         }
195         dstTable[currBinding][arrOffset] = toCopy[i];
196     }
197 }
198 
doEmulatedDescriptorImageInfoWriteFromTemplate(VkDescriptorType descType,uint32_t binding,uint32_t dstArrayElement,uint32_t count,const VkDescriptorImageInfo * imageInfos,ReifiedDescriptorSet * set)199 void doEmulatedDescriptorImageInfoWriteFromTemplate(
200     VkDescriptorType descType,
201     uint32_t binding,
202     uint32_t dstArrayElement,
203     uint32_t count,
204     const VkDescriptorImageInfo* imageInfos,
205     ReifiedDescriptorSet* set) {
206 
207     DescriptorWriteTable& table = set->allWrites;
208 
209     uint32_t currBinding = binding;
210     uint32_t arrOffset = dstArrayElement;
211 
212     for (uint32_t i = 0; i < count; ++i, ++arrOffset) {
213         if (arrOffset >= table[currBinding].size()) {
214             ++currBinding;
215             arrOffset = 0;
216         }
217         auto& entry = table[currBinding][arrOffset];
218         entry.imageInfo = imageInfos[i];
219         entry.type = DescriptorWriteType::ImageInfo;
220         entry.descriptorType = descType;
221     }
222 }
223 
doEmulatedDescriptorBufferInfoWriteFromTemplate(VkDescriptorType descType,uint32_t binding,uint32_t dstArrayElement,uint32_t count,const VkDescriptorBufferInfo * bufferInfos,ReifiedDescriptorSet * set)224 void doEmulatedDescriptorBufferInfoWriteFromTemplate(
225     VkDescriptorType descType,
226     uint32_t binding,
227     uint32_t dstArrayElement,
228     uint32_t count,
229     const VkDescriptorBufferInfo* bufferInfos,
230     ReifiedDescriptorSet* set) {
231 
232     DescriptorWriteTable& table = set->allWrites;
233 
234     uint32_t currBinding = binding;
235     uint32_t arrOffset = dstArrayElement;
236 
237     for (uint32_t i = 0; i < count; ++i, ++arrOffset) {
238         if (arrOffset >= table[currBinding].size()) {
239             ++currBinding;
240             arrOffset = 0;
241         }
242         auto& entry = table[currBinding][dstArrayElement + i];
243         entry.bufferInfo = bufferInfos[i];
244         entry.type = DescriptorWriteType::BufferInfo;
245         entry.descriptorType = descType;
246     }
247 }
248 
doEmulatedDescriptorBufferViewWriteFromTemplate(VkDescriptorType descType,uint32_t binding,uint32_t dstArrayElement,uint32_t count,const VkBufferView * bufferViews,ReifiedDescriptorSet * set)249 void doEmulatedDescriptorBufferViewWriteFromTemplate(
250     VkDescriptorType descType,
251     uint32_t binding,
252     uint32_t dstArrayElement,
253     uint32_t count,
254     const VkBufferView* bufferViews,
255     ReifiedDescriptorSet* set) {
256 
257     DescriptorWriteTable& table = set->allWrites;
258 
259     uint32_t currBinding = binding;
260     uint32_t arrOffset = dstArrayElement;
261 
262     for (uint32_t i = 0; i < count; ++i, ++arrOffset) {
263         if (arrOffset >= table[currBinding].size()) {
264             ++currBinding;
265             arrOffset = 0;
266         }
267         auto& entry = table[currBinding][dstArrayElement + i];
268         entry.bufferView = bufferViews[i];
269         entry.type = DescriptorWriteType::BufferView;
270         entry.descriptorType = descType;
271     }
272 }
273 
isBindingFeasibleForAlloc(const DescriptorPoolAllocationInfo::DescriptorCountInfo & countInfo,const VkDescriptorSetLayoutBinding & binding)274 static bool isBindingFeasibleForAlloc(
275     const DescriptorPoolAllocationInfo::DescriptorCountInfo& countInfo,
276     const VkDescriptorSetLayoutBinding& binding) {
277 
278     if (binding.descriptorCount && (countInfo.type != binding.descriptorType)) {
279         return false;
280     }
281 
282     uint32_t availDescriptorCount =
283         countInfo.descriptorCount - countInfo.used;
284 
285     if (availDescriptorCount < binding.descriptorCount) {
286         ALOGV("%s: Ran out of descriptors of type 0x%x. "
287               "Wanted %u from layout but "
288               "we only have %u free (total in pool: %u)\n", __func__,
289               binding.descriptorType,
290               binding.descriptorCount,
291               countInfo.descriptorCount - countInfo.used,
292               countInfo.descriptorCount);
293         return false;
294     }
295 
296     return true;
297 }
298 
isBindingFeasibleForFree(const DescriptorPoolAllocationInfo::DescriptorCountInfo & countInfo,const VkDescriptorSetLayoutBinding & binding)299 static bool isBindingFeasibleForFree(
300     const DescriptorPoolAllocationInfo::DescriptorCountInfo& countInfo,
301     const VkDescriptorSetLayoutBinding& binding) {
302 
303     if (countInfo.type != binding.descriptorType) return false;
304     if (countInfo.used < binding.descriptorCount) {
305         ALOGV("%s: Was a descriptor set double freed? "
306               "Ran out of descriptors of type 0x%x. "
307               "Wanted to free %u from layout but "
308               "we only have %u used (total in pool: %u)\n", __func__,
309               binding.descriptorType,
310               binding.descriptorCount,
311               countInfo.used,
312               countInfo.descriptorCount);
313         return false;
314     }
315     return true;
316 }
317 
allocBindingFeasible(const VkDescriptorSetLayoutBinding & binding,DescriptorPoolAllocationInfo::DescriptorCountInfo & poolState)318 static void allocBindingFeasible(
319     const VkDescriptorSetLayoutBinding& binding,
320     DescriptorPoolAllocationInfo::DescriptorCountInfo& poolState) {
321     poolState.used += binding.descriptorCount;
322 }
323 
freeBindingFeasible(const VkDescriptorSetLayoutBinding & binding,DescriptorPoolAllocationInfo::DescriptorCountInfo & poolState)324 static void freeBindingFeasible(
325     const VkDescriptorSetLayoutBinding& binding,
326     DescriptorPoolAllocationInfo::DescriptorCountInfo& poolState) {
327     poolState.used -= binding.descriptorCount;
328 }
329 
validateDescriptorSetAllocation(const VkDescriptorSetAllocateInfo * pAllocateInfo)330 static VkResult validateDescriptorSetAllocation(const VkDescriptorSetAllocateInfo* pAllocateInfo) {
331     VkDescriptorPool pool = pAllocateInfo->descriptorPool;
332     DescriptorPoolAllocationInfo* poolInfo = as_goldfish_VkDescriptorPool(pool)->allocInfo;
333 
334     // Check the number of sets available.
335     auto setsAvailable = poolInfo->maxSets - poolInfo->usedSets;
336 
337     if (setsAvailable < pAllocateInfo->descriptorSetCount) {
338         ALOGV("%s: Error: VkDescriptorSetAllocateInfo wants %u sets "
339               "but we only have %u available. "
340               "Bailing with VK_ERROR_OUT_OF_POOL_MEMORY.\n", __func__,
341               pAllocateInfo->descriptorSetCount,
342               setsAvailable);
343         return VK_ERROR_OUT_OF_POOL_MEMORY;
344     }
345 
346     // Perform simulated allocation and error out with
347     // VK_ERROR_OUT_OF_POOL_MEMORY if it fails.
348     std::vector<DescriptorPoolAllocationInfo::DescriptorCountInfo> descriptorCountCopy =
349         poolInfo->descriptorCountInfo;
350 
351     for (uint32_t i = 0; i < pAllocateInfo->descriptorSetCount; ++i) {
352         if (!pAllocateInfo->pSetLayouts[i]) {
353             ALOGV("%s: Error: Tried to allocate a descriptor set with null set layout.\n", __func__);
354             return VK_ERROR_INITIALIZATION_FAILED;
355         }
356 
357         auto setLayoutInfo = as_goldfish_VkDescriptorSetLayout(pAllocateInfo->pSetLayouts[i])->layoutInfo;
358         if (!setLayoutInfo) {
359             return VK_ERROR_INITIALIZATION_FAILED;
360         }
361 
362         for (const auto& binding : setLayoutInfo->bindings) {
363             bool success = false;
364             for (auto& pool : descriptorCountCopy) {
365                 if (!isBindingFeasibleForAlloc(pool, binding)) continue;
366 
367                 success = true;
368                 allocBindingFeasible(binding, pool);
369                 break;
370             }
371 
372             if (!success) {
373                 return VK_ERROR_OUT_OF_POOL_MEMORY;
374             }
375         }
376     }
377     return VK_SUCCESS;
378 }
379 
applyDescriptorSetAllocation(VkDescriptorPool pool,VkDescriptorSetLayout setLayout)380 void applyDescriptorSetAllocation(VkDescriptorPool pool, VkDescriptorSetLayout setLayout) {
381     auto allocInfo = as_goldfish_VkDescriptorPool(pool)->allocInfo;
382     auto setLayoutInfo = as_goldfish_VkDescriptorSetLayout(setLayout)->layoutInfo;
383 
384     ++allocInfo->usedSets;
385 
386     for (const auto& binding : setLayoutInfo->bindings) {
387         for (auto& countForPool : allocInfo->descriptorCountInfo) {
388             if (!isBindingFeasibleForAlloc(countForPool, binding)) continue;
389             allocBindingFeasible(binding, countForPool);
390             break;
391         }
392     }
393 }
394 
removeDescriptorSetAllocation(VkDescriptorPool pool,const std::vector<VkDescriptorSetLayoutBinding> & bindings)395 void removeDescriptorSetAllocation(VkDescriptorPool pool, const std::vector<VkDescriptorSetLayoutBinding>& bindings) {
396     auto allocInfo = as_goldfish_VkDescriptorPool(pool)->allocInfo;
397 
398     if (0 == allocInfo->usedSets) {
399         ALOGV("%s: Warning: a descriptor set was double freed.\n", __func__);
400         return;
401     }
402 
403     --allocInfo->usedSets;
404 
405     for (const auto& binding : bindings) {
406         for (auto& countForPool : allocInfo->descriptorCountInfo) {
407             if (!isBindingFeasibleForFree(countForPool, binding)) continue;
408             freeBindingFeasible(binding, countForPool);
409             break;
410         }
411     }
412 }
413 
fillDescriptorSetInfoForPool(VkDescriptorPool pool,VkDescriptorSetLayout setLayout,VkDescriptorSet set)414 void fillDescriptorSetInfoForPool(VkDescriptorPool pool, VkDescriptorSetLayout setLayout, VkDescriptorSet set) {
415     DescriptorPoolAllocationInfo* allocInfo = as_goldfish_VkDescriptorPool(pool)->allocInfo;
416 
417     ReifiedDescriptorSet* newReified = new ReifiedDescriptorSet;
418     newReified->poolId = as_goldfish_VkDescriptorSet(set)->underlying;
419     newReified->allocationPending = true;
420 
421     as_goldfish_VkDescriptorSet(set)->reified = newReified;
422 
423     allocInfo->allocedPoolIds.insert(newReified->poolId);
424     allocInfo->allocedSets.insert(set);
425 
426     initializeReifiedDescriptorSet(pool, setLayout, newReified);
427 }
428 
validateAndApplyVirtualDescriptorSetAllocation(const VkDescriptorSetAllocateInfo * pAllocateInfo,VkDescriptorSet * pSets)429 VkResult validateAndApplyVirtualDescriptorSetAllocation(const VkDescriptorSetAllocateInfo* pAllocateInfo, VkDescriptorSet* pSets) {
430     VkResult validateRes = validateDescriptorSetAllocation(pAllocateInfo);
431 
432     if (validateRes != VK_SUCCESS) return validateRes;
433 
434     for (uint32_t i = 0; i < pAllocateInfo->descriptorSetCount; ++i) {
435         applyDescriptorSetAllocation(pAllocateInfo->descriptorPool, pAllocateInfo->pSetLayouts[i]);
436     }
437 
438     VkDescriptorPool pool = pAllocateInfo->descriptorPool;
439     DescriptorPoolAllocationInfo* allocInfo = as_goldfish_VkDescriptorPool(pool)->allocInfo;
440 
441     if (allocInfo->freePoolIds.size() < pAllocateInfo->descriptorSetCount) {
442         ALOGE("%s: FATAL: Somehow out of descriptor pool IDs. Wanted %u IDs but only have %u free IDs remaining. The count for maxSets was %u and used was %u\n", __func__,
443                 pAllocateInfo->descriptorSetCount,
444                 (uint32_t)allocInfo->freePoolIds.size(),
445                 allocInfo->maxSets,
446                 allocInfo->usedSets);
447         abort();
448     }
449 
450     for (uint32_t i = 0 ; i < pAllocateInfo->descriptorSetCount; ++i) {
451         uint64_t id = allocInfo->freePoolIds.back();
452         allocInfo->freePoolIds.pop_back();
453 
454         VkDescriptorSet newSet = new_from_host_VkDescriptorSet((VkDescriptorSet)id);
455         pSets[i] = newSet;
456 
457         fillDescriptorSetInfoForPool(pool, pAllocateInfo->pSetLayouts[i], newSet);
458     }
459 
460     return VK_SUCCESS;
461 }
462 
removeDescriptorSetFromPool(VkDescriptorSet set,bool usePoolIds)463 bool removeDescriptorSetFromPool(VkDescriptorSet set, bool usePoolIds) {
464     ReifiedDescriptorSet* reified = as_goldfish_VkDescriptorSet(set)->reified;
465 
466     VkDescriptorPool pool = reified->pool;
467     DescriptorPoolAllocationInfo* allocInfo = as_goldfish_VkDescriptorPool(pool)->allocInfo;
468 
469     if (usePoolIds) {
470         // Look for the set's pool Id in the pool. If not found, then this wasn't really allocated, and bail.
471         if (allocInfo->allocedPoolIds.find(reified->poolId) == allocInfo->allocedPoolIds.end()) {
472             return false;
473         }
474     }
475 
476     const std::vector<VkDescriptorSetLayoutBinding>& bindings = reified->bindings;
477     removeDescriptorSetAllocation(pool, bindings);
478 
479     if (usePoolIds) {
480         allocInfo->freePoolIds.push_back(reified->poolId);
481         allocInfo->allocedPoolIds.erase(reified->poolId);
482     }
483     allocInfo->allocedSets.erase(set);
484 
485     return true;
486 }
487 
clearDescriptorPool(VkDescriptorPool pool,bool usePoolIds)488 std::vector<VkDescriptorSet> clearDescriptorPool(VkDescriptorPool pool, bool usePoolIds) {
489     std::vector<VkDescriptorSet> toClear;
490     for (auto set : as_goldfish_VkDescriptorPool(pool)->allocInfo->allocedSets) {
491         toClear.push_back(set);
492     }
493 
494     for (auto set: toClear) {
495         removeDescriptorSetFromPool(set, usePoolIds);
496     }
497 
498     return toClear;
499 }
500 
501 } // namespace goldfish_vk
502