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