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 "ShaderProgram.h"
21 :
22 : #include "graphics/ShaderDefines.h"
23 : #include "ps/CLogger.h"
24 : #include "ps/CStr.h"
25 : #include "ps/CStrInternStatic.h"
26 : #include "ps/Filesystem.h"
27 : #include "ps/Profile.h"
28 : #include "ps/XML/Xeromyces.h"
29 : #include "renderer/backend/vulkan/DescriptorManager.h"
30 : #include "renderer/backend/vulkan/Device.h"
31 : #include "renderer/backend/vulkan/Texture.h"
32 :
33 : #include <algorithm>
34 : #include <limits>
35 :
36 : namespace Renderer
37 : {
38 :
39 : namespace Backend
40 : {
41 :
42 : namespace Vulkan
43 : {
44 :
45 : namespace
46 : {
47 :
48 0 : VkShaderModule CreateShaderModule(CDevice* device, const VfsPath& path)
49 : {
50 0 : CVFSFile file;
51 0 : if (file.Load(g_VFS, path) != PSRETURN_OK)
52 : {
53 0 : LOGERROR("Failed to load shader file: '%s'", path.string8());
54 0 : return VK_NULL_HANDLE;
55 : }
56 :
57 0 : VkShaderModuleCreateInfo createInfo{};
58 0 : createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
59 : // Casting to uint32_t requires to fit alignment and size.
60 0 : ENSURE(file.GetBufferSize() % 4 == 0);
61 0 : ENSURE(reinterpret_cast<uintptr_t>(file.GetBuffer()) % alignof(uint32_t) == 0u);
62 0 : createInfo.codeSize = file.GetBufferSize();
63 0 : createInfo.pCode = reinterpret_cast<const uint32_t*>(file.GetBuffer());
64 :
65 : VkShaderModule shaderModule;
66 0 : if (vkCreateShaderModule(device->GetVkDevice(), &createInfo, nullptr, &shaderModule) != VK_SUCCESS)
67 : {
68 0 : LOGERROR("Failed to create shader module from file: '%s'", path.string8());
69 0 : return VK_NULL_HANDLE;
70 : }
71 0 : device->SetObjectName(VK_OBJECT_TYPE_SHADER_MODULE, shaderModule, path.string8().c_str());
72 0 : return shaderModule;
73 : }
74 :
75 0 : VfsPath FindProgramMatchingDefines(const VfsPath& xmlFilename, const CShaderDefines& defines)
76 : {
77 0 : CXeromyces xeroFile;
78 0 : PSRETURN ret = xeroFile.Load(g_VFS, xmlFilename);
79 0 : if (ret != PSRETURN_OK)
80 0 : return {};
81 :
82 : // TODO: add XML validation.
83 :
84 : #define EL(x) const int el_##x = xeroFile.GetElementID(#x)
85 : #define AT(x) const int at_##x = xeroFile.GetAttributeID(#x)
86 0 : EL(define);
87 0 : EL(defines);
88 0 : EL(program);
89 0 : AT(file);
90 0 : AT(name);
91 0 : AT(value);
92 : #undef AT
93 : #undef EL
94 :
95 0 : const CStrIntern strUndefined("UNDEFINED");
96 0 : VfsPath programFilename;
97 0 : XMBElement root = xeroFile.GetRoot();
98 0 : XERO_ITER_EL(root, rootChild)
99 : {
100 0 : if (rootChild.GetNodeName() == el_program)
101 : {
102 0 : CShaderDefines programDefines;
103 0 : XERO_ITER_EL(rootChild, programChild)
104 : {
105 0 : if (programChild.GetNodeName() == el_defines)
106 : {
107 0 : XERO_ITER_EL(programChild, definesChild)
108 : {
109 0 : XMBAttributeList attributes = definesChild.GetAttributes();
110 0 : if (definesChild.GetNodeName() == el_define)
111 : {
112 0 : const CStrIntern value(attributes.GetNamedItem(at_value));
113 0 : if (value == strUndefined)
114 0 : continue;
115 0 : programDefines.Add(
116 0 : CStrIntern(attributes.GetNamedItem(at_name)), value);
117 : }
118 : }
119 : }
120 : }
121 :
122 0 : if (programDefines == defines)
123 0 : return L"shaders/" + rootChild.GetAttributes().GetNamedItem(at_file).FromUTF8();
124 : }
125 : }
126 :
127 0 : return {};
128 : }
129 :
130 : } // anonymous namespace
131 :
132 0 : IDevice* CVertexInputLayout::GetDevice()
133 : {
134 0 : return m_Device;
135 : }
136 :
137 : // static
138 0 : std::unique_ptr<CShaderProgram> CShaderProgram::Create(
139 : CDevice* device, const CStr& name, const CShaderDefines& baseDefines)
140 : {
141 0 : const VfsPath xmlFilename = L"shaders/" + wstring_from_utf8(name) + L".xml";
142 :
143 0 : std::unique_ptr<CShaderProgram> shaderProgram(new CShaderProgram());
144 0 : shaderProgram->m_Device = device;
145 0 : shaderProgram->m_FileDependencies = {xmlFilename};
146 :
147 0 : CShaderDefines defines = baseDefines;
148 0 : if (device->GetDescriptorManager().UseDescriptorIndexing())
149 0 : defines.Add(str_USE_DESCRIPTOR_INDEXING, str_1);
150 :
151 0 : const VfsPath programFilename = FindProgramMatchingDefines(xmlFilename, defines);
152 0 : if (programFilename.empty())
153 : {
154 0 : LOGERROR("Program '%s' with required defines not found.", name);
155 0 : for (const auto& pair : defines.GetMap())
156 0 : LOGERROR(" \"%s\": \"%s\"", pair.first.c_str(), pair.second.c_str());
157 0 : return nullptr;
158 : }
159 0 : shaderProgram->m_FileDependencies.emplace_back(programFilename);
160 :
161 0 : CXeromyces programXeroFile;
162 0 : if (programXeroFile.Load(g_VFS, programFilename) != PSRETURN_OK)
163 0 : return nullptr;
164 0 : XMBElement programRoot = programXeroFile.GetRoot();
165 :
166 : #define EL(x) const int el_##x = programXeroFile.GetElementID(#x)
167 : #define AT(x) const int at_##x = programXeroFile.GetAttributeID(#x)
168 0 : EL(binding);
169 0 : EL(descriptor_set);
170 0 : EL(descriptor_sets);
171 0 : EL(fragment);
172 0 : EL(member);
173 0 : EL(push_constant);
174 0 : EL(stream);
175 0 : EL(vertex);
176 0 : AT(binding);
177 0 : AT(file);
178 0 : AT(location);
179 0 : AT(name);
180 0 : AT(offset);
181 0 : AT(set);
182 0 : AT(size);
183 0 : AT(type);
184 : #undef AT
185 : #undef EL
186 :
187 : auto addPushConstant =
188 0 : [&pushConstants=shaderProgram->m_PushConstants, &pushConstantDataFlags=shaderProgram->m_PushConstantDataFlags, &at_name, &at_offset, &at_size](
189 0 : const XMBElement& element, VkShaderStageFlags stageFlags) -> bool
190 0 : {
191 0 : const XMBAttributeList attributes = element.GetAttributes();
192 0 : const CStrIntern name = CStrIntern(attributes.GetNamedItem(at_name));
193 0 : const uint32_t size = attributes.GetNamedItem(at_size).ToUInt();
194 0 : const uint32_t offset = attributes.GetNamedItem(at_offset).ToUInt();
195 0 : if (offset % 4 != 0 || size % 4 != 0)
196 : {
197 0 : LOGERROR("Push constant should have offset and size be multiple of 4.");
198 0 : return false;
199 : }
200 0 : for (PushConstant& pushConstant : pushConstants)
201 : {
202 0 : if (pushConstant.name == name)
203 : {
204 0 : if (size != pushConstant.size || offset != pushConstant.offset)
205 : {
206 0 : LOGERROR("All shared push constants must have the same size and offset.");
207 0 : return false;
208 : }
209 : // We found the same constant so we don't need to add it again.
210 0 : pushConstant.stageFlags |= stageFlags;
211 0 : for (uint32_t index = 0; index < (size >> 2); ++index)
212 0 : pushConstantDataFlags[(offset >> 2) + index] |= stageFlags;
213 0 : return true;
214 : }
215 0 : if (offset + size < pushConstant.offset || offset >= pushConstant.offset + pushConstant.size)
216 0 : continue;
217 0 : LOGERROR("All push constant must not intersect each other in memory.");
218 0 : return false;
219 : }
220 0 : pushConstants.push_back({name, offset, size, stageFlags});
221 0 : for (uint32_t index = 0; index < (size >> 2); ++index)
222 0 : pushConstantDataFlags[(offset >> 2) + index] = stageFlags;
223 0 : return true;
224 0 : };
225 :
226 0 : auto addDescriptorSets = [&](const XMBElement& element) -> bool
227 : {
228 : const bool useDescriptorIndexing =
229 0 : device->GetDescriptorManager().UseDescriptorIndexing();
230 : // TODO: reduce the indentation.
231 0 : XERO_ITER_EL(element, descriporSetsChild)
232 : {
233 0 : if (descriporSetsChild.GetNodeName() == el_descriptor_set)
234 : {
235 0 : const uint32_t set = descriporSetsChild.GetAttributes().GetNamedItem(at_set).ToUInt();
236 0 : if (useDescriptorIndexing && set == 0 && !descriporSetsChild.GetChildNodes().empty())
237 : {
238 0 : LOGERROR("Descritor set for descriptor indexing shouldn't contain bindings.");
239 0 : return false;
240 : }
241 0 : XERO_ITER_EL(descriporSetsChild, descriporSetChild)
242 : {
243 0 : if (descriporSetChild.GetNodeName() == el_binding)
244 : {
245 0 : const XMBAttributeList attributes = descriporSetChild.GetAttributes();
246 0 : const uint32_t binding = attributes.GetNamedItem(at_binding).ToUInt();
247 0 : const uint32_t size = attributes.GetNamedItem(at_size).ToUInt();
248 0 : const CStr type = attributes.GetNamedItem(at_type);
249 0 : if (type == "uniform")
250 : {
251 : const uint32_t expectedSet =
252 0 : device->GetDescriptorManager().GetUniformSet();
253 0 : if (set != expectedSet || binding != 0)
254 : {
255 0 : LOGERROR("We support only a single uniform block per shader program.");
256 0 : return false;
257 : }
258 0 : shaderProgram->m_MaterialConstantsDataSize = size;
259 0 : XERO_ITER_EL(descriporSetChild, bindingChild)
260 : {
261 0 : if (bindingChild.GetNodeName() == el_member)
262 : {
263 0 : const XMBAttributeList memberAttributes = bindingChild.GetAttributes();
264 0 : const uint32_t offset = memberAttributes.GetNamedItem(at_offset).ToUInt();
265 0 : const uint32_t size = memberAttributes.GetNamedItem(at_size).ToUInt();
266 0 : const CStrIntern name{memberAttributes.GetNamedItem(at_name)};
267 0 : bool found = false;
268 0 : for (const Uniform& uniform : shaderProgram->m_Uniforms)
269 : {
270 0 : if (uniform.name == name)
271 : {
272 0 : if (offset != uniform.offset || size != uniform.size)
273 : {
274 0 : LOGERROR("All uniforms across all stage should match.");
275 0 : return false;
276 : }
277 0 : found = true;
278 : }
279 : else
280 : {
281 0 : if (offset + size <= uniform.offset || uniform.offset + uniform.size <= offset)
282 0 : continue;
283 0 : LOGERROR("Uniforms must not overlap each other.");
284 0 : return false;
285 : }
286 : }
287 0 : if (!found)
288 0 : shaderProgram->m_Uniforms.push_back({name, offset, size});
289 : }
290 : }
291 : }
292 0 : else if (type == "sampler1D" || type == "sampler2D" || type == "sampler2DShadow" || type == "sampler3D" || type == "samplerCube")
293 : {
294 0 : if (useDescriptorIndexing)
295 : {
296 0 : LOGERROR("We support only uniform descriptor sets with enabled descriptor indexing.");
297 0 : return false;
298 : }
299 0 : const CStrIntern name{attributes.GetNamedItem(at_name)};
300 0 : shaderProgram->m_TextureMapping[name] = binding;
301 0 : shaderProgram->m_TexturesDescriptorSetSize =
302 0 : std::max(shaderProgram->m_TexturesDescriptorSetSize, binding + 1);
303 : }
304 : else
305 : {
306 0 : LOGERROR("Unsupported binding: '%s'", type.c_str());
307 0 : return false;
308 : }
309 : }
310 : }
311 : }
312 : }
313 0 : return true;
314 0 : };
315 :
316 0 : XERO_ITER_EL(programRoot, programChild)
317 : {
318 0 : if (programChild.GetNodeName() == el_vertex)
319 : {
320 : const VfsPath shaderModulePath =
321 0 : L"shaders/" + programChild.GetAttributes().GetNamedItem(at_file).FromUTF8();
322 0 : shaderProgram->m_FileDependencies.emplace_back(shaderModulePath);
323 0 : shaderProgram->m_ShaderModules.emplace_back(
324 0 : CreateShaderModule(device, shaderModulePath));
325 0 : if (shaderProgram->m_ShaderModules.back() == VK_NULL_HANDLE)
326 0 : return nullptr;
327 0 : VkPipelineShaderStageCreateInfo vertexShaderStageInfo{};
328 0 : vertexShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
329 0 : vertexShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
330 0 : vertexShaderStageInfo.module = shaderProgram->m_ShaderModules.back();
331 0 : vertexShaderStageInfo.pName = "main";
332 0 : shaderProgram->m_Stages.emplace_back(std::move(vertexShaderStageInfo));
333 0 : XERO_ITER_EL(programChild, stageChild)
334 : {
335 0 : if (stageChild.GetNodeName() == el_stream)
336 : {
337 0 : XMBAttributeList attributes = stageChild.GetAttributes();
338 0 : const uint32_t location = attributes.GetNamedItem(at_location).ToUInt();
339 0 : const CStr streamName = attributes.GetNamedItem(at_name);
340 0 : VertexAttributeStream stream = VertexAttributeStream::UV7;
341 0 : if (streamName == "pos")
342 0 : stream = VertexAttributeStream::POSITION;
343 0 : else if (streamName == "normal")
344 0 : stream = VertexAttributeStream::NORMAL;
345 0 : else if (streamName == "color")
346 0 : stream = VertexAttributeStream::COLOR;
347 0 : else if (streamName == "uv0")
348 0 : stream = VertexAttributeStream::UV0;
349 0 : else if (streamName == "uv1")
350 0 : stream = VertexAttributeStream::UV1;
351 0 : else if (streamName == "uv2")
352 0 : stream = VertexAttributeStream::UV2;
353 0 : else if (streamName == "uv3")
354 0 : stream = VertexAttributeStream::UV3;
355 0 : else if (streamName == "uv4")
356 0 : stream = VertexAttributeStream::UV4;
357 0 : else if (streamName == "uv5")
358 0 : stream = VertexAttributeStream::UV5;
359 0 : else if (streamName == "uv6")
360 0 : stream = VertexAttributeStream::UV6;
361 0 : else if (streamName == "uv7")
362 0 : stream = VertexAttributeStream::UV7;
363 : else
364 0 : debug_warn("Unknown stream");
365 0 : shaderProgram->m_StreamLocations[stream] = location;
366 : }
367 0 : else if (stageChild.GetNodeName() == el_push_constant)
368 : {
369 0 : if (!addPushConstant(stageChild, VK_SHADER_STAGE_VERTEX_BIT))
370 0 : return nullptr;
371 : }
372 0 : else if (stageChild.GetNodeName() == el_descriptor_sets)
373 : {
374 0 : if (!addDescriptorSets(stageChild))
375 0 : return nullptr;
376 : }
377 : }
378 : }
379 0 : else if (programChild.GetNodeName() == el_fragment)
380 : {
381 : const VfsPath shaderModulePath =
382 0 : L"shaders/" + programChild.GetAttributes().GetNamedItem(at_file).FromUTF8();
383 0 : shaderProgram->m_FileDependencies.emplace_back(shaderModulePath);
384 0 : shaderProgram->m_ShaderModules.emplace_back(
385 0 : CreateShaderModule(device, shaderModulePath));
386 0 : if (shaderProgram->m_ShaderModules.back() == VK_NULL_HANDLE)
387 0 : return nullptr;
388 0 : VkPipelineShaderStageCreateInfo fragmentShaderStageInfo{};
389 0 : fragmentShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
390 0 : fragmentShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
391 0 : fragmentShaderStageInfo.module = shaderProgram->m_ShaderModules.back();
392 0 : fragmentShaderStageInfo.pName = "main";
393 0 : shaderProgram->m_Stages.emplace_back(std::move(fragmentShaderStageInfo));
394 0 : XERO_ITER_EL(programChild, stageChild)
395 : {
396 0 : if (stageChild.GetNodeName() == el_push_constant)
397 : {
398 0 : if (!addPushConstant(stageChild, VK_SHADER_STAGE_FRAGMENT_BIT))
399 0 : return nullptr;
400 : }
401 0 : else if (stageChild.GetNodeName() == el_descriptor_sets)
402 : {
403 0 : if (!addDescriptorSets(stageChild))
404 0 : return nullptr;
405 : }
406 : }
407 : }
408 : }
409 :
410 0 : if (shaderProgram->m_Stages.empty())
411 : {
412 0 : LOGERROR("Program should contain at least one stage.");
413 0 : return nullptr;
414 : }
415 :
416 0 : for (size_t index = 0; index < shaderProgram->m_PushConstants.size(); ++index)
417 0 : shaderProgram->m_PushConstantMapping[shaderProgram->m_PushConstants[index].name] = index;
418 0 : std::vector<VkPushConstantRange> pushConstantRanges;
419 0 : pushConstantRanges.reserve(shaderProgram->m_PushConstants.size());
420 0 : std::transform(
421 0 : shaderProgram->m_PushConstants.begin(), shaderProgram->m_PushConstants.end(),
422 0 : std::back_insert_iterator(pushConstantRanges), [](const PushConstant& pushConstant)
423 : {
424 0 : return VkPushConstantRange{pushConstant.stageFlags, pushConstant.offset, pushConstant.size};
425 0 : });
426 0 : if (!pushConstantRanges.empty())
427 : {
428 0 : std::sort(pushConstantRanges.begin(), pushConstantRanges.end(),
429 0 : [](const VkPushConstantRange& lhs, const VkPushConstantRange& rhs)
430 : {
431 0 : return lhs.offset < rhs.offset;
432 0 : });
433 : // Merge subsequent constants.
434 0 : auto it = pushConstantRanges.begin();
435 0 : while (std::next(it) != pushConstantRanges.end())
436 : {
437 0 : auto next = std::next(it);
438 0 : if (it->stageFlags == next->stageFlags)
439 : {
440 0 : it->size = next->offset - it->offset + next->size;
441 0 : pushConstantRanges.erase(next);
442 : }
443 : else
444 0 : it = next;
445 : }
446 0 : for (const VkPushConstantRange& range : pushConstantRanges)
447 0 : if (std::count_if(pushConstantRanges.begin(), pushConstantRanges.end(),
448 0 : [stageFlags=range.stageFlags](const VkPushConstantRange& range) { return range.stageFlags & stageFlags; }) != 1)
449 : {
450 0 : LOGERROR("Any two range must not include the same stage in stageFlags.");
451 0 : return nullptr;
452 : }
453 : }
454 :
455 0 : for (size_t index = 0; index < shaderProgram->m_Uniforms.size(); ++index)
456 0 : shaderProgram->m_UniformMapping[shaderProgram->m_Uniforms[index].name] = index;
457 0 : if (!shaderProgram->m_Uniforms.empty())
458 : {
459 0 : if (shaderProgram->m_MaterialConstantsDataSize > device->GetChoosenPhysicalDevice().properties.limits.maxUniformBufferRange)
460 : {
461 0 : LOGERROR("Uniform buffer size is too big for the device.");
462 0 : return nullptr;
463 : }
464 0 : shaderProgram->m_MaterialConstantsData =
465 0 : std::make_unique<std::byte[]>(shaderProgram->m_MaterialConstantsDataSize);
466 : }
467 :
468 : std::vector<VkDescriptorSetLayout> layouts =
469 0 : device->GetDescriptorManager().GetDescriptorSetLayouts();
470 0 : if (shaderProgram->m_TexturesDescriptorSetSize > 0)
471 : {
472 0 : ENSURE(!device->GetDescriptorManager().UseDescriptorIndexing());
473 0 : shaderProgram->m_BoundTextures.resize(shaderProgram->m_TexturesDescriptorSetSize);
474 0 : shaderProgram->m_BoundTexturesUID.resize(shaderProgram->m_TexturesDescriptorSetSize);
475 0 : shaderProgram->m_BoundTexturesOutdated = true;
476 0 : shaderProgram->m_TexturesDescriptorSetLayout =
477 0 : device->GetDescriptorManager().GetSingleTypeDescritorSetLayout(
478 0 : VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, shaderProgram->m_TexturesDescriptorSetSize);
479 0 : layouts.emplace_back(shaderProgram->m_TexturesDescriptorSetLayout);
480 : }
481 :
482 0 : VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{};
483 0 : pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
484 0 : pipelineLayoutCreateInfo.setLayoutCount = layouts.size();
485 0 : pipelineLayoutCreateInfo.pSetLayouts = layouts.data();
486 0 : pipelineLayoutCreateInfo.pushConstantRangeCount = pushConstantRanges.size();
487 0 : pipelineLayoutCreateInfo.pPushConstantRanges = pushConstantRanges.data();
488 :
489 0 : const VkResult result = vkCreatePipelineLayout(
490 : device->GetVkDevice(), &pipelineLayoutCreateInfo, nullptr,
491 0 : &shaderProgram->m_PipelineLayout);
492 0 : if (result != VK_SUCCESS)
493 : {
494 0 : LOGERROR("Failed to create a pipeline layout: %d", static_cast<int>(result));
495 0 : return nullptr;
496 : }
497 :
498 0 : return shaderProgram;
499 : }
500 :
501 : CShaderProgram::CShaderProgram() = default;
502 :
503 0 : CShaderProgram::~CShaderProgram()
504 : {
505 0 : if (m_PipelineLayout != VK_NULL_HANDLE)
506 0 : m_Device->ScheduleObjectToDestroy(VK_OBJECT_TYPE_PIPELINE_LAYOUT, m_PipelineLayout, VK_NULL_HANDLE);
507 0 : for (VkShaderModule shaderModule : m_ShaderModules)
508 0 : if (shaderModule != VK_NULL_HANDLE)
509 0 : m_Device->ScheduleObjectToDestroy(VK_OBJECT_TYPE_SHADER_MODULE, shaderModule, VK_NULL_HANDLE);
510 0 : }
511 :
512 0 : IDevice* CShaderProgram::GetDevice()
513 : {
514 0 : return m_Device;
515 : }
516 :
517 0 : int32_t CShaderProgram::GetBindingSlot(const CStrIntern name) const
518 : {
519 0 : if (auto it = m_PushConstantMapping.find(name); it != m_PushConstantMapping.end())
520 0 : return it->second;
521 0 : if (auto it = m_UniformMapping.find(name); it != m_UniformMapping.end())
522 0 : return it->second + m_PushConstants.size();
523 0 : if (auto it = m_TextureMapping.find(name); it != m_TextureMapping.end())
524 0 : return it->second + m_PushConstants.size() + m_UniformMapping.size();
525 0 : return -1;
526 : }
527 :
528 0 : std::vector<VfsPath> CShaderProgram::GetFileDependencies() const
529 : {
530 0 : return m_FileDependencies;
531 : }
532 :
533 0 : uint32_t CShaderProgram::GetStreamLocation(const VertexAttributeStream stream) const
534 : {
535 0 : auto it = m_StreamLocations.find(stream);
536 0 : return it != m_StreamLocations.end() ? it->second : std::numeric_limits<uint32_t>::max();
537 : }
538 :
539 0 : void CShaderProgram::Bind()
540 : {
541 0 : if (m_MaterialConstantsData)
542 0 : m_MaterialConstantsDataOutdated = true;
543 0 : }
544 :
545 0 : void CShaderProgram::Unbind()
546 : {
547 0 : if (m_TexturesDescriptorSetSize > 0)
548 : {
549 0 : for (CTexture*& texture : m_BoundTextures)
550 0 : texture = nullptr;
551 0 : for (CTexture::UID& uid : m_BoundTexturesUID)
552 0 : uid = 0;
553 0 : m_BoundTexturesOutdated = true;
554 : }
555 0 : }
556 :
557 0 : void CShaderProgram::PreDraw(VkCommandBuffer commandBuffer)
558 : {
559 0 : UpdateActiveDescriptorSet(commandBuffer);
560 0 : if (m_PushConstantDataMask)
561 : {
562 0 : for (uint32_t index = 0; index < 32;)
563 : {
564 0 : if (!(m_PushConstantDataMask & (1 << index)))
565 : {
566 0 : ++index;
567 0 : continue;
568 : }
569 0 : uint32_t indexEnd = index + 1;
570 0 : while (indexEnd < 32 && (m_PushConstantDataMask & (1 << indexEnd)) && m_PushConstantDataFlags[index] == m_PushConstantDataFlags[indexEnd])
571 0 : ++indexEnd;
572 0 : vkCmdPushConstants(
573 : commandBuffer, GetPipelineLayout(),
574 0 : m_PushConstantDataFlags[index],
575 0 : index * 4, (indexEnd - index) * 4, m_PushConstantData.data() + index * 4);
576 0 : index = indexEnd;
577 : }
578 0 : m_PushConstantDataMask = 0;
579 : }
580 0 : }
581 :
582 0 : void CShaderProgram::UpdateActiveDescriptorSet(
583 : VkCommandBuffer commandBuffer)
584 : {
585 0 : if (m_BoundTexturesOutdated)
586 : {
587 0 : m_BoundTexturesOutdated = false;
588 :
589 0 : m_ActiveTexturesDescriptorSet =
590 0 : m_Device->GetDescriptorManager().GetSingleTypeDescritorSet(
591 : VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, m_TexturesDescriptorSetLayout,
592 : m_BoundTexturesUID, m_BoundTextures);
593 0 : ENSURE(m_ActiveTexturesDescriptorSet != VK_NULL_HANDLE);
594 :
595 0 : vkCmdBindDescriptorSets(
596 : commandBuffer, GetPipelineBindPoint(), GetPipelineLayout(),
597 0 : 1, 1, &m_ActiveTexturesDescriptorSet, 0, nullptr);
598 : }
599 0 : }
600 :
601 0 : void CShaderProgram::SetUniform(
602 : const int32_t bindingSlot,
603 : const float value)
604 : {
605 0 : const float values[1] = {value};
606 0 : SetUniform(bindingSlot, PS::span<const float>(values, values + 1));
607 0 : }
608 :
609 0 : void CShaderProgram::SetUniform(
610 : const int32_t bindingSlot,
611 : const float valueX, const float valueY)
612 : {
613 0 : const float values[2] = {valueX, valueY};
614 0 : SetUniform(bindingSlot, PS::span<const float>(values, values + 2));
615 0 : }
616 :
617 0 : void CShaderProgram::SetUniform(
618 : const int32_t bindingSlot,
619 : const float valueX, const float valueY,
620 : const float valueZ)
621 : {
622 0 : const float values[3] = {valueX, valueY, valueZ};
623 0 : SetUniform(bindingSlot, PS::span<const float>(values, values + 3));
624 0 : }
625 :
626 0 : void CShaderProgram::SetUniform(
627 : const int32_t bindingSlot,
628 : const float valueX, const float valueY,
629 : const float valueZ, const float valueW)
630 : {
631 0 : const float values[4] = {valueX, valueY, valueZ, valueW};
632 0 : SetUniform(bindingSlot, PS::span<const float>(values, values + 4));
633 0 : }
634 :
635 0 : void CShaderProgram::SetUniform(const int32_t bindingSlot, PS::span<const float> values)
636 : {
637 0 : if (bindingSlot < 0)
638 0 : return;
639 0 : const auto data = GetUniformData(bindingSlot, values.size() * sizeof(float));
640 0 : std::memcpy(data.first, values.data(), data.second);
641 : }
642 :
643 0 : std::pair<std::byte*, uint32_t> CShaderProgram::GetUniformData(
644 : const int32_t bindingSlot, const uint32_t dataSize)
645 : {
646 0 : if (bindingSlot < static_cast<int32_t>(m_PushConstants.size()))
647 : {
648 0 : const uint32_t size = m_PushConstants[bindingSlot].size;
649 0 : const uint32_t offset = m_PushConstants[bindingSlot].offset;
650 0 : ENSURE(size <= dataSize);
651 0 : m_PushConstantDataMask |= ((1 << (size >> 2)) - 1) << (offset >> 2);
652 0 : return {m_PushConstantData.data() + offset, size};
653 : }
654 : else
655 : {
656 0 : ENSURE(bindingSlot - m_PushConstants.size() < m_Uniforms.size());
657 0 : const Uniform& uniform = m_Uniforms[bindingSlot - m_PushConstants.size()];
658 0 : m_MaterialConstantsDataOutdated = true;
659 0 : const uint32_t size = uniform.size;
660 0 : const uint32_t offset = uniform.offset;
661 0 : ENSURE(size <= dataSize);
662 0 : return {m_MaterialConstantsData.get() + offset, size};
663 : }
664 : }
665 :
666 0 : void CShaderProgram::SetTexture(const int32_t bindingSlot, CTexture* texture)
667 : {
668 0 : if (bindingSlot < 0)
669 0 : return;
670 0 : CDescriptorManager& descriptorManager = m_Device->GetDescriptorManager();
671 0 : if (descriptorManager.UseDescriptorIndexing())
672 : {
673 0 : const uint32_t descriptorIndex = descriptorManager.GetTextureDescriptor(texture->As<CTexture>());
674 0 : ENSURE(bindingSlot < static_cast<int32_t>(m_PushConstants.size()));
675 :
676 0 : const uint32_t size = m_PushConstants[bindingSlot].size;
677 0 : const uint32_t offset = m_PushConstants[bindingSlot].offset;
678 0 : ENSURE(size == sizeof(descriptorIndex));
679 0 : std::memcpy(m_PushConstantData.data() + offset, &descriptorIndex, size);
680 0 : m_PushConstantDataMask |= ((1 << (size >> 2)) - 1) << (offset >> 2);
681 : }
682 : else
683 : {
684 0 : ENSURE(bindingSlot >= static_cast<int32_t>(m_PushConstants.size() + m_UniformMapping.size()));
685 0 : const uint32_t index = bindingSlot - (m_PushConstants.size() + m_UniformMapping.size());
686 0 : if (m_BoundTexturesUID[index] != texture->GetUID())
687 : {
688 0 : m_BoundTextures[index] = texture;
689 0 : m_BoundTexturesUID[index] = texture->GetUID();
690 0 : m_BoundTexturesOutdated = true;
691 : }
692 : }
693 : }
694 :
695 : } // namespace Vulkan
696 :
697 : } // namespace Backend
698 :
699 3 : } // namespace Renderer
|