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)67 VkResult DescriptorPool::allocateSets(uint32_t descriptorSetCount, const VkDescriptorSetLayout *pSetLayouts, VkDescriptorSet *pDescriptorSets)
68 {
69 // FIXME (b/119409619): use an allocator here so we can control all memory allocations
70 std::unique_ptr<size_t[]> layoutSizes(new size_t[descriptorSetCount]);
71 for(uint32_t i = 0; i < descriptorSetCount; i++)
72 {
73 pDescriptorSets[i] = VK_NULL_HANDLE;
74 layoutSizes[i] = vk::Cast(pSetLayouts[i])->getDescriptorSetAllocationSize();
75 }
76
77 VkResult result = allocateSets(&(layoutSizes[0]), descriptorSetCount, pDescriptorSets);
78 if(result == VK_SUCCESS)
79 {
80 for(uint32_t i = 0; i < descriptorSetCount; i++)
81 {
82 vk::Cast(pSetLayouts[i])->initialize(vk::Cast(pDescriptorSets[i]));
83 }
84 }
85 return result;
86 }
87
findAvailableMemory(size_t size)88 uint8_t *DescriptorPool::findAvailableMemory(size_t size)
89 {
90 if(nodes.empty())
91 {
92 return pool;
93 }
94
95 // First, look for space at the end of the pool
96 const auto itLast = nodes.rbegin();
97 ptrdiff_t itemStart = itLast->set - pool;
98 ptrdiff_t nextItemStart = itemStart + itLast->size;
99 size_t freeSpace = poolSize - nextItemStart;
100 if(freeSpace >= size)
101 {
102 return pool + nextItemStart;
103 }
104
105 // Second, look for space at the beginning of the pool
106 const auto itBegin = nodes.begin();
107 freeSpace = itBegin->set - pool;
108 if(freeSpace >= size)
109 {
110 return pool;
111 }
112
113 // Finally, look between existing pool items
114 const auto itEnd = nodes.end();
115 auto nextIt = itBegin;
116 ++nextIt;
117 for(auto it = itBegin; nextIt != itEnd; ++it, ++nextIt)
118 {
119 uint8_t *freeSpaceStart = it->set + it->size;
120 freeSpace = nextIt->set - freeSpaceStart;
121 if(freeSpace >= size)
122 {
123 return freeSpaceStart;
124 }
125 }
126
127 return nullptr;
128 }
129
allocateSets(size_t * sizes,uint32_t numAllocs,VkDescriptorSet * pDescriptorSets)130 VkResult DescriptorPool::allocateSets(size_t *sizes, uint32_t numAllocs, VkDescriptorSet *pDescriptorSets)
131 {
132 size_t totalSize = 0;
133 for(uint32_t i = 0; i < numAllocs; i++)
134 {
135 totalSize += sizes[i];
136 }
137
138 if(totalSize > poolSize)
139 {
140 return VK_ERROR_OUT_OF_POOL_MEMORY;
141 }
142
143 // Attempt to allocate single chunk of memory
144 {
145 uint8_t *memory = findAvailableMemory(totalSize);
146 if(memory)
147 {
148 for(uint32_t i = 0; i < numAllocs; i++)
149 {
150 pDescriptorSets[i] = *(new(memory) DescriptorSet());
151 nodes.insert(Node(memory, sizes[i]));
152 memory += sizes[i];
153 }
154
155 return VK_SUCCESS;
156 }
157 }
158
159 // Attempt to allocate each descriptor set separately
160 for(uint32_t i = 0; i < numAllocs; i++)
161 {
162 uint8_t *memory = findAvailableMemory(sizes[i]);
163 if(memory)
164 {
165 pDescriptorSets[i] = *(new(memory) DescriptorSet());
166 }
167 else
168 {
169 // vkAllocateDescriptorSets can be used to create multiple descriptor sets. If the
170 // creation of any of those descriptor sets fails, then the implementation must
171 // destroy all successfully created descriptor set objects from this command, set
172 // all entries of the pDescriptorSets array to VK_NULL_HANDLE and return the error.
173 for(uint32_t j = 0; j < i; j++)
174 {
175 freeSet(pDescriptorSets[j]);
176 pDescriptorSets[j] = VK_NULL_HANDLE;
177 }
178 return (computeTotalFreeSize() > totalSize) ? VK_ERROR_FRAGMENTED_POOL : VK_ERROR_OUT_OF_POOL_MEMORY;
179 }
180 nodes.insert(Node(memory, sizes[i]));
181 }
182
183 return VK_SUCCESS;
184 }
185
freeSets(uint32_t descriptorSetCount,const VkDescriptorSet * pDescriptorSets)186 void DescriptorPool::freeSets(uint32_t descriptorSetCount, const VkDescriptorSet *pDescriptorSets)
187 {
188 for(uint32_t i = 0; i < descriptorSetCount; i++)
189 {
190 freeSet(pDescriptorSets[i]);
191 }
192 }
193
freeSet(const VkDescriptorSet descriptorSet)194 void DescriptorPool::freeSet(const VkDescriptorSet descriptorSet)
195 {
196 const auto itEnd = nodes.end();
197 auto it = std::find(nodes.begin(), itEnd, asMemory(descriptorSet));
198 if(it != itEnd)
199 {
200 nodes.erase(it);
201 }
202 }
203
reset()204 VkResult DescriptorPool::reset()
205 {
206 nodes.clear();
207
208 return VK_SUCCESS;
209 }
210
computeTotalFreeSize() const211 size_t DescriptorPool::computeTotalFreeSize() const
212 {
213 size_t totalFreeSize = 0;
214
215 // Compute space at the end of the pool
216 const auto itLast = nodes.rbegin();
217 totalFreeSize += poolSize - ((itLast->set - pool) + itLast->size);
218
219 // Compute space at the beginning of the pool
220 const auto itBegin = nodes.begin();
221 totalFreeSize += itBegin->set - pool;
222
223 // Finally, look between existing pool items
224 const auto itEnd = nodes.end();
225 auto nextIt = itBegin;
226 ++nextIt;
227 for(auto it = itBegin; nextIt != itEnd; ++it, ++nextIt)
228 {
229 totalFreeSize += (nextIt->set - it->set) - it->size;
230 }
231
232 return totalFreeSize;
233 }
234
235 } // namespace vk