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