LCOV - code coverage report
Current view: top level - source/renderer/backend/vulkan - ShaderProgram.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 1 404 0.2 %
Date: 2023-01-19 00:18:29 Functions: 2 28 7.1 %

          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

Generated by: LCOV version 1.13