Advertisement
Guest User

descriptor allocator

a guest
Feb 29th, 2020
124
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.25 KB | None | 0 0
  1. // HEADER -------------------------------------------------
  2. #include <stdint.h>
  3. #include <vulkan/vulkan_core.h>
  4.  
  5. namespace vke {
  6.  
  7.     enum class DescriptorAllocatorLifetime {
  8.         Static,
  9.         PerFrame
  10.     };
  11.  
  12.     struct DescriptorAllocatorPool;
  13.  
  14.     struct DescriptorAllocatorHandle {     
  15.  
  16.         DescriptorAllocatorHandle() = default;
  17.  
  18.         ~DescriptorAllocatorHandle();
  19.  
  20.         DescriptorAllocatorHandle(DescriptorAllocatorHandle&& other);
  21.  
  22.         DescriptorAllocatorHandle& operator=(const DescriptorAllocatorHandle&) = delete;
  23.  
  24.         DescriptorAllocatorHandle& operator=(DescriptorAllocatorHandle&& other);
  25.  
  26.         void Return();
  27.         bool Allocate(const VkDescriptorSetLayout& layout, VkDescriptorSet& builtSet);
  28.  
  29.         DescriptorAllocatorPool* ownerPool{nullptr};
  30.         VkDescriptorPool vkPool;
  31.         int8_t poolIdx;
  32.     };
  33.  
  34.     struct DescriptorAllocatorPool {
  35.  
  36.         static DescriptorAllocatorPool* Create(const VkDevice& device, int nFrames = 3);
  37.  
  38.         virtual void Flip() = 0;
  39.  
  40.         virtual void SetPoolSizeMultiplier(VkDescriptorType type, float multiplier) = 0;
  41.  
  42.         virtual DescriptorAllocatorHandle GetAllocator(DescriptorAllocatorLifetime lifetime) = 0;
  43.     };
  44. }
  45. // SOURCE -------------------------------------------------
  46. #include <descriptor_allocator.h>
  47. #include <vector>
  48. #include <memory>
  49. #include <mutex>
  50.  
  51.  
  52.  
  53. namespace vke {
  54.  
  55.     bool IsMemoryError(VkResult errorResult) {
  56.         switch (errorResult) {
  57.         case VK_ERROR_FRAGMENTED_POOL:
  58.         case VK_ERROR_OUT_OF_POOL_MEMORY:
  59.             return true;
  60.         }
  61.         return false;
  62.     }
  63.    
  64.     struct DescriptorAllocator {       
  65.         VkDescriptorPool pool;
  66.     };
  67.     struct PoolStorage {
  68.  
  69.         std::vector<DescriptorAllocator> _usableAllocators;
  70.         std::vector<DescriptorAllocator> _fullAllocators;
  71.     };
  72.  
  73.     struct PoolSize {
  74.         VkDescriptorType type;
  75.         float multiplier;
  76.     };
  77.     struct PoolSizes {
  78.         std::vector<PoolSize> sizes =
  79.         {
  80.             { VK_DESCRIPTOR_TYPE_SAMPLER, 1.f },
  81.             { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 4.f },
  82.             { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 4.f },
  83.             { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1.f },
  84.             { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1.f },
  85.             { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1.f },
  86.             { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2.f },
  87.             { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2.f },
  88.             { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1.f },
  89.             { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1.f },
  90.             { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1.f }
  91.         };
  92.     };
  93.  
  94.    
  95.     struct DescriptorAllocatorPoolImpl :public DescriptorAllocatorPool {
  96.  
  97.         virtual void Flip() override;
  98.         virtual void SetPoolSizeMultiplier(VkDescriptorType type, float multiplier) override;      
  99.         virtual DescriptorAllocatorHandle GetAllocator(DescriptorAllocatorLifetime lifetime) override;
  100.  
  101.         void ReturnAllocator(DescriptorAllocatorHandle& handle, bool bIsFull);
  102.         VkDescriptorPool createPool(int count, VkDescriptorPoolCreateFlags flags);
  103.        
  104.         VkDevice _device;
  105.         PoolSizes _poolSizes;
  106.         int _frameIndex;
  107.         int _maxFrames;
  108.  
  109.         std::mutex _poolMutex;
  110.  
  111.         //zero is for static pool, next is for frame indexing
  112.         std::vector<std::unique_ptr<PoolStorage>> _descriptorPools;    
  113.  
  114.         //fully cleared allocators
  115.         std::vector<DescriptorAllocator> _clearAllocators;
  116.     };
  117.  
  118.    
  119.  
  120.     vke::DescriptorAllocatorPool* vke::DescriptorAllocatorPool::Create(const VkDevice& device, int nFrames)
  121.     {
  122.         DescriptorAllocatorPoolImpl* impl = new DescriptorAllocatorPoolImpl();
  123.         impl->_device = device;
  124.         impl->_frameIndex = 0;
  125.         impl->_maxFrames = nFrames;
  126.         for (int i = 0; i < nFrames + 1; i++) {
  127.             impl->_descriptorPools.push_back(std::make_unique<PoolStorage>());
  128.         }
  129.         return impl;
  130.     }
  131.  
  132.     DescriptorAllocatorHandle::~DescriptorAllocatorHandle()
  133.     {
  134.         DescriptorAllocatorPoolImpl* implPool = static_cast<DescriptorAllocatorPoolImpl*>(ownerPool);
  135.         if (implPool) {
  136.            
  137.             implPool->ReturnAllocator(*this, false);
  138.         }
  139.  
  140.     }
  141.  
  142.     DescriptorAllocatorHandle::DescriptorAllocatorHandle(DescriptorAllocatorHandle&& other)
  143.     {
  144.         vkPool = other.vkPool;
  145.         poolIdx = other.poolIdx;
  146.         ownerPool = other.ownerPool;
  147.  
  148.         other.ownerPool = nullptr;
  149.         other.poolIdx = -1;
  150.         other.vkPool = VkDescriptorPool{};
  151.     }
  152.  
  153.     vke::DescriptorAllocatorHandle& DescriptorAllocatorHandle::operator=(DescriptorAllocatorHandle&& other)
  154.     {
  155.         vkPool = other.vkPool;
  156.         poolIdx = other.poolIdx;
  157.         ownerPool = other.ownerPool;
  158.  
  159.         other.ownerPool = nullptr;
  160.         other.poolIdx = -1;
  161.         other.vkPool = VkDescriptorPool{};
  162.  
  163.         return *this;
  164.     }
  165.  
  166.     void DescriptorAllocatorHandle::Return()
  167.     {
  168.         DescriptorAllocatorPoolImpl* implPool = static_cast<DescriptorAllocatorPoolImpl*>(ownerPool);
  169.  
  170.         implPool->ReturnAllocator(*this, false);
  171.  
  172.         vkPool = VkDescriptorPool{ };
  173.         poolIdx = -1;
  174.         ownerPool = nullptr;
  175.     }
  176.  
  177.     bool DescriptorAllocatorHandle::Allocate(const VkDescriptorSetLayout& layout, VkDescriptorSet& builtSet)
  178.     {
  179.         DescriptorAllocatorPoolImpl*implPool = static_cast<DescriptorAllocatorPoolImpl*>(ownerPool);
  180.    
  181.  
  182.         VkDescriptorSetAllocateInfo allocInfo;
  183.         allocInfo.pNext = nullptr;
  184.         allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
  185.         allocInfo.descriptorPool = vkPool;
  186.         allocInfo.descriptorSetCount = 1;
  187.         allocInfo.pSetLayouts = &layout;
  188.  
  189.         VkResult result = vkAllocateDescriptorSets(implPool->_device, &allocInfo, &builtSet);
  190.         if (result != VK_SUCCESS)
  191.         {
  192.             //we reallocate pools on memory error
  193.             if (IsMemoryError(result))
  194.             {
  195.                 //out of space need reallocate         
  196.            
  197.                 implPool->ReturnAllocator(*this, true);
  198.  
  199.                 DescriptorAllocatorHandle newHandle = implPool->GetAllocator(poolIdx == 0 ? DescriptorAllocatorLifetime::Static : DescriptorAllocatorLifetime::PerFrame);
  200.                
  201.                 vkPool = newHandle.vkPool;
  202.                 poolIdx = newHandle.poolIdx;
  203.  
  204.                 newHandle.vkPool = VkDescriptorPool{};
  205.                 newHandle.poolIdx = -1;
  206.                 newHandle.ownerPool = nullptr;
  207.                 //could be good idea to avoid infinite loop here
  208.                 return Allocate(layout, builtSet);
  209.             }
  210.             else {
  211.                 //stuff is truly broken
  212.                 return false;
  213.             }
  214.         }
  215.  
  216.         return true;
  217.     }
  218.  
  219.     VkDescriptorPool DescriptorAllocatorPoolImpl::createPool(int count, VkDescriptorPoolCreateFlags flags)
  220.     {
  221.         std::vector<VkDescriptorPoolSize> sizes;
  222.         sizes.reserve(_poolSizes.sizes.size());
  223.         for (auto sz : _poolSizes.sizes) {
  224.             sizes.push_back({ sz.type, uint32_t(sz.multiplier * count) });
  225.         }
  226.         VkDescriptorPoolCreateInfo pool_info = {};
  227.         pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
  228.         pool_info.flags = flags;
  229.         pool_info.maxSets = count;
  230.         pool_info.poolSizeCount = (uint32_t)sizes.size();
  231.         pool_info.pPoolSizes = sizes.data();
  232.  
  233.         VkDescriptorPool descriptorPool;
  234.         vkCreateDescriptorPool(_device, &pool_info, nullptr, &descriptorPool);
  235.  
  236.         return descriptorPool;
  237.     }
  238.  
  239.     void DescriptorAllocatorPoolImpl::Flip()
  240.     {
  241.          _frameIndex = (_frameIndex+1) % _maxFrames;
  242.        
  243.          for (auto al :  _descriptorPools[_frameIndex + 1]->_fullAllocators ) {
  244.  
  245.              vkResetDescriptorPool(_device, al.pool, VkDescriptorPoolResetFlags{ 0 });
  246.  
  247.              _clearAllocators.push_back(al);
  248.          }
  249.  
  250.          for (auto al : _descriptorPools[_frameIndex + 1]->_usableAllocators) {
  251.  
  252.              vkResetDescriptorPool(_device, al.pool, VkDescriptorPoolResetFlags{ 0 });
  253.  
  254.              _clearAllocators.push_back(al);
  255.          }
  256.  
  257.          _descriptorPools[_frameIndex + 1]->_fullAllocators.clear();
  258.          _descriptorPools[_frameIndex + 1]->_usableAllocators.clear();
  259.     }
  260.  
  261.     void DescriptorAllocatorPoolImpl::SetPoolSizeMultiplier(VkDescriptorType type, float multiplier)
  262.     {
  263.         for (auto& s : _poolSizes.sizes) {
  264.             if (s.type == type) {
  265.                 s.multiplier = multiplier;
  266.                 return;
  267.             }
  268.         }
  269.  
  270.         //not found, so add it
  271.         PoolSize newSize;
  272.         newSize.type = type;
  273.         newSize.multiplier = multiplier;
  274.         _poolSizes.sizes.push_back(newSize);
  275.     }
  276.  
  277.     void DescriptorAllocatorPoolImpl::ReturnAllocator(DescriptorAllocatorHandle& handle, bool bIsFull)
  278.     {
  279.         std::lock_guard<std::mutex> lk(_poolMutex);
  280.  
  281.        
  282.         if (bIsFull) {
  283.             _descriptorPools[handle.poolIdx]->_fullAllocators.push_back(DescriptorAllocator{ handle.vkPool });
  284.         }
  285.         else {
  286.             _descriptorPools[handle.poolIdx]->_usableAllocators.push_back(DescriptorAllocator{ handle.vkPool });
  287.         }
  288.     }
  289.  
  290.     vke::DescriptorAllocatorHandle DescriptorAllocatorPoolImpl::GetAllocator(DescriptorAllocatorLifetime lifetime)
  291.     {
  292.         std::lock_guard<std::mutex> lk(_poolMutex);
  293.  
  294.         int poolIndex = 0;
  295.         bool foundAllocator = false;
  296.         if (lifetime == DescriptorAllocatorLifetime::PerFrame) {
  297.             poolIndex = _frameIndex + 1;
  298.         }
  299.  
  300.         DescriptorAllocator allocator;
  301.         //try reuse an allocated pool
  302.         if (_clearAllocators.size() != 0) {
  303.             allocator = _clearAllocators.back();
  304.             _clearAllocators.pop_back();
  305.             foundAllocator = true;             
  306.         }
  307.         else {
  308.             if (_descriptorPools[poolIndex]->_usableAllocators.size() > 0) {
  309.                 allocator = _descriptorPools[poolIndex]->_usableAllocators.back();
  310.                 _descriptorPools[poolIndex]->_usableAllocators.pop_back();
  311.                 foundAllocator = 1;
  312.             }
  313.         }
  314.         //need a new pool
  315.         if (!foundAllocator)
  316.         {
  317.             //static pool has to be free-able
  318.             VkDescriptorPoolCreateFlags flags = 0;
  319.             if (poolIndex == 0) {
  320.                 flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
  321.             }
  322.  
  323.             VkDescriptorPool newPool = createPool(2000, flags);
  324.  
  325.             allocator.pool = newPool;      
  326.  
  327.             foundAllocator = true;
  328.         }
  329.  
  330.         DescriptorAllocatorHandle newHandle;
  331.         newHandle.ownerPool = this;
  332.         newHandle.poolIdx = poolIndex;
  333.         newHandle.vkPool = allocator.pool;
  334.  
  335.         return newHandle;
  336.     }
  337. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement