0ad/source/renderer/backend/vulkan/ShaderProgram.cpp
bb 157c6af18e Make the space in 0 A.D. non-breaking throughout the codebase.
Avoid cases of filenames
Update years in terms and other legal(ish) documents
Don't update years in license headers, since change is not meaningful

Will add linter rule in seperate commit

Happy recompiling everyone!

Original Patch By: Nescio
Comment By: Gallaecio
Differential Revision: D2620
This was SVN commit r27786.
2023-07-27 20:54:46 +00:00

699 lines
23 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Copyright (C) 2023 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "ShaderProgram.h"
#include "graphics/ShaderDefines.h"
#include "ps/CLogger.h"
#include "ps/CStr.h"
#include "ps/CStrInternStatic.h"
#include "ps/Filesystem.h"
#include "ps/Profile.h"
#include "ps/XML/Xeromyces.h"
#include "renderer/backend/vulkan/DescriptorManager.h"
#include "renderer/backend/vulkan/Device.h"
#include "renderer/backend/vulkan/Texture.h"
#include <algorithm>
#include <limits>
namespace Renderer
{
namespace Backend
{
namespace Vulkan
{
namespace
{
VkShaderModule CreateShaderModule(CDevice* device, const VfsPath& path)
{
CVFSFile file;
if (file.Load(g_VFS, path) != PSRETURN_OK)
{
LOGERROR("Failed to load shader file: '%s'", path.string8());
return VK_NULL_HANDLE;
}
VkShaderModuleCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
// Casting to uint32_t requires to fit alignment and size.
ENSURE(file.GetBufferSize() % 4 == 0);
ENSURE(reinterpret_cast<uintptr_t>(file.GetBuffer()) % alignof(uint32_t) == 0u);
createInfo.codeSize = file.GetBufferSize();
createInfo.pCode = reinterpret_cast<const uint32_t*>(file.GetBuffer());
VkShaderModule shaderModule;
if (vkCreateShaderModule(device->GetVkDevice(), &createInfo, nullptr, &shaderModule) != VK_SUCCESS)
{
LOGERROR("Failed to create shader module from file: '%s'", path.string8());
return VK_NULL_HANDLE;
}
device->SetObjectName(VK_OBJECT_TYPE_SHADER_MODULE, shaderModule, path.string8().c_str());
return shaderModule;
}
VfsPath FindProgramMatchingDefines(const VfsPath& xmlFilename, const CShaderDefines& defines)
{
CXeromyces xeroFile;
PSRETURN ret = xeroFile.Load(g_VFS, xmlFilename);
if (ret != PSRETURN_OK)
return {};
// TODO: add XML validation.
#define EL(x) const int el_##x = xeroFile.GetElementID(#x)
#define AT(x) const int at_##x = xeroFile.GetAttributeID(#x)
EL(define);
EL(defines);
EL(program);
AT(file);
AT(name);
AT(value);
#undef AT
#undef EL
const CStrIntern strUndefined("UNDEFINED");
VfsPath programFilename;
XMBElement root = xeroFile.GetRoot();
XERO_ITER_EL(root, rootChild)
{
if (rootChild.GetNodeName() == el_program)
{
CShaderDefines programDefines;
XERO_ITER_EL(rootChild, programChild)
{
if (programChild.GetNodeName() == el_defines)
{
XERO_ITER_EL(programChild, definesChild)
{
XMBAttributeList attributes = definesChild.GetAttributes();
if (definesChild.GetNodeName() == el_define)
{
const CStrIntern value(attributes.GetNamedItem(at_value));
if (value == strUndefined)
continue;
programDefines.Add(
CStrIntern(attributes.GetNamedItem(at_name)), value);
}
}
}
}
if (programDefines == defines)
return L"shaders/" + rootChild.GetAttributes().GetNamedItem(at_file).FromUTF8();
}
}
return {};
}
} // anonymous namespace
IDevice* CVertexInputLayout::GetDevice()
{
return m_Device;
}
// static
std::unique_ptr<CShaderProgram> CShaderProgram::Create(
CDevice* device, const CStr& name, const CShaderDefines& baseDefines)
{
const VfsPath xmlFilename = L"shaders/" + wstring_from_utf8(name) + L".xml";
std::unique_ptr<CShaderProgram> shaderProgram(new CShaderProgram());
shaderProgram->m_Device = device;
shaderProgram->m_FileDependencies = {xmlFilename};
CShaderDefines defines = baseDefines;
if (device->GetDescriptorManager().UseDescriptorIndexing())
defines.Add(str_USE_DESCRIPTOR_INDEXING, str_1);
const VfsPath programFilename = FindProgramMatchingDefines(xmlFilename, defines);
if (programFilename.empty())
{
LOGERROR("Program '%s' with required defines not found.", name);
for (const auto& pair : defines.GetMap())
LOGERROR(" \"%s\": \"%s\"", pair.first.c_str(), pair.second.c_str());
return nullptr;
}
shaderProgram->m_FileDependencies.emplace_back(programFilename);
CXeromyces programXeroFile;
if (programXeroFile.Load(g_VFS, programFilename) != PSRETURN_OK)
return nullptr;
XMBElement programRoot = programXeroFile.GetRoot();
#define EL(x) const int el_##x = programXeroFile.GetElementID(#x)
#define AT(x) const int at_##x = programXeroFile.GetAttributeID(#x)
EL(binding);
EL(descriptor_set);
EL(descriptor_sets);
EL(fragment);
EL(member);
EL(push_constant);
EL(stream);
EL(vertex);
AT(binding);
AT(file);
AT(location);
AT(name);
AT(offset);
AT(set);
AT(size);
AT(type);
#undef AT
#undef EL
auto addPushConstant =
[&pushConstants=shaderProgram->m_PushConstants, &pushConstantDataFlags=shaderProgram->m_PushConstantDataFlags, &at_name, &at_offset, &at_size](
const XMBElement& element, VkShaderStageFlags stageFlags) -> bool
{
const XMBAttributeList attributes = element.GetAttributes();
const CStrIntern name = CStrIntern(attributes.GetNamedItem(at_name));
const uint32_t size = attributes.GetNamedItem(at_size).ToUInt();
const uint32_t offset = attributes.GetNamedItem(at_offset).ToUInt();
if (offset % 4 != 0 || size % 4 != 0)
{
LOGERROR("Push constant should have offset and size be multiple of 4.");
return false;
}
for (PushConstant& pushConstant : pushConstants)
{
if (pushConstant.name == name)
{
if (size != pushConstant.size || offset != pushConstant.offset)
{
LOGERROR("All shared push constants must have the same size and offset.");
return false;
}
// We found the same constant so we don't need to add it again.
pushConstant.stageFlags |= stageFlags;
for (uint32_t index = 0; index < (size >> 2); ++index)
pushConstantDataFlags[(offset >> 2) + index] |= stageFlags;
return true;
}
if (offset + size < pushConstant.offset || offset >= pushConstant.offset + pushConstant.size)
continue;
LOGERROR("All push constant must not intersect each other in memory.");
return false;
}
pushConstants.push_back({name, offset, size, stageFlags});
for (uint32_t index = 0; index < (size >> 2); ++index)
pushConstantDataFlags[(offset >> 2) + index] = stageFlags;
return true;
};
auto addDescriptorSets = [&](const XMBElement& element) -> bool
{
const bool useDescriptorIndexing =
device->GetDescriptorManager().UseDescriptorIndexing();
// TODO: reduce the indentation.
XERO_ITER_EL(element, descriporSetsChild)
{
if (descriporSetsChild.GetNodeName() == el_descriptor_set)
{
const uint32_t set = descriporSetsChild.GetAttributes().GetNamedItem(at_set).ToUInt();
if (useDescriptorIndexing && set == 0 && !descriporSetsChild.GetChildNodes().empty())
{
LOGERROR("Descritor set for descriptor indexing shouldn't contain bindings.");
return false;
}
XERO_ITER_EL(descriporSetsChild, descriporSetChild)
{
if (descriporSetChild.GetNodeName() == el_binding)
{
const XMBAttributeList attributes = descriporSetChild.GetAttributes();
const uint32_t binding = attributes.GetNamedItem(at_binding).ToUInt();
const uint32_t size = attributes.GetNamedItem(at_size).ToUInt();
const CStr type = attributes.GetNamedItem(at_type);
if (type == "uniform")
{
const uint32_t expectedSet =
device->GetDescriptorManager().GetUniformSet();
if (set != expectedSet || binding != 0)
{
LOGERROR("We support only a single uniform block per shader program.");
return false;
}
shaderProgram->m_MaterialConstantsDataSize = size;
XERO_ITER_EL(descriporSetChild, bindingChild)
{
if (bindingChild.GetNodeName() == el_member)
{
const XMBAttributeList memberAttributes = bindingChild.GetAttributes();
const uint32_t offset = memberAttributes.GetNamedItem(at_offset).ToUInt();
const uint32_t size = memberAttributes.GetNamedItem(at_size).ToUInt();
const CStrIntern name{memberAttributes.GetNamedItem(at_name)};
bool found = false;
for (const Uniform& uniform : shaderProgram->m_Uniforms)
{
if (uniform.name == name)
{
if (offset != uniform.offset || size != uniform.size)
{
LOGERROR("All uniforms across all stage should match.");
return false;
}
found = true;
}
else
{
if (offset + size <= uniform.offset || uniform.offset + uniform.size <= offset)
continue;
LOGERROR("Uniforms must not overlap each other.");
return false;
}
}
if (!found)
shaderProgram->m_Uniforms.push_back({name, offset, size});
}
}
}
else if (type == "sampler1D" || type == "sampler2D" || type == "sampler2DShadow" || type == "sampler3D" || type == "samplerCube")
{
if (useDescriptorIndexing)
{
LOGERROR("We support only uniform descriptor sets with enabled descriptor indexing.");
return false;
}
const CStrIntern name{attributes.GetNamedItem(at_name)};
shaderProgram->m_TextureMapping[name] = binding;
shaderProgram->m_TexturesDescriptorSetSize =
std::max(shaderProgram->m_TexturesDescriptorSetSize, binding + 1);
}
else
{
LOGERROR("Unsupported binding: '%s'", type.c_str());
return false;
}
}
}
}
}
return true;
};
XERO_ITER_EL(programRoot, programChild)
{
if (programChild.GetNodeName() == el_vertex)
{
const VfsPath shaderModulePath =
L"shaders/" + programChild.GetAttributes().GetNamedItem(at_file).FromUTF8();
shaderProgram->m_FileDependencies.emplace_back(shaderModulePath);
shaderProgram->m_ShaderModules.emplace_back(
CreateShaderModule(device, shaderModulePath));
if (shaderProgram->m_ShaderModules.back() == VK_NULL_HANDLE)
return nullptr;
VkPipelineShaderStageCreateInfo vertexShaderStageInfo{};
vertexShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertexShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertexShaderStageInfo.module = shaderProgram->m_ShaderModules.back();
vertexShaderStageInfo.pName = "main";
shaderProgram->m_Stages.emplace_back(std::move(vertexShaderStageInfo));
XERO_ITER_EL(programChild, stageChild)
{
if (stageChild.GetNodeName() == el_stream)
{
XMBAttributeList attributes = stageChild.GetAttributes();
const uint32_t location = attributes.GetNamedItem(at_location).ToUInt();
const CStr streamName = attributes.GetNamedItem(at_name);
VertexAttributeStream stream = VertexAttributeStream::UV7;
if (streamName == "pos")
stream = VertexAttributeStream::POSITION;
else if (streamName == "normal")
stream = VertexAttributeStream::NORMAL;
else if (streamName == "color")
stream = VertexAttributeStream::COLOR;
else if (streamName == "uv0")
stream = VertexAttributeStream::UV0;
else if (streamName == "uv1")
stream = VertexAttributeStream::UV1;
else if (streamName == "uv2")
stream = VertexAttributeStream::UV2;
else if (streamName == "uv3")
stream = VertexAttributeStream::UV3;
else if (streamName == "uv4")
stream = VertexAttributeStream::UV4;
else if (streamName == "uv5")
stream = VertexAttributeStream::UV5;
else if (streamName == "uv6")
stream = VertexAttributeStream::UV6;
else if (streamName == "uv7")
stream = VertexAttributeStream::UV7;
else
debug_warn("Unknown stream");
shaderProgram->m_StreamLocations[stream] = location;
}
else if (stageChild.GetNodeName() == el_push_constant)
{
if (!addPushConstant(stageChild, VK_SHADER_STAGE_VERTEX_BIT))
return nullptr;
}
else if (stageChild.GetNodeName() == el_descriptor_sets)
{
if (!addDescriptorSets(stageChild))
return nullptr;
}
}
}
else if (programChild.GetNodeName() == el_fragment)
{
const VfsPath shaderModulePath =
L"shaders/" + programChild.GetAttributes().GetNamedItem(at_file).FromUTF8();
shaderProgram->m_FileDependencies.emplace_back(shaderModulePath);
shaderProgram->m_ShaderModules.emplace_back(
CreateShaderModule(device, shaderModulePath));
if (shaderProgram->m_ShaderModules.back() == VK_NULL_HANDLE)
return nullptr;
VkPipelineShaderStageCreateInfo fragmentShaderStageInfo{};
fragmentShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragmentShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragmentShaderStageInfo.module = shaderProgram->m_ShaderModules.back();
fragmentShaderStageInfo.pName = "main";
shaderProgram->m_Stages.emplace_back(std::move(fragmentShaderStageInfo));
XERO_ITER_EL(programChild, stageChild)
{
if (stageChild.GetNodeName() == el_push_constant)
{
if (!addPushConstant(stageChild, VK_SHADER_STAGE_FRAGMENT_BIT))
return nullptr;
}
else if (stageChild.GetNodeName() == el_descriptor_sets)
{
if (!addDescriptorSets(stageChild))
return nullptr;
}
}
}
}
if (shaderProgram->m_Stages.empty())
{
LOGERROR("Program should contain at least one stage.");
return nullptr;
}
for (size_t index = 0; index < shaderProgram->m_PushConstants.size(); ++index)
shaderProgram->m_PushConstantMapping[shaderProgram->m_PushConstants[index].name] = index;
std::vector<VkPushConstantRange> pushConstantRanges;
pushConstantRanges.reserve(shaderProgram->m_PushConstants.size());
std::transform(
shaderProgram->m_PushConstants.begin(), shaderProgram->m_PushConstants.end(),
std::back_insert_iterator(pushConstantRanges), [](const PushConstant& pushConstant)
{
return VkPushConstantRange{pushConstant.stageFlags, pushConstant.offset, pushConstant.size};
});
if (!pushConstantRanges.empty())
{
std::sort(pushConstantRanges.begin(), pushConstantRanges.end(),
[](const VkPushConstantRange& lhs, const VkPushConstantRange& rhs)
{
return lhs.offset < rhs.offset;
});
// Merge subsequent constants.
auto it = pushConstantRanges.begin();
while (std::next(it) != pushConstantRanges.end())
{
auto next = std::next(it);
if (it->stageFlags == next->stageFlags)
{
it->size = next->offset - it->offset + next->size;
pushConstantRanges.erase(next);
}
else
it = next;
}
for (const VkPushConstantRange& range : pushConstantRanges)
if (std::count_if(pushConstantRanges.begin(), pushConstantRanges.end(),
[stageFlags=range.stageFlags](const VkPushConstantRange& range) { return range.stageFlags & stageFlags; }) != 1)
{
LOGERROR("Any two range must not include the same stage in stageFlags.");
return nullptr;
}
}
for (size_t index = 0; index < shaderProgram->m_Uniforms.size(); ++index)
shaderProgram->m_UniformMapping[shaderProgram->m_Uniforms[index].name] = index;
if (!shaderProgram->m_Uniforms.empty())
{
if (shaderProgram->m_MaterialConstantsDataSize > device->GetChoosenPhysicalDevice().properties.limits.maxUniformBufferRange)
{
LOGERROR("Uniform buffer size is too big for the device.");
return nullptr;
}
shaderProgram->m_MaterialConstantsData =
std::make_unique<std::byte[]>(shaderProgram->m_MaterialConstantsDataSize);
}
std::vector<VkDescriptorSetLayout> layouts =
device->GetDescriptorManager().GetDescriptorSetLayouts();
if (shaderProgram->m_TexturesDescriptorSetSize > 0)
{
ENSURE(!device->GetDescriptorManager().UseDescriptorIndexing());
shaderProgram->m_BoundTextures.resize(shaderProgram->m_TexturesDescriptorSetSize);
shaderProgram->m_BoundTexturesUID.resize(shaderProgram->m_TexturesDescriptorSetSize);
shaderProgram->m_BoundTexturesOutdated = true;
shaderProgram->m_TexturesDescriptorSetLayout =
device->GetDescriptorManager().GetSingleTypeDescritorSetLayout(
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, shaderProgram->m_TexturesDescriptorSetSize);
layouts.emplace_back(shaderProgram->m_TexturesDescriptorSetLayout);
}
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{};
pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutCreateInfo.setLayoutCount = layouts.size();
pipelineLayoutCreateInfo.pSetLayouts = layouts.data();
pipelineLayoutCreateInfo.pushConstantRangeCount = pushConstantRanges.size();
pipelineLayoutCreateInfo.pPushConstantRanges = pushConstantRanges.data();
const VkResult result = vkCreatePipelineLayout(
device->GetVkDevice(), &pipelineLayoutCreateInfo, nullptr,
&shaderProgram->m_PipelineLayout);
if (result != VK_SUCCESS)
{
LOGERROR("Failed to create a pipeline layout: %d", static_cast<int>(result));
return nullptr;
}
return shaderProgram;
}
CShaderProgram::CShaderProgram() = default;
CShaderProgram::~CShaderProgram()
{
if (m_PipelineLayout != VK_NULL_HANDLE)
m_Device->ScheduleObjectToDestroy(VK_OBJECT_TYPE_PIPELINE_LAYOUT, m_PipelineLayout, VK_NULL_HANDLE);
for (VkShaderModule shaderModule : m_ShaderModules)
if (shaderModule != VK_NULL_HANDLE)
m_Device->ScheduleObjectToDestroy(VK_OBJECT_TYPE_SHADER_MODULE, shaderModule, VK_NULL_HANDLE);
}
IDevice* CShaderProgram::GetDevice()
{
return m_Device;
}
int32_t CShaderProgram::GetBindingSlot(const CStrIntern name) const
{
if (auto it = m_PushConstantMapping.find(name); it != m_PushConstantMapping.end())
return it->second;
if (auto it = m_UniformMapping.find(name); it != m_UniformMapping.end())
return it->second + m_PushConstants.size();
if (auto it = m_TextureMapping.find(name); it != m_TextureMapping.end())
return it->second + m_PushConstants.size() + m_UniformMapping.size();
return -1;
}
std::vector<VfsPath> CShaderProgram::GetFileDependencies() const
{
return m_FileDependencies;
}
uint32_t CShaderProgram::GetStreamLocation(const VertexAttributeStream stream) const
{
auto it = m_StreamLocations.find(stream);
return it != m_StreamLocations.end() ? it->second : std::numeric_limits<uint32_t>::max();
}
void CShaderProgram::Bind()
{
if (m_MaterialConstantsData)
m_MaterialConstantsDataOutdated = true;
}
void CShaderProgram::Unbind()
{
if (m_TexturesDescriptorSetSize > 0)
{
for (CTexture*& texture : m_BoundTextures)
texture = nullptr;
for (CTexture::UID& uid : m_BoundTexturesUID)
uid = 0;
m_BoundTexturesOutdated = true;
}
}
void CShaderProgram::PreDraw(VkCommandBuffer commandBuffer)
{
UpdateActiveDescriptorSet(commandBuffer);
if (m_PushConstantDataMask)
{
for (uint32_t index = 0; index < 32;)
{
if (!(m_PushConstantDataMask & (1 << index)))
{
++index;
continue;
}
uint32_t indexEnd = index + 1;
while (indexEnd < 32 && (m_PushConstantDataMask & (1 << indexEnd)) && m_PushConstantDataFlags[index] == m_PushConstantDataFlags[indexEnd])
++indexEnd;
vkCmdPushConstants(
commandBuffer, GetPipelineLayout(),
m_PushConstantDataFlags[index],
index * 4, (indexEnd - index) * 4, m_PushConstantData.data() + index * 4);
index = indexEnd;
}
m_PushConstantDataMask = 0;
}
}
void CShaderProgram::UpdateActiveDescriptorSet(
VkCommandBuffer commandBuffer)
{
if (m_BoundTexturesOutdated)
{
m_BoundTexturesOutdated = false;
m_ActiveTexturesDescriptorSet =
m_Device->GetDescriptorManager().GetSingleTypeDescritorSet(
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, m_TexturesDescriptorSetLayout,
m_BoundTexturesUID, m_BoundTextures);
ENSURE(m_ActiveTexturesDescriptorSet != VK_NULL_HANDLE);
vkCmdBindDescriptorSets(
commandBuffer, GetPipelineBindPoint(), GetPipelineLayout(),
1, 1, &m_ActiveTexturesDescriptorSet, 0, nullptr);
}
}
void CShaderProgram::SetUniform(
const int32_t bindingSlot,
const float value)
{
const float values[1] = {value};
SetUniform(bindingSlot, PS::span<const float>(values, values + 1));
}
void CShaderProgram::SetUniform(
const int32_t bindingSlot,
const float valueX, const float valueY)
{
const float values[2] = {valueX, valueY};
SetUniform(bindingSlot, PS::span<const float>(values, values + 2));
}
void CShaderProgram::SetUniform(
const int32_t bindingSlot,
const float valueX, const float valueY,
const float valueZ)
{
const float values[3] = {valueX, valueY, valueZ};
SetUniform(bindingSlot, PS::span<const float>(values, values + 3));
}
void CShaderProgram::SetUniform(
const int32_t bindingSlot,
const float valueX, const float valueY,
const float valueZ, const float valueW)
{
const float values[4] = {valueX, valueY, valueZ, valueW};
SetUniform(bindingSlot, PS::span<const float>(values, values + 4));
}
void CShaderProgram::SetUniform(const int32_t bindingSlot, PS::span<const float> values)
{
if (bindingSlot < 0)
return;
const auto data = GetUniformData(bindingSlot, values.size() * sizeof(float));
std::memcpy(data.first, values.data(), data.second);
}
std::pair<std::byte*, uint32_t> CShaderProgram::GetUniformData(
const int32_t bindingSlot, const uint32_t dataSize)
{
if (bindingSlot < static_cast<int32_t>(m_PushConstants.size()))
{
const uint32_t size = m_PushConstants[bindingSlot].size;
const uint32_t offset = m_PushConstants[bindingSlot].offset;
ENSURE(size <= dataSize);
m_PushConstantDataMask |= ((1 << (size >> 2)) - 1) << (offset >> 2);
return {m_PushConstantData.data() + offset, size};
}
else
{
ENSURE(bindingSlot - m_PushConstants.size() < m_Uniforms.size());
const Uniform& uniform = m_Uniforms[bindingSlot - m_PushConstants.size()];
m_MaterialConstantsDataOutdated = true;
const uint32_t size = uniform.size;
const uint32_t offset = uniform.offset;
ENSURE(size <= dataSize);
return {m_MaterialConstantsData.get() + offset, size};
}
}
void CShaderProgram::SetTexture(const int32_t bindingSlot, CTexture* texture)
{
if (bindingSlot < 0)
return;
CDescriptorManager& descriptorManager = m_Device->GetDescriptorManager();
if (descriptorManager.UseDescriptorIndexing())
{
const uint32_t descriptorIndex = descriptorManager.GetTextureDescriptor(texture->As<CTexture>());
ENSURE(bindingSlot < static_cast<int32_t>(m_PushConstants.size()));
const uint32_t size = m_PushConstants[bindingSlot].size;
const uint32_t offset = m_PushConstants[bindingSlot].offset;
ENSURE(size == sizeof(descriptorIndex));
std::memcpy(m_PushConstantData.data() + offset, &descriptorIndex, size);
m_PushConstantDataMask |= ((1 << (size >> 2)) - 1) << (offset >> 2);
}
else
{
ENSURE(bindingSlot >= static_cast<int32_t>(m_PushConstants.size() + m_UniformMapping.size()));
const uint32_t index = bindingSlot - (m_PushConstants.size() + m_UniformMapping.size());
if (m_BoundTexturesUID[index] != texture->GetUID())
{
m_BoundTextures[index] = texture;
m_BoundTexturesUID[index] = texture->GetUID();
m_BoundTexturesOutdated = true;
}
}
}
} // namespace Vulkan
} // namespace Backend
} // namespace Renderer