diff --git a/include/vsg/app/CompileManager.h b/include/vsg/app/CompileManager.h index fefdd3e25..94e9bb877 100644 --- a/include/vsg/app/CompileManager.h +++ b/include/vsg/app/CompileManager.h @@ -78,10 +78,14 @@ namespace vsg using ContextSelectionFunction = std::function; - /// compile object + /// compile object. + /// Does not throw on compile failure: any vsg::Exception is caught internally and + /// reported via the returned CompileResult. Check the result (CompileResult::result + /// == VK_SUCCESS, or operator bool()) before using the compiled subgraph. CompileResult compile(ref_ptr object, ContextSelectionFunction contextSelection = {}); - /// compile all the command graphs in a task + /// compile all the command graphs in a task. + /// Does not throw on compile failure; check the returned CompileResult as for compile(). CompileResult compileTask(ref_ptr task, const ResourceRequirements& resourceRequirements = {}); /// mechanism for releasing and reusing used resources diff --git a/include/vsg/vk/DescriptorPool.h b/include/vsg/vk/DescriptorPool.h index e4324a06b..e3bf783d4 100644 --- a/include/vsg/vk/DescriptorPool.h +++ b/include/vsg/vk/DescriptorPool.h @@ -63,6 +63,11 @@ namespace vsg DescriptorPoolSizes _availableDescriptorPoolSizes; std::list> _recyclingList; + + // Running per-type total of the descriptors held in _recyclingList, + // updated incrementally by freeDescriptorSet() and allocateDescriptorSet(). + // available() reads this directly instead of walking the whole list. + DescriptorPoolSizes _recycledDescriptorPoolSizes; }; VSG_type_name(vsg::DescriptorPool); diff --git a/src/vsg/vk/DescriptorPool.cpp b/src/vsg/vk/DescriptorPool.cpp index fb9704a7c..5edbad01b 100644 --- a/src/vsg/vk/DescriptorPool.cpp +++ b/src/vsg/vk/DescriptorPool.cpp @@ -20,6 +20,28 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI using namespace vsg; +namespace +{ + // Add (sign +1) or remove (sign -1) a layout's binding counts from a running + // per-type total, so available() can read the recycled totals directly rather + // than re-walking the recycling list on every call. + void accumulateBindings(vsg::DescriptorPoolSizes& sizes, + const vsg::DescriptorSetLayout* layout, int sign) + { + if (!layout) return; + for (auto& binding : layout->bindings) + { + auto itr = std::find_if(sizes.begin(), sizes.end(), + [&binding](const VkDescriptorPoolSize& v) { return v.type == binding.descriptorType; }); + if (itr != sizes.end()) + itr->descriptorCount = static_cast( + static_cast(itr->descriptorCount) + sign * static_cast(binding.descriptorCount)); + else if (sign > 0) + sizes.push_back(VkDescriptorPoolSize{binding.descriptorType, binding.descriptorCount}); + } + } +} + DescriptorPool::DescriptorPool(Device* device, uint32_t in_maxSets, const DescriptorPoolSizes& in_descriptorPoolSizes) : maxSets(in_maxSets), descriptorPoolSizes(in_descriptorPoolSizes), @@ -72,6 +94,7 @@ ref_ptr DescriptorPool::allocateDescriptorSet(Des { // swap ownership so that DescriptorSet::Implementation now "has a" reference to this DescriptorPool dsi->_descriptorPool = this; + accumulateBindings(_recycledDescriptorPoolSizes, dsi->_descriptorSetLayout, -1); _recyclingList.erase(itr); --_availableDescriptorSet; return dsi; @@ -122,6 +145,7 @@ void DescriptorPool::freeDescriptorSet(ref_ptr ds std::scoped_lock lock(mutex); _recyclingList.push_back(dsi); ++_availableDescriptorSet; + accumulateBindings(_recycledDescriptorPoolSizes, dsi->_descriptorSetLayout, +1); } dsi->_descriptorPool = {}; } @@ -147,19 +171,19 @@ bool DescriptorPool::available(uint32_t& numSets, DescriptorPoolSizes& available } } - for (const auto& dsi : _recyclingList) + // Merge the cached recycled totals rather than walking _recyclingList, which + // is O(recycled sets) and dominates compile time once a high-churn scene has + // freed many sets. The cache is kept in sync by freeDescriptorSet() and + // allocateDescriptorSet(). + for (const auto& dps : _recycledDescriptorPoolSizes) { - if (dsi->_descriptorSetLayout) + if (dps.descriptorCount > 0) { - for (auto& binding : dsi->_descriptorSetLayout->bindings) - { - // increment any entries that are already in the descriptorPoolSizes vector - auto itr = std::find_if(availableDescriptorPoolSizes.begin(), availableDescriptorPoolSizes.end(), [&binding](const VkDescriptorPoolSize& value) { return value.type == binding.descriptorType; }); - if (itr != availableDescriptorPoolSizes.end()) - itr->descriptorCount += binding.descriptorCount; - else - availableDescriptorPoolSizes.push_back(VkDescriptorPoolSize{binding.descriptorType, binding.descriptorCount}); - } + auto itr = std::find_if(availableDescriptorPoolSizes.begin(), availableDescriptorPoolSizes.end(), [&dps](const VkDescriptorPoolSize& value) { return value.type == dps.type; }); + if (itr != availableDescriptorPoolSizes.end()) + itr->descriptorCount += dps.descriptorCount; + else + availableDescriptorPoolSizes.push_back(VkDescriptorPoolSize{dps.type, dps.descriptorCount}); } }