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
17 #include "VkDescriptorSet.hpp"
18 #include "VkDescriptorSetLayout.hpp"
19
20 #include <algorithm>
21 #include <memory>
22
23 namespace {
24
asMemory(VkDescriptorSet descriptorSet)25 inline uint8_t *asMemory(VkDescriptorSet descriptorSet)
26 {
27 return reinterpret_cast<uint8_t *>(vk::Cast(descriptorSet));
28 }
29
30 } // anonymous namespace
31
32 namespace vk {
33
DescriptorPool(const VkDescriptorPoolCreateInfo * pCreateInfo,void * mem)34 DescriptorPool::DescriptorPool(const VkDescriptorPoolCreateInfo *pCreateInfo, void *mem)
35 : pool(static_cast<uint8_t *>(mem))
36 , poolSize(ComputeRequiredAllocationSize(pCreateInfo))
37 {
38 }
39
destroy(const VkAllocationCallbacks * pAllocator)40 void DescriptorPool::destroy(const VkAllocationCallbacks *pAllocator)
41 {
42 vk::freeHostMemory(pool, pAllocator);
43 }
44
ComputeRequiredAllocationSize(const VkDescriptorPoolCreateInfo * pCreateInfo)45 size_t DescriptorPool::ComputeRequiredAllocationSize(const VkDescriptorPoolCreateInfo *pCreateInfo)
46 {
47 size_t size = pCreateInfo->maxSets * sw::align(sizeof(DescriptorSetHeader), 16);
48
49 for(uint32_t i = 0; i < pCreateInfo->poolSizeCount; i++)
50 {
51 uint32_t descriptorSize = DescriptorSetLayout::GetDescriptorSize(pCreateInfo->pPoolSizes[i].type);
52 if(pCreateInfo->pPoolSizes[i].type == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK)
53 {
54 // If type is VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK then descriptorCount
55 // is the number of bytes to allocate for descriptors of this type.
56 size += sw::align(pCreateInfo->pPoolSizes[i].descriptorCount, 16);
57 }
58 else
59 {
60 size += pCreateInfo->pPoolSizes[i].descriptorCount * sw::align(descriptorSize, 16);
61 }
62 }
63
64 return size;
65 }
66
allocateSets(uint32_t descriptorSetCount,const VkDescriptorSetLayout * pSetLayouts,VkDescriptorSet * pDescriptorSets,const VkDescriptorSetVariableDescriptorCountAllocateInfo * variableDescriptorCountAllocateInfo)67 VkResult DescriptorPool::allocateSets(uint32_t descriptorSetCount, const VkDescriptorSetLayout *pSetLayouts, VkDescriptorSet *pDescriptorSets, const VkDescriptorSetVariableDescriptorCountAllocateInfo *variableDescriptorCountAllocateInfo)
68 {
69 const uint32_t *variableDescriptorCounts =
70 (variableDescriptorCountAllocateInfo && (variableDescriptorCountAllocateInfo->descriptorSetCount == descriptorSetCount)) ? variableDescriptorCountAllocateInfo->pDescriptorCounts : nullptr;
71
72 // FIXME (b/119409619): use an allocator here so we can control all memory allocations
73 std::unique_ptr<size_t[]> layoutSizes(new size_t[descriptorSetCount]);
74 for(uint32_t i = 0; i < descriptorSetCount; i++)
75 {
76 pDescriptorSets[i] = VK_NULL_HANDLE;
77 layoutSizes[i] = vk::Cast(pSetLayouts[i])->getDescriptorSetAllocationSize(variableDescriptorCounts ? variableDescriptorCounts[i] : 0);
78 }
79
80 VkResult result = allocateSets(&(layoutSizes[0]), descriptorSetCount, pDescriptorSets);
81 if(result == VK_SUCCESS)
82 {
83 for(uint32_t i = 0; i < descriptorSetCount; i++)
84 {
85 vk::Cast(pSetLayouts[i])->initialize(vk::Cast(pDescriptorSets[i]), variableDescriptorCounts ? variableDescriptorCounts[i] : 0);
86 }
87 }
88 return result;
89 }
90
findAvailableMemory(size_t size)91 uint8_t *DescriptorPool::findAvailableMemory(size_t size)
92 {
93 if(nodes.empty())
94 {
95 return pool;
96 }
97
98 // First, look for space at the end of the pool
99 const auto itLast = nodes.rbegin();
100 ptrdiff_t itemStart = itLast->set - pool;
101 ptrdiff_t nextItemStart = itemStart + itLast->size;
102 size_t freeSpace = poolSize - nextItemStart;
103 if(freeSpace >= size)
104 {
105 return pool + nextItemStart;
106 }
107
108 // Second, look for space at the beginning of the pool
109 const auto itBegin = nodes.begin();
110 freeSpace = itBegin->set - pool;
111 if(freeSpace >= size)
112 {
113 return pool;
114 }
115
116 // Finally, look between existing pool items
117 const auto itEnd = nodes.end();
118 auto nextIt = itBegin;
119 ++nextIt;
120 for(auto it = itBegin; nextIt != itEnd; ++it, ++nextIt)
121 {
122 uint8_t *freeSpaceStart = it->set + it->size;
123 freeSpace = nextIt->set - freeSpaceStart;
124 if(freeSpace >= size)
125 {
126 return freeSpaceStart;
127 }
128 }
129
130 return nullptr;
131 }
132
allocateSets(size_t * sizes,uint32_t numAllocs,VkDescriptorSet * pDescriptorSets)133 VkResult DescriptorPool::allocateSets(size_t *sizes, uint32_t numAllocs, VkDescriptorSet *pDescriptorSets)
134 {
135 size_t totalSize = 0;
136 for(uint32_t i = 0; i < numAllocs; i++)
137 {
138 totalSize += sizes[i];
139 }
140
141 if(totalSize > poolSize)
142 {
143 return VK_ERROR_OUT_OF_POOL_MEMORY;
144 }
145
146 // Attempt to allocate single chunk of memory
147 {
148 uint8_t *memory = findAvailableMemory(totalSize);
149 if(memory)
150 {
151 for(uint32_t i = 0; i < numAllocs; i++)
152 {
153 pDescriptorSets[i] = *(new(memory) DescriptorSet());
154 nodes.insert(Node(memory, sizes[i]));
155 memory += sizes[i];
156 }
157
158 return VK_SUCCESS;
159 }
160 }
161
162 // Attempt to allocate each descriptor set separately
163 for(uint32_t i = 0; i < numAllocs; i++)
164 {
165 uint8_t *memory = findAvailableMemory(sizes[i]);
166 if(memory)
167 {
168 pDescriptorSets[i] = *(new(memory) DescriptorSet());
169 }
170 else
171 {
172 // vkAllocateDescriptorSets can be used to create multiple descriptor sets. If the
173 // creation of any of those descriptor sets fails, then the implementation must
174 // destroy all successfully created descriptor set objects from this command, set
175 // all entries of the pDescriptorSets array to VK_NULL_HANDLE and return the error.
176 for(uint32_t j = 0; j < i; j++)
177 {
178 freeSet(pDescriptorSets[j]);
179 pDescriptorSets[j] = VK_NULL_HANDLE;
180 }
181 return (computeTotalFreeSize() > totalSize) ? VK_ERROR_FRAGMENTED_POOL : VK_ERROR_OUT_OF_POOL_MEMORY;
182 }
183 nodes.insert(Node(memory, sizes[i]));
184 }
185
186 return VK_SUCCESS;
187 }
188
freeSets(uint32_t descriptorSetCount,const VkDescriptorSet * pDescriptorSets)189 void DescriptorPool::freeSets(uint32_t descriptorSetCount, const VkDescriptorSet *pDescriptorSets)
190 {
191 for(uint32_t i = 0; i < descriptorSetCount; i++)
192 {
193 freeSet(pDescriptorSets[i]);
194 }
195 }
196
freeSet(const VkDescriptorSet descriptorSet)197 void DescriptorPool::freeSet(const VkDescriptorSet descriptorSet)
198 {
199 const auto itEnd = nodes.end();
200 auto it = std::find(nodes.begin(), itEnd, asMemory(descriptorSet));
201 if(it != itEnd)
202 {
203 nodes.erase(it);
204 }
205 }
206
reset()207 VkResult DescriptorPool::reset()
208 {
209 nodes.clear();
210
211 return VK_SUCCESS;
212 }
213
computeTotalFreeSize() const214 size_t DescriptorPool::computeTotalFreeSize() const
215 {
216 size_t totalFreeSize = 0;
217
218 // Compute space at the end of the pool
219 const auto itLast = nodes.rbegin();
220 totalFreeSize += poolSize - ((itLast->set - pool) + itLast->size);
221
222 // Compute space at the beginning of the pool
223 const auto itBegin = nodes.begin();
224 totalFreeSize += itBegin->set - pool;
225
226 // Finally, look between existing pool items
227 const auto itEnd = nodes.end();
228 auto nextIt = itBegin;
229 ++nextIt;
230 for(auto it = itBegin; nextIt != itEnd; ++it, ++nextIt)
231 {
232 totalFreeSize += (nextIt->set - it->set) - it->size;
233 }
234
235 return totalFreeSize;
236 }
237
238 } // namespace vk