diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.cpp b/filament/backend/src/vulkan/VulkanPipelineCache.cpp index b4dfea5a28..be1f006adb 100644 --- a/filament/backend/src/vulkan/VulkanPipelineCache.cpp +++ b/filament/backend/src/vulkan/VulkanPipelineCache.cpp @@ -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. diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.h b/filament/backend/src/vulkan/VulkanPipelineCache.h index d206058027..f83700d105 100644 --- a/filament/backend/src/vulkan/VulkanPipelineCache.h +++ b/filament/backend/src/vulkan/VulkanPipelineCache.h @@ -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.