• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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