Vulkan: fix logic that determines when to grow descriptor pool.

When we added per-layout arenas for each of the 3 descriptor types, we
did not account for them when determining how much of the Vk Pool is in
use. Therefore the "growth" (really a re-creation) of the Vk Pool wasn't
always occurring when necessary, causing descriptor set allocation to
fail with large models.
This commit is contained in:
Philip Rideout
2022-03-01 15:43:50 -08:00
parent 426f345f19
commit 684b0622eb
2 changed files with 31 additions and 1 deletions

View File

@@ -232,9 +232,18 @@ VulkanPipelineCache::DescriptorCacheEntry* VulkanPipelineCache::createDescriptor
// are no longer used. This occurs during the cleanup phase during command buffer submission.
auto& descriptorSetArenas = layoutCacheEntry->descriptorSetArenas;
if (descriptorSetArenas[0].empty()) {
if (mDescriptorSets.size() >= mDescriptorPoolSize) {
// If allocating a new descriptor set from the pool would cause it to overflow, then
// recreate the pool. The number of descriptor sets that have already been allocated from
// the pool is the sum of the "active" descriptor sets (mDescriptorSets) and the "dormant"
// descriptor sets (mDescriptorArenasCount).
//
// NOTE: technically both sides of the inequality below should be multiplied by
// DESCRIPTOR_TYPE_COUNT to get the true number of descriptor sets.
if (mDescriptorSets.size() + mDescriptorArenasCount + 1 > mDescriptorPoolSize) {
growDescriptorPool();
}
VkDescriptorSetAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = mDescriptorPool;
@@ -251,6 +260,8 @@ VulkanPipelineCache::DescriptorCacheEntry* VulkanPipelineCache::createDescriptor
descriptorCacheEntry.handles[i] = descriptorSetArenas[i].back();
descriptorSetArenas[i].pop_back();
}
assert_invariant(mDescriptorArenasCount > 0);
mDescriptorArenasCount--;
}
// Rewrite every binding in the new descriptor sets.
@@ -704,6 +715,7 @@ void VulkanPipelineCache::onCommandBuffer(const VulkanCommandBuffer& cmdbuffer)
for (uint32_t i = 0; i < DESCRIPTOR_TYPE_COUNT; ++i) {
arenas[i].push_back(cacheEntry.handles[i]);
}
++mDescriptorArenasCount;
iter = mDescriptorSets.erase(iter);
} else {
++iter;
@@ -738,6 +750,12 @@ void VulkanPipelineCache::onCommandBuffer(const VulkanCommandBuffer& cmdbuffer)
#endif
vkDestroyDescriptorSetLayout(mDevice, setLayout, VKALLOC);
}
auto& arenas = iter->second.descriptorSetArenas;
assert_invariant(mDescriptorArenasCount >= arenas[0].size());
mDescriptorArenasCount -= arenas[0].size();
for (auto& arena : arenas) {
vkFreeDescriptorSets(mDevice, mDescriptorPool, arena.size(), arena.data());
}
iter = mPipelineLayouts.erase(iter);
} else {
++iter;
@@ -839,6 +857,7 @@ void VulkanPipelineCache::growDescriptorPool() noexcept {
arena.clear();
}
}
mDescriptorArenasCount = 0;
// Move all in-use descriptors from the primary cache into an "extinct" list, so that they will
// later be destroyed rather than reclaimed.

View File

@@ -399,8 +399,19 @@ private:
// The descriptor set pool starts out with a decent number of descriptor sets. The cache can
// grow the pool by re-creating it with a larger size. See growDescriptorPool().
VkDescriptorPool mDescriptorPool;
// This describes the number of descriptor sets in mDescriptorPool. Note that this needs to be
// multiplied by DESCRIPTOR_TYPE_COUNT to get the actual number of descriptor sets. Also note
// that the number of low-level "descriptors" (not descriptor *sets*) is actually much more than
// this size. It can be computed only by factoring in UBUFFER_BINDING_COUNT etc.
uint32_t mDescriptorPoolSize = INITIAL_DESCRIPTOR_SET_POOL_SIZE;
// To get the actual number of descriptor sets that have been allocated from the pool,
// take the sum of mDescriptorArenasCount (these are inactive descriptor sets) and the
// number of entries in the mDescriptorPool map (active descriptor sets). Multiply the result by
// DESCRIPTOR_TYPE_COUNT.
uint32_t mDescriptorArenasCount = 0;
// After a growth event (i.e. when the VkDescriptorPool is replaced with a bigger version), all
// currently used descriptors are moved into the "extinct" sets so that they can be safely
// destroyed a few frames later.