Line data Source code
1 : /* Copyright (C) 2023 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "DescriptorManager.h"
21 :
22 : #include "lib/hash.h"
23 : #include "ps/containers/StaticVector.h"
24 : #include "renderer/backend/vulkan/Device.h"
25 : #include "renderer/backend/vulkan/Mapping.h"
26 : #include "renderer/backend/vulkan/Texture.h"
27 : #include "renderer/backend/vulkan/Utilities.h"
28 :
29 : #include <array>
30 : #include <numeric>
31 :
32 : namespace Renderer
33 : {
34 :
35 : namespace Backend
36 : {
37 :
38 : namespace Vulkan
39 : {
40 :
41 0 : CDescriptorManager::CDescriptorManager(CDevice* device, const bool useDescriptorIndexing)
42 0 : : m_Device(device), m_UseDescriptorIndexing(useDescriptorIndexing)
43 : {
44 0 : if (useDescriptorIndexing)
45 : {
46 0 : VkDescriptorPoolSize descriptorPoolSize{};
47 0 : descriptorPoolSize.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
48 0 : descriptorPoolSize.descriptorCount = DESCRIPTOR_INDEXING_BINDING_SIZE * NUMBER_OF_BINDINGS_PER_DESCRIPTOR_INDEXING_SET;
49 :
50 0 : VkDescriptorPoolCreateInfo descriptorPoolCreateInfo{};
51 0 : descriptorPoolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
52 0 : descriptorPoolCreateInfo.poolSizeCount = 1;
53 0 : descriptorPoolCreateInfo.pPoolSizes = &descriptorPoolSize;
54 0 : descriptorPoolCreateInfo.maxSets = 1;
55 0 : descriptorPoolCreateInfo.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT_EXT;
56 0 : ENSURE_VK_SUCCESS(vkCreateDescriptorPool(
57 : device->GetVkDevice(), &descriptorPoolCreateInfo, nullptr, &m_DescriptorIndexingPool));
58 :
59 0 : const VkShaderStageFlags stageFlags =
60 : VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_COMPUTE_BIT;
61 0 : const std::array<VkDescriptorSetLayoutBinding, NUMBER_OF_BINDINGS_PER_DESCRIPTOR_INDEXING_SET> bindings{{
62 : {0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, DESCRIPTOR_INDEXING_BINDING_SIZE, stageFlags},
63 : {1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, DESCRIPTOR_INDEXING_BINDING_SIZE, stageFlags},
64 : {2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, DESCRIPTOR_INDEXING_BINDING_SIZE, stageFlags}
65 : }};
66 :
67 0 : const VkDescriptorBindingFlagsEXT baseBindingFlags =
68 : VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT
69 : | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT
70 : | VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT_EXT;
71 0 : const std::array<VkDescriptorBindingFlagsEXT, NUMBER_OF_BINDINGS_PER_DESCRIPTOR_INDEXING_SET> bindingFlags{{
72 : baseBindingFlags, baseBindingFlags, baseBindingFlags
73 : }};
74 :
75 0 : VkDescriptorSetLayoutBindingFlagsCreateInfoEXT bindingFlagsCreateInfo{};
76 0 : bindingFlagsCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT;
77 0 : bindingFlagsCreateInfo.bindingCount = bindingFlags.size();
78 0 : bindingFlagsCreateInfo.pBindingFlags = bindingFlags.data();
79 :
80 0 : VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo{};
81 0 : descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
82 0 : descriptorSetLayoutCreateInfo.bindingCount = bindings.size();
83 0 : descriptorSetLayoutCreateInfo.pBindings = bindings.data();
84 0 : descriptorSetLayoutCreateInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT;
85 0 : descriptorSetLayoutCreateInfo.pNext = &bindingFlagsCreateInfo;
86 :
87 0 : ENSURE_VK_SUCCESS(vkCreateDescriptorSetLayout(
88 : device->GetVkDevice(), &descriptorSetLayoutCreateInfo,
89 : nullptr, &m_DescriptorIndexingSetLayout));
90 :
91 0 : m_DescriptorSetLayouts.emplace_back(m_DescriptorIndexingSetLayout);
92 :
93 0 : VkDescriptorSetAllocateInfo descriptorSetAllocateInfo{};
94 0 : descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
95 0 : descriptorSetAllocateInfo.descriptorPool = m_DescriptorIndexingPool;
96 0 : descriptorSetAllocateInfo.descriptorSetCount = 1;
97 0 : descriptorSetAllocateInfo.pSetLayouts = &m_DescriptorIndexingSetLayout;
98 :
99 0 : ENSURE_VK_SUCCESS(vkAllocateDescriptorSets(
100 : device->GetVkDevice(), &descriptorSetAllocateInfo, &m_DescriptorIndexingSet));
101 :
102 0 : for (DescriptorIndexingBindingMap& bindingMap : m_DescriptorIndexingBindings)
103 : {
104 0 : bindingMap.firstFreeIndex = 0;
105 0 : bindingMap.elements.resize(DESCRIPTOR_INDEXING_BINDING_SIZE);
106 0 : std::iota(bindingMap.elements.begin(), std::prev(bindingMap.elements.end()), 1);
107 0 : bindingMap.elements.back() = -1;
108 : }
109 : }
110 :
111 : // Currently we hard-code the layout for uniforms.
112 0 : const VkDescriptorSetLayoutBinding bindings[] =
113 : {
114 : {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_COMPUTE_BIT}
115 : };
116 :
117 0 : VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo{};
118 0 : descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
119 0 : descriptorSetLayoutCreateInfo.bindingCount = std::size(bindings);
120 0 : descriptorSetLayoutCreateInfo.pBindings = bindings;
121 :
122 0 : ENSURE_VK_SUCCESS(vkCreateDescriptorSetLayout(
123 : device->GetVkDevice(), &descriptorSetLayoutCreateInfo,
124 : nullptr, &m_UniformDescriptorSetLayout));
125 0 : m_DescriptorSetLayouts.emplace_back(m_UniformDescriptorSetLayout);
126 0 : }
127 :
128 0 : CDescriptorManager::~CDescriptorManager()
129 : {
130 0 : VkDevice device = m_Device->GetVkDevice();
131 :
132 0 : for (auto& pair: m_SingleTypePools)
133 : {
134 0 : for (SingleTypePool& pool : pair.second)
135 : {
136 0 : if (pool.pool != VK_NULL_HANDLE)
137 0 : vkDestroyDescriptorPool(device, pool.pool, nullptr);
138 0 : if (pool.layout != VK_NULL_HANDLE)
139 0 : vkDestroyDescriptorSetLayout(device, pool.layout, nullptr);
140 : }
141 : }
142 0 : m_SingleTypePools.clear();
143 :
144 0 : for (VkDescriptorSetLayout descriptorSetLayout : m_DescriptorSetLayouts)
145 0 : vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr);
146 0 : m_DescriptorSetLayouts.clear();
147 :
148 0 : if (m_DescriptorIndexingPool != VK_NULL_HANDLE)
149 0 : vkDestroyDescriptorPool(device, m_DescriptorIndexingPool, nullptr);
150 0 : }
151 :
152 0 : CDescriptorManager::SingleTypePool& CDescriptorManager::GetSingleTypePool(
153 : const VkDescriptorType type, const uint32_t size)
154 : {
155 0 : ENSURE(size > 0 && size <= 16);
156 0 : std::vector<SingleTypePool>& pools = m_SingleTypePools[type];
157 0 : if (pools.size() <= size)
158 0 : pools.resize(size + 1);
159 0 : SingleTypePool& pool = pools[size];
160 0 : if (pool.pool == VK_NULL_HANDLE)
161 : {
162 0 : constexpr uint32_t maxSets = 16384;
163 :
164 0 : VkDescriptorPoolSize descriptorPoolSize{};
165 0 : descriptorPoolSize.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
166 0 : descriptorPoolSize.descriptorCount = maxSets * size;
167 :
168 0 : VkDescriptorPoolCreateInfo descriptorPoolCreateInfo{};
169 0 : descriptorPoolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
170 0 : descriptorPoolCreateInfo.poolSizeCount = 1;
171 0 : descriptorPoolCreateInfo.pPoolSizes = &descriptorPoolSize;
172 0 : descriptorPoolCreateInfo.maxSets = maxSets;
173 0 : ENSURE_VK_SUCCESS(vkCreateDescriptorPool(
174 : m_Device->GetVkDevice(), &descriptorPoolCreateInfo, nullptr, &pool.pool));
175 :
176 0 : const VkPipelineStageFlags stageFlags =
177 : VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_COMPUTE_BIT;
178 0 : PS::StaticVector<VkDescriptorSetLayoutBinding, 16> bindings;
179 0 : for (uint32_t index = 0; index < size; ++index)
180 0 : bindings.emplace_back(VkDescriptorSetLayoutBinding{index, type, 1, stageFlags});
181 :
182 0 : VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo{};
183 0 : descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
184 0 : descriptorSetLayoutCreateInfo.bindingCount = size;
185 0 : descriptorSetLayoutCreateInfo.pBindings = bindings.data();
186 :
187 0 : ENSURE_VK_SUCCESS(vkCreateDescriptorSetLayout(
188 : m_Device->GetVkDevice(), &descriptorSetLayoutCreateInfo, nullptr, &pool.layout));
189 :
190 0 : pool.firstFreeIndex = 0;
191 0 : pool.elements.reserve(maxSets);
192 0 : for (uint32_t index = 0; index < maxSets; ++index)
193 0 : pool.elements.push_back({VK_NULL_HANDLE, static_cast<int16_t>(index + 1)});
194 0 : pool.elements.back().second = -1;
195 : }
196 0 : return pool;
197 : }
198 :
199 0 : VkDescriptorSetLayout CDescriptorManager::GetSingleTypeDescritorSetLayout(
200 : VkDescriptorType type, const uint32_t size)
201 : {
202 0 : return GetSingleTypePool(type, size).layout;
203 : }
204 :
205 0 : size_t CDescriptorManager::SingleTypeCacheKeyHash::operator()(const SingleTypeCacheKey& key) const
206 : {
207 0 : size_t seed = 0;
208 0 : hash_combine(seed, key.first);
209 0 : for (CTexture::UID uid : key.second)
210 0 : hash_combine(seed, uid);
211 0 : return seed;
212 : }
213 :
214 0 : VkDescriptorSet CDescriptorManager::GetSingleTypeDescritorSet(
215 : VkDescriptorType type, VkDescriptorSetLayout layout,
216 : const std::vector<CTexture::UID>& texturesUID,
217 : const std::vector<CTexture*>& textures)
218 : {
219 0 : ENSURE(texturesUID.size() == textures.size());
220 0 : ENSURE(!texturesUID.empty());
221 0 : const SingleTypeCacheKey key{layout, texturesUID};
222 0 : auto it = m_SingleTypeSets.find(key);
223 0 : if (it == m_SingleTypeSets.end())
224 : {
225 0 : SingleTypePool& pool = GetSingleTypePool(type, texturesUID.size());
226 0 : const int16_t elementIndex = pool.firstFreeIndex;
227 0 : ENSURE(elementIndex != -1);
228 0 : std::pair<VkDescriptorSet, int16_t>& element = pool.elements[elementIndex];
229 0 : pool.firstFreeIndex = element.second;
230 :
231 0 : if (element.first == VK_NULL_HANDLE)
232 : {
233 0 : VkDescriptorSetAllocateInfo descriptorSetAllocateInfo{};
234 0 : descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
235 0 : descriptorSetAllocateInfo.descriptorPool = pool.pool;
236 0 : descriptorSetAllocateInfo.descriptorSetCount = 1;
237 0 : descriptorSetAllocateInfo.pSetLayouts = &layout;
238 :
239 0 : ENSURE_VK_SUCCESS(vkAllocateDescriptorSets(
240 : m_Device->GetVkDevice(), &descriptorSetAllocateInfo, &element.first));
241 : }
242 :
243 0 : it = m_SingleTypeSets.emplace(key, element.first).first;
244 :
245 0 : for (const CTexture::UID uid : texturesUID)
246 0 : m_TextureSingleTypePoolMap[uid].push_back({type, static_cast<uint8_t>(texturesUID.size()), elementIndex});
247 :
248 0 : PS::StaticVector<VkDescriptorImageInfo, 16> infos;
249 0 : PS::StaticVector<VkWriteDescriptorSet, 16> writes;
250 0 : for (size_t index = 0; index < textures.size(); ++index)
251 : {
252 0 : if (!textures[index])
253 0 : continue;
254 0 : VkDescriptorImageInfo descriptorImageInfo{};
255 0 : descriptorImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
256 0 : descriptorImageInfo.imageView = textures[index]->GetSamplerImageView();
257 0 : descriptorImageInfo.sampler = textures[index]->GetSampler();
258 0 : infos.emplace_back(std::move(descriptorImageInfo));
259 :
260 0 : VkWriteDescriptorSet writeDescriptorSet{};
261 0 : writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
262 0 : writeDescriptorSet.dstSet = element.first;
263 0 : writeDescriptorSet.dstBinding = index;
264 0 : writeDescriptorSet.dstArrayElement = 0;
265 0 : writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
266 0 : writeDescriptorSet.descriptorCount = 1;
267 0 : writeDescriptorSet.pImageInfo = &infos.back();
268 0 : writes.emplace_back(std::move(writeDescriptorSet));
269 : }
270 :
271 0 : vkUpdateDescriptorSets(
272 0 : m_Device->GetVkDevice(), writes.size(), writes.data(), 0, nullptr);
273 : }
274 0 : return it->second;
275 : }
276 :
277 0 : uint32_t CDescriptorManager::GetUniformSet() const
278 : {
279 0 : return m_UseDescriptorIndexing ? 1 : 0;
280 : }
281 :
282 0 : uint32_t CDescriptorManager::GetTextureDescriptor(CTexture* texture)
283 : {
284 0 : ENSURE(m_UseDescriptorIndexing);
285 :
286 0 : uint32_t binding = 0;
287 0 : if (texture->GetType() == ITexture::Type::TEXTURE_2D &&
288 0 : IsDepthFormat(texture->GetFormat()) &&
289 0 : texture->IsCompareEnabled())
290 0 : binding = 2;
291 0 : else if (texture->GetType() == ITexture::Type::TEXTURE_CUBE)
292 0 : binding = 1;
293 :
294 0 : DescriptorIndexingBindingMap& bindingMap = m_DescriptorIndexingBindings[binding];
295 0 : auto it = bindingMap.map.find(texture->GetUID());
296 0 : if (it != bindingMap.map.end())
297 0 : return it->second;
298 0 : m_TextureToBindingMap[texture->GetUID()] = binding;
299 :
300 0 : ENSURE(bindingMap.firstFreeIndex != -1);
301 0 : uint32_t descriptorSetIndex = bindingMap.firstFreeIndex;
302 0 : bindingMap.firstFreeIndex = bindingMap.elements[bindingMap.firstFreeIndex];
303 :
304 0 : ENSURE(texture->GetType() != ITexture::Type::TEXTURE_2D_MULTISAMPLE);
305 :
306 0 : VkDescriptorImageInfo descriptorImageInfo{};
307 0 : descriptorImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
308 0 : descriptorImageInfo.imageView = texture->GetSamplerImageView();
309 0 : descriptorImageInfo.sampler = texture->GetSampler();
310 :
311 0 : VkWriteDescriptorSet writeDescriptorSet{};
312 0 : writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
313 0 : writeDescriptorSet.dstSet = m_DescriptorIndexingSet;
314 0 : writeDescriptorSet.dstBinding = binding;
315 0 : writeDescriptorSet.dstArrayElement = descriptorSetIndex;
316 0 : writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
317 0 : writeDescriptorSet.descriptorCount = 1;
318 0 : writeDescriptorSet.pImageInfo = &descriptorImageInfo;
319 :
320 0 : vkUpdateDescriptorSets(
321 0 : m_Device->GetVkDevice(), 1, &writeDescriptorSet, 0, nullptr);
322 :
323 0 : bindingMap.map[texture->GetUID()] = descriptorSetIndex;
324 :
325 0 : return descriptorSetIndex;
326 : }
327 :
328 0 : void CDescriptorManager::OnTextureDestroy(const CTexture::UID uid)
329 : {
330 0 : if (m_UseDescriptorIndexing)
331 : {
332 : DescriptorIndexingBindingMap& bindingMap =
333 0 : m_DescriptorIndexingBindings[m_TextureToBindingMap[uid]];
334 0 : auto it = bindingMap.map.find(uid);
335 : // It's possible to not have the texture in the map. Because a texture will
336 : // be added to it only in case of usage.
337 0 : if (it == bindingMap.map.end())
338 0 : return;
339 0 : const int16_t index = it->second;
340 0 : bindingMap.elements[index] = bindingMap.firstFreeIndex;
341 0 : bindingMap.firstFreeIndex = index;
342 : }
343 : else
344 : {
345 0 : auto it = m_TextureSingleTypePoolMap.find(uid);
346 0 : if (it == m_TextureSingleTypePoolMap.end())
347 0 : return;
348 0 : for (const auto& entry : it->second)
349 : {
350 0 : SingleTypePool& pool = GetSingleTypePool(std::get<0>(entry), std::get<1>(entry));
351 0 : const int16_t elementIndex = std::get<2>(entry);
352 0 : pool.elements[elementIndex].second = pool.firstFreeIndex;
353 0 : pool.firstFreeIndex = elementIndex;
354 : }
355 : }
356 : }
357 :
358 : } // namespace Vulkan
359 :
360 : } // namespace Backend
361 :
362 3 : } // namespace Renderer
|