[drape][Vulkan] Improve descriptor sets allocation

Signed-off-by: renderexpert <expert@renderconsulting.co.uk>
This commit is contained in:
renderexpert 2024-05-24 14:51:22 +01:00 committed by Alexander Borsuk
parent 7cb03774f8
commit 35d8a7c3b3
8 changed files with 124 additions and 42 deletions

View file

@ -51,8 +51,6 @@ VulkanObjectManager::VulkanObjectManager(VkDevice device, VkPhysicalDeviceLimits
for (auto & descriptorsToDestroy : m_descriptorsToDestroy)
descriptorsToDestroy.reserve(kAvgDestroyCount);
CreateDescriptorPool();
}
VulkanObjectManager::~VulkanObjectManager()
@ -190,34 +188,36 @@ DescriptorSetGroup VulkanObjectManager::CreateDescriptorSetGroup(ref_ptr<VulkanG
{
CHECK(std::this_thread::get_id() == m_renderers[ThreadType::Frontend], ());
CHECK(!m_descriptorPools.empty(), ());
DescriptorSetGroup s;
VkDescriptorSetLayout layout = program->GetDescriptorSetLayout();
VkDescriptorSetAllocateInfo allocInfo = {};
// Find a pool with available sets.
uint32_t poolIndex = 0;
while (poolIndex < m_descriptorPools.size() &&
m_descriptorPools[poolIndex].m_availableSetsCount == 0)
{
++poolIndex;
}
// No such a pool, create one.
if (poolIndex == m_descriptorPools.size())
{
CreateDescriptorPool();
poolIndex = m_descriptorPools.size() - 1;
}
// Allocate a descriptor set.
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = s.m_descriptorPool = m_descriptorPools.front();
allocInfo.descriptorPool = m_descriptorPools[poolIndex].m_pool;
s.m_descriptorPoolIndex = poolIndex;
allocInfo.pSetLayouts = &layout;
allocInfo.descriptorSetCount = 1;
auto result = vkAllocateDescriptorSets(m_device, &allocInfo, &s.m_descriptorSet);
if (result != VK_SUCCESS)
{
for (size_t i = 1; i < m_descriptorPools.size(); ++i)
{
allocInfo.descriptorPool = s.m_descriptorPool = m_descriptorPools[i];
result = vkAllocateDescriptorSets(m_device, &allocInfo, &s.m_descriptorSet);
if (result == VK_SUCCESS)
break;
}
// Decrease the available sets count.
m_descriptorPools[poolIndex].m_availableSetsCount--;
if (s.m_descriptorSet == VK_NULL_HANDLE)
{
CreateDescriptorPool();
allocInfo.descriptorPool = s.m_descriptorPool = m_descriptorPools.back();
CHECK_VK_CALL(vkAllocateDescriptorSets(m_device, &allocInfo, &s.m_descriptorSet));
}
}
CHECK_VK_CALL(vkAllocateDescriptorSets(m_device, &allocInfo, &s.m_descriptorSet));
return s;
}
@ -255,8 +255,10 @@ void VulkanObjectManager::CollectDescriptorSetGroupsUnsafe(DescriptorSetGroupArr
{
for (auto const & d : descriptors)
{
CHECK_VK_CALL(vkFreeDescriptorSets(m_device, d.m_descriptorPool,
CHECK_LESS(d.m_descriptorPoolIndex, m_descriptorPools.size(), ());
CHECK_VK_CALL(vkFreeDescriptorSets(m_device, m_descriptorPools[d.m_descriptorPoolIndex].m_pool,
1 /* count */, &d.m_descriptorSet));
m_descriptorPools[d.m_descriptorPoolIndex].m_availableSetsCount++;
}
descriptors.clear();
}
@ -317,6 +319,16 @@ void VulkanObjectManager::DestroyObjectUnsafe(VulkanObject object)
CollectObjectsImpl(VulkanObjectArray{object});
}
void VulkanObjectManager::SetMaxUniformBuffers(uint32_t maxUniformBuffers)
{
m_maxUniformBuffers = maxUniformBuffers;
}
void VulkanObjectManager::SetMaxImageSamplers(uint32_t maxImageSamplers)
{
m_maxImageSamplers = maxImageSamplers;
}
uint8_t * VulkanObjectManager::MapUnsafe(VulkanObject object)
{
CHECK(!object.m_allocation->m_memoryBlock->m_isBlocked, ());
@ -368,15 +380,15 @@ void VulkanObjectManager::Fill(VulkanObject object, void const * data, uint32_t
void VulkanObjectManager::CreateDescriptorPool()
{
// Maximum uniform buffers descriptors count per frame.
uint32_t constexpr kMaxUniformBufferDescriptorsCount = 500 * kMaxInflightFrames;
// Maximum textures descriptors count per frame.
uint32_t constexpr kMaxImageSamplerDescriptorsCount = 1000 * kMaxInflightFrames;
// Maximum descriptors sets count in the pool.
uint32_t constexpr kMaxDescriptorsSetCount = 256 * kMaxInflightFrames;
CHECK(m_maxUniformBuffers > 0, ());
CHECK(m_maxImageSamplers > 0, ());
std::vector<VkDescriptorPoolSize> poolSizes =
{
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, kMaxUniformBufferDescriptorsCount},
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, kMaxImageSamplerDescriptorsCount},
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, m_maxUniformBuffers * kMaxDescriptorsSetCount},
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, m_maxImageSamplers * kMaxDescriptorsSetCount},
};
VkDescriptorPoolCreateInfo descriptorPoolInfo = {};
@ -384,17 +396,19 @@ void VulkanObjectManager::CreateDescriptorPool()
descriptorPoolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
descriptorPoolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
descriptorPoolInfo.pPoolSizes = poolSizes.data();
descriptorPoolInfo.maxSets = kMaxUniformBufferDescriptorsCount + kMaxImageSamplerDescriptorsCount;
descriptorPoolInfo.maxSets = kMaxDescriptorsSetCount;
DescriptorPool descriptorPool;
CHECK_VK_CALL(vkCreateDescriptorPool(m_device, &descriptorPoolInfo, nullptr, &descriptorPool.m_pool));
descriptorPool.m_availableSetsCount = descriptorPoolInfo.maxSets;
VkDescriptorPool descriptorPool;
CHECK_VK_CALL(vkCreateDescriptorPool(m_device, &descriptorPoolInfo, nullptr, &descriptorPool));
m_descriptorPools.push_back(descriptorPool);
}
void VulkanObjectManager::DestroyDescriptorPools()
{
for (auto & pool : m_descriptorPools)
vkDestroyDescriptorPool(m_device, pool, nullptr);
vkDestroyDescriptorPool(m_device, pool.m_pool, nullptr);
}
VkSampler VulkanObjectManager::GetSampler(SamplerKey const & key)

View file

@ -93,6 +93,9 @@ public:
VulkanMemoryManager const & GetMemoryManager() const { return m_memoryManager; };
VkSampler GetSampler(SamplerKey const & key);
void SetMaxUniformBuffers(uint32_t maxUniformBuffers);
void SetMaxImageSamplers(uint32_t maxImageSamplers);
private:
using DescriptorSetGroupArray = std::vector<DescriptorSetGroup>;
using VulkanObjectArray = std::vector<VulkanObject>;
@ -110,7 +113,12 @@ private:
std::array<std::thread::id, ThreadType::Count> m_renderers = {};
std::array<std::array<VulkanObjectArray, kMaxInflightFrames>, ThreadType::Count> m_queuesToDestroy = {};
std::vector<VkDescriptorPool> m_descriptorPools;
struct DescriptorPool
{
VkDescriptorPool m_pool;
uint32_t m_availableSetsCount = 0;
};
std::vector<DescriptorPool> m_descriptorPools;
std::array<DescriptorSetGroupArray, kMaxInflightFrames> m_descriptorsToDestroy;
@ -118,6 +126,9 @@ private:
uint32_t m_currentInflightFrameIndex = 0;
uint32_t m_maxUniformBuffers = 0;
uint32_t m_maxImageSamplers = 0;
std::mutex m_mutex;
std::mutex m_samplerMutex;
std::mutex m_destroyMutex;

View file

@ -6,6 +6,7 @@
#include <array>
#include <cstdint>
#include <limits>
#include <string>
#include <vector>
@ -37,7 +38,7 @@ size_t constexpr kMaxDescriptorSets = 8;
struct DescriptorSetGroup
{
VkDescriptorSet m_descriptorSet = {};
VkDescriptorPool m_descriptorPool = {};
uint32_t m_descriptorPoolIndex = std::numeric_limits<uint32_t>::max();
std::array<uint32_t, kMaxDescriptorSets> m_ids = {};
bool m_updated = false;
@ -45,7 +46,7 @@ struct DescriptorSetGroup
explicit operator bool()
{
return m_descriptorSet != VK_NULL_HANDLE &&
m_descriptorPool != VK_NULL_HANDLE;
m_descriptorPoolIndex != std::numeric_limits<uint32_t>::max();
}
void Update(VkDevice device, std::vector<ParamDescriptor> const & descriptors);

View file

@ -87,7 +87,8 @@ void ProgramManager::InitForOpenGL(ref_ptr<dp::GraphicsContext> context)
void ProgramManager::InitForVulkan(ref_ptr<dp::GraphicsContext> context)
{
m_pool = make_unique_dp<vulkan::VulkanProgramPool>(context);
m_paramsSetter = make_unique_dp<vulkan::VulkanProgramParamsSetter>(context);
m_paramsSetter = make_unique_dp<vulkan::VulkanProgramParamsSetter>(context,
make_ref(static_cast<vulkan::VulkanProgramPool *>(m_pool.get())));
}
void ProgramManager::DestroyForVulkan(ref_ptr<dp::GraphicsContext> context)

View file

@ -1,5 +1,7 @@
#include "shaders/vulkan_program_params.hpp"
#include "shaders/vulkan_program_pool.hpp"
#include "drape/vulkan/vulkan_base_context.hpp"
#include "drape/vulkan/vulkan_gpu_program.hpp"
#include "drape/vulkan/vulkan_utils.hpp"
@ -31,10 +33,14 @@ VulkanProgramParamsSetter::UniformBuffer CreateUniformBuffer(VkDevice device,
}
} // namespace
VulkanProgramParamsSetter::VulkanProgramParamsSetter(ref_ptr<dp::vulkan::VulkanBaseContext> context)
VulkanProgramParamsSetter::VulkanProgramParamsSetter(ref_ptr<dp::vulkan::VulkanBaseContext> context,
ref_ptr<VulkanProgramPool> programPool)
{
using namespace dp::vulkan;
m_objectManager = context->GetObjectManager();
m_objectManager->SetMaxUniformBuffers(programPool->GetMaxUniformBuffers());
m_objectManager->SetMaxImageSamplers(programPool->GetMaxImageSamplers());
for (auto & ub : m_uniformBuffers)
{
ub.emplace_back(CreateUniformBuffer(context->GetDevice(), m_objectManager,

View file

@ -16,6 +16,8 @@ namespace gpu
{
namespace vulkan
{
class VulkanProgramPool;
class VulkanProgramParamsSetter : public ProgramParamsSetter
{
public:
@ -24,9 +26,10 @@ public:
dp::vulkan::VulkanObject m_object;
uint8_t * m_pointer = nullptr;
uint32_t m_freeOffset = 0;
};
};
explicit VulkanProgramParamsSetter(ref_ptr<dp::vulkan::VulkanBaseContext> context);
VulkanProgramParamsSetter(ref_ptr<dp::vulkan::VulkanBaseContext> context,
ref_ptr<VulkanProgramPool> programPool);
~VulkanProgramParamsSetter() override;
void Destroy(ref_ptr<dp::vulkan::VulkanBaseContext> context);

View file

@ -12,6 +12,7 @@
#include "base/file_name_utils.hpp"
#include "base/logging.hpp"
#include <algorithm>
#include <cstdint>
#include <map>
#include <string>
@ -24,6 +25,7 @@ namespace vulkan
namespace
{
int8_t constexpr kInvalidBindingIndex = -1;
uint32_t constexpr kMaxUniformBuffers = 1;
std::string const kShadersDir = "vulkan_shaders";
std::string const kShadersReflecton = "reflection.json";
@ -120,13 +122,18 @@ std::map<uint8_t, ReflectionData> ReadReflectionFile(std::string const & filenam
for (auto & d : reflectionFile.m_reflectionData)
{
auto const index = d.m_programIndex;
std::sort(d.m_info.m_textures.begin(), d.m_info.m_textures.end(),
[](auto const & a, auto const & b) {
return a.m_index < b.m_index;
});
result.insert(std::make_pair(index, std::move(d)));
}
return result;
}
std::vector<VkDescriptorSetLayoutBinding> GetLayoutBindings(ReflectionInfo const & reflectionInfo)
std::vector<VkDescriptorSetLayoutBinding> GetLayoutBindings(ReflectionInfo const & reflectionInfo,
uint32_t maxTextureBindings)
{
std::vector<VkDescriptorSetLayoutBinding> result;
@ -171,6 +178,22 @@ std::vector<VkDescriptorSetLayoutBinding> GetLayoutBindings(ReflectionInfo const
result.push_back(std::move(textureBinding));
}
// Add empty bindings for unused texture slots.
uint32_t bindingIndex = 1;
if (!reflectionInfo.m_textures.empty())
bindingIndex = static_cast<uint32_t>(reflectionInfo.m_textures.back().m_index) + 1;
for (uint32_t i = static_cast<uint32_t>(reflectionInfo.m_textures.size());
i < maxTextureBindings; ++i)
{
VkDescriptorSetLayoutBinding emptyBinding = {};
emptyBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
emptyBinding.binding = bindingIndex++;
emptyBinding.descriptorCount = 1;
emptyBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
result.push_back(std::move(emptyBinding));
}
return result;
}
@ -203,6 +226,12 @@ VulkanProgramPool::VulkanProgramPool(ref_ptr<dp::GraphicsContext> context)
auto packFileData = ReadShadersPackFile(base::JoinPath(kShadersDir, kShadersPackFile));
for (size_t i = 0; i < static_cast<size_t>(Program::ProgramsCount); ++i)
{
m_maxImageSamplers = std::max(m_maxImageSamplers,
static_cast<uint32_t>(reflection[i].m_info.m_textures.size()));
}
for (size_t i = 0; i < static_cast<size_t>(Program::ProgramsCount); ++i)
{
auto const & refl = reflection[i];
@ -210,7 +239,10 @@ VulkanProgramPool::VulkanProgramPool(ref_ptr<dp::GraphicsContext> context)
refl.m_vsOffset, refl.m_vsSize);
m_programData[i].m_fragmentShader = LoadShaderModule(device, packFileData,
refl.m_fsOffset, refl.m_fsSize);
auto bindings = GetLayoutBindings(refl.m_info);
auto bindings = GetLayoutBindings(refl.m_info, m_maxImageSamplers);
CHECK(bindings.size() == kMaxUniformBuffers + m_maxImageSamplers,
("Incorrect bindings count."));
VkDescriptorSetLayoutCreateInfo descriptorLayout = {};
descriptorLayout.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptorLayout.pBindings = bindings.data();
@ -275,5 +307,15 @@ drape_ptr<dp::GpuProgram> VulkanProgramPool::Get(Program program)
d.m_pipelineLayout,
d.m_textureBindings);
}
uint32_t VulkanProgramPool::GetMaxUniformBuffers() const
{
return kMaxUniformBuffers;
}
uint32_t VulkanProgramPool::GetMaxImageSamplers() const
{
return m_maxImageSamplers;
}
} // namespace vulkan
} // namespace gpu

View file

@ -21,6 +21,9 @@ public:
void Destroy(ref_ptr<dp::GraphicsContext> context);
drape_ptr<dp::GpuProgram> Get(Program program) override;
uint32_t GetMaxUniformBuffers() const;
uint32_t GetMaxImageSamplers() const;
private:
struct ProgramData
{
@ -31,6 +34,7 @@ private:
dp::vulkan::VulkanGpuProgram::TextureBindings m_textureBindings;
};
std::array<ProgramData, static_cast<size_t>(Program::ProgramsCount)> m_programData;
uint32_t m_maxImageSamplers = 0;
};
} // namespace vulkan
} // namespace gpu