Resource binding was over-complicated as I didn't understood the state manager and vulkan to make the correct decisions at that time. This refactor will remove a lot of the complexity and improves the performance. **Performance** The performance improvement is noticeable in complex grease pencil scenes. Grease pencil benchmark file picknick: - `NVIDIA Quadro RTX 6000` 17 fps -> 24 fps - `Intel(R) Arc(tm) A750 Graphics (DG2)` 6 -> 21 fps **Bottle-neck** The performance improvements originates from moving the update entry point from state manager to shader interface. The previous implementation (state manager) had to loop over all the bound resources and find in the shader interface where it was located in the descriptor set. Ignoring resources that were not used by the shader. But also making it hard to determine if descriptor sets actually changed. Previous implementation assumed descriptor sets always changed. When descriptor set changed a new descriptor set needed to be allocated. Most drivers this is a fast operation, but on Intel/Mesa this was measurable slow. Using an allocation pool doesn't fit the Vulkan API as you are only able to reuse when the layout matches exactly. Of course doable, but requires another structure to keep track of the actual layouts. **Solution** By using the shader interface as entry point we can: 1. Keep track if there are any changes in the state manager. If not and the layout is the same, the previous shader can be reused. 2. In stead of looping over each bound resource, we loop over bind points. **Future extensions** Bundle all descriptor set uploads just before use. This would be more in line with how 'modern' Vulkan should be implemented. This PR already separates the uploading from the updating and technically allows to upload more than one descriptor set. Instead of looking 1 set back we should measure if we can handle multiple or keep track of the different layouts resources to improve the performance even further. Optional use `VK_KHR_descriptor_buffer` when available. Pull Request: https://projects.blender.org/blender/blender/pulls/128068
117 lines
3.4 KiB
C++
117 lines
3.4 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup gpu
|
|
*/
|
|
|
|
#include "vk_descriptor_pools.hh"
|
|
#include "vk_backend.hh"
|
|
#include "vk_device.hh"
|
|
#include "vk_memory.hh"
|
|
|
|
namespace blender::gpu {
|
|
VKDescriptorPools::VKDescriptorPools() {}
|
|
|
|
VKDescriptorPools::~VKDescriptorPools()
|
|
{
|
|
VK_ALLOCATION_CALLBACKS
|
|
const VKDevice &device = VKBackend::get().device;
|
|
for (const VkDescriptorPool vk_descriptor_pool : pools_) {
|
|
vkDestroyDescriptorPool(device.vk_handle(), vk_descriptor_pool, vk_allocation_callbacks);
|
|
}
|
|
}
|
|
|
|
void VKDescriptorPools::init(const VKDevice &device)
|
|
{
|
|
BLI_assert(pools_.is_empty());
|
|
add_new_pool(device);
|
|
}
|
|
|
|
void VKDescriptorPools::reset()
|
|
{
|
|
const VKDevice &device = VKBackend::get().device;
|
|
for (const VkDescriptorPool vk_descriptor_pool : pools_) {
|
|
vkResetDescriptorPool(device.vk_handle(), vk_descriptor_pool, 0);
|
|
}
|
|
|
|
active_pool_index_ = 0;
|
|
}
|
|
|
|
void VKDescriptorPools::add_new_pool(const VKDevice &device)
|
|
{
|
|
VK_ALLOCATION_CALLBACKS
|
|
Vector<VkDescriptorPoolSize> pool_sizes = {
|
|
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, POOL_SIZE_STORAGE_BUFFER},
|
|
{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, POOL_SIZE_STORAGE_IMAGE},
|
|
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, POOL_SIZE_COMBINED_IMAGE_SAMPLER},
|
|
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, POOL_SIZE_UNIFORM_BUFFER},
|
|
{VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, POOL_SIZE_UNIFORM_TEXEL_BUFFER},
|
|
};
|
|
VkDescriptorPoolCreateInfo pool_info = {};
|
|
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
|
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
|
|
pool_info.maxSets = POOL_SIZE_DESCRIPTOR_SETS;
|
|
pool_info.poolSizeCount = pool_sizes.size();
|
|
pool_info.pPoolSizes = pool_sizes.data();
|
|
VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;
|
|
VkResult result = vkCreateDescriptorPool(
|
|
device.vk_handle(), &pool_info, vk_allocation_callbacks, &descriptor_pool);
|
|
UNUSED_VARS(result);
|
|
pools_.append(descriptor_pool);
|
|
}
|
|
|
|
VkDescriptorPool VKDescriptorPools::active_pool_get()
|
|
{
|
|
BLI_assert(!pools_.is_empty());
|
|
return pools_[active_pool_index_];
|
|
}
|
|
|
|
void VKDescriptorPools::activate_next_pool()
|
|
{
|
|
BLI_assert(!is_last_pool_active());
|
|
active_pool_index_ += 1;
|
|
}
|
|
|
|
void VKDescriptorPools::activate_last_pool()
|
|
{
|
|
active_pool_index_ = pools_.size() - 1;
|
|
}
|
|
|
|
bool VKDescriptorPools::is_last_pool_active()
|
|
{
|
|
return active_pool_index_ == pools_.size() - 1;
|
|
}
|
|
|
|
VkDescriptorSet VKDescriptorPools::allocate(const VkDescriptorSetLayout descriptor_set_layout)
|
|
{
|
|
BLI_assert(descriptor_set_layout != VK_NULL_HANDLE);
|
|
const VKDevice &device = VKBackend::get().device;
|
|
|
|
VkDescriptorSetAllocateInfo allocate_info = {};
|
|
VkDescriptorPool pool = active_pool_get();
|
|
allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
|
allocate_info.descriptorPool = pool;
|
|
allocate_info.descriptorSetCount = 1;
|
|
allocate_info.pSetLayouts = &descriptor_set_layout;
|
|
VkDescriptorSet vk_descriptor_set = VK_NULL_HANDLE;
|
|
VkResult result = vkAllocateDescriptorSets(
|
|
device.vk_handle(), &allocate_info, &vk_descriptor_set);
|
|
|
|
if (ELEM(result, VK_ERROR_OUT_OF_POOL_MEMORY, VK_ERROR_FRAGMENTED_POOL)) {
|
|
if (is_last_pool_active()) {
|
|
add_new_pool(device);
|
|
activate_last_pool();
|
|
}
|
|
else {
|
|
activate_next_pool();
|
|
}
|
|
return allocate(descriptor_set_layout);
|
|
}
|
|
|
|
return vk_descriptor_set;
|
|
}
|
|
|
|
} // namespace blender::gpu
|