xref: /aosp_15_r20/external/swiftshader/src/Vulkan/VkDescriptorPool.cpp (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
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