1 // Copyright 2018 The SwiftShader Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "VkDescriptorPool.hpp" 16 #include "VkDescriptorSetLayout.hpp" 17 #include <algorithm> 18 #include <memory> 19 20 namespace vk 21 { 22 23 DescriptorPool::DescriptorPool(const VkDescriptorPoolCreateInfo* pCreateInfo, void* mem) : 24 pool(reinterpret_cast<VkDescriptorSet>(mem)), 25 poolSize(ComputeRequiredAllocationSize(pCreateInfo)) 26 { 27 } 28 29 void DescriptorPool::destroy(const VkAllocationCallbacks* pAllocator) 30 { 31 vk::deallocate(pool, pAllocator); 32 } 33 34 size_t DescriptorPool::ComputeRequiredAllocationSize(const VkDescriptorPoolCreateInfo* pCreateInfo) 35 { 36 size_t size = 0; 37 38 for(uint32_t i = 0; i < pCreateInfo->poolSizeCount; i++) 39 { 40 size += pCreateInfo->pPoolSizes[i].descriptorCount * DescriptorSetLayout::GetDescriptorSize(pCreateInfo->pPoolSizes[i].type); 41 } 42 43 return size; 44 } 45 46 VkResult DescriptorPool::allocateSets(uint32_t descriptorSetCount, const VkDescriptorSetLayout* pSetLayouts, VkDescriptorSet* pDescriptorSets) 47 { 48 std::unique_ptr<size_t[]> layoutSizes(new size_t[descriptorSetCount]); 49 for(uint32_t i = 0; i < descriptorSetCount; i++) 50 { 51 pDescriptorSets[i] = VK_NULL_HANDLE; 52 layoutSizes[i] = Cast(pSetLayouts[i])->getSize(); 53 } 54 55 return allocateSets(&(layoutSizes[0]), descriptorSetCount, pDescriptorSets); 56 } 57 58 VkDescriptorSet DescriptorPool::findAvailableMemory(size_t size) 59 { 60 if(nodes.empty()) 61 { 62 return pool; 63 } 64 65 // First, look for space at the end of the pool 66 const auto itLast = nodes.rbegin(); 67 ptrdiff_t itemStart = reinterpret_cast<char*>(itLast->set) - reinterpret_cast<char*>(pool); 68 ptrdiff_t nextItemStart = itemStart + itLast->size; 69 size_t freeSpace = poolSize - nextItemStart; 70 if(freeSpace >= size) 71 { 72 return reinterpret_cast<VkDescriptorSet>(nextItemStart); 73 } 74 75 // Second, look for space at the beginning of the pool 76 const auto itBegin = nodes.end(); 77 freeSpace = reinterpret_cast<char*>(itBegin->set) - reinterpret_cast<char*>(pool); 78 if(freeSpace >= size) 79 { 80 return pool; 81 } 82 83 // Finally, look between existing pool items 84 const auto itEnd = nodes.end(); 85 auto nextIt = itBegin; 86 ++nextIt; 87 for(auto it = itBegin; nextIt != itEnd; ++it, ++nextIt) 88 { 89 VkDescriptorSet freeSpaceStart = reinterpret_cast<VkDescriptorSet>(reinterpret_cast<char*>(it->set) + it->size); 90 freeSpace = reinterpret_cast<char*>(nextIt->set) - reinterpret_cast<char*>(freeSpaceStart); 91 if(freeSpace >= size) 92 { 93 return freeSpaceStart; 94 } 95 } 96 97 return VK_NULL_HANDLE; 98 } 99 100 VkResult DescriptorPool::allocateSets(size_t* sizes, uint32_t numAllocs, VkDescriptorSet* pDescriptorSets) 101 { 102 size_t totalSize = 0; 103 for(uint32_t i = 0; i < numAllocs; i++) 104 { 105 totalSize += sizes[i]; 106 } 107 108 if(totalSize > poolSize) 109 { 110 return VK_ERROR_OUT_OF_POOL_MEMORY; 111 } 112 113 // Attempt to allocate single chunk of memory 114 VkDescriptorSet memory = findAvailableMemory(totalSize); 115 if(memory != VK_NULL_HANDLE) 116 { 117 pDescriptorSets[0] = memory; 118 for(uint32_t i = 1; i < numAllocs; i++) 119 { 120 pDescriptorSets[i] = 121 reinterpret_cast<VkDescriptorSet>(reinterpret_cast<char*>(memory) + sizes[i - 1]); 122 nodes.insert(Node(pDescriptorSets[i], sizes[i])); 123 } 124 return VK_SUCCESS; 125 } 126 127 // Atttempt to allocate each descriptor set separately 128 for(uint32_t i = 0; i < numAllocs; i++) 129 { 130 pDescriptorSets[i] = findAvailableMemory(sizes[i]); 131 if(pDescriptorSets[i] == VK_NULL_HANDLE) 132 { 133 // vkAllocateDescriptorSets can be used to create multiple descriptor sets. If the 134 // creation of any of those descriptor sets fails, then the implementation must 135 // destroy all successfully created descriptor set objects from this command, set 136 // all entries of the pDescriptorSets array to VK_NULL_HANDLE and return the error. 137 for(uint32_t j = 0; j < i; j++) 138 { 139 freeSet(pDescriptorSets[j]); 140 pDescriptorSets[j] = VK_NULL_HANDLE; 141 } 142 return (computeTotalFreeSize() > totalSize) ? VK_ERROR_FRAGMENTED_POOL : VK_ERROR_OUT_OF_POOL_MEMORY; 143 } 144 nodes.insert(Node(pDescriptorSets[i], sizes[i])); 145 } 146 147 return VK_SUCCESS; 148 } 149 150 void DescriptorPool::freeSets(uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets) 151 { 152 for(uint32_t i = 0; i < descriptorSetCount; i++) 153 { 154 freeSet(pDescriptorSets[i]); 155 } 156 } 157 158 void DescriptorPool::freeSet(const VkDescriptorSet descriptorSet) 159 { 160 const auto itEnd = nodes.end(); 161 auto it = std::find(nodes.begin(), itEnd, descriptorSet); 162 if(it != itEnd) 163 { 164 nodes.erase(it); 165 } 166 } 167 168 VkResult DescriptorPool::reset() 169 { 170 nodes.clear(); 171 172 return VK_SUCCESS; 173 } 174 175 size_t DescriptorPool::computeTotalFreeSize() const 176 { 177 size_t totalFreeSize = 0; 178 179 // Compute space at the end of the pool 180 const auto itLast = nodes.rbegin(); 181 totalFreeSize += poolSize - ((reinterpret_cast<char*>(itLast->set) - reinterpret_cast<char*>(pool)) + itLast->size); 182 183 // Compute space at the beginning of the pool 184 const auto itBegin = nodes.end(); 185 totalFreeSize += reinterpret_cast<char*>(itBegin->set) - reinterpret_cast<char*>(pool); 186 187 // Finally, look between existing pool items 188 const auto itEnd = nodes.end(); 189 auto nextIt = itBegin; 190 ++nextIt; 191 for(auto it = itBegin; nextIt != itEnd; ++it, ++nextIt) 192 { 193 totalFreeSize += (reinterpret_cast<char*>(nextIt->set) - reinterpret_cast<char*>(it->set)) - it->size; 194 } 195 196 return totalFreeSize; 197 } 198 199 } // namespace vk