proper descriptor pooling (prerequisite for properly segregating pipelines as shareable and descriptor sets as not)

This commit is contained in:
ecker 2026-05-09 14:02:15 -05:00
parent a10ed8bc60
commit 5f5bc08068
5 changed files with 106 additions and 30 deletions

View File

@ -118,7 +118,7 @@
"gui": true,
"vsync": true, // vsync on vulkan side rather than engine-side
"hdr": true,
"vxgi": false,
"vxgi": true,
"culling": false,
"bloom": false,
"dof": false,

View File

@ -29,6 +29,30 @@ namespace ext {
operator VkCommandBuffer() { return handle; }
};
struct DescriptorAllocator {
VkDevice device = VK_NULL_HANDLE;
VkDescriptorPool currentPool = VK_NULL_HANDLE;
uf::stl::vector<VkDescriptorPool> usedPools;
uf::stl::vector<VkDescriptorPoolSize> poolSizes = {
{ VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 4000 },
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 4000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 500 },
{ VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 100 }
};
void initialize( VkDevice );
void destroy();
VkDescriptorPool createPool();
bool allocate( VkDescriptorSet* set, VkDescriptorSetLayout layout );
};
struct Texture;
struct UF_API Device {
@ -69,6 +93,7 @@ namespace ext {
} extensions;
VkPipelineCache pipelineCache;
DescriptorAllocator descriptorAllocator;
uf::stl::vector<VkQueueFamilyProperties> queueFamilyProperties;

View File

@ -12,6 +12,8 @@ namespace ext {
struct Graphic;
struct UF_API Pipeline : public Buffers {
static uf::stl::unordered_map<GraphicDescriptor, Pipeline> pipelines;
bool aliased = false;
Device* device = NULL;
@ -42,9 +44,6 @@ namespace ext {
void record( const Graphic& graphic, const GraphicDescriptor& descriptor, VkCommandBuffer, size_t = 0, size_t = 0, size_t = 0 ) const;
void destroy();
uf::stl::vector<Shader*> getShaders( uf::stl::vector<Shader>&, const uf::stl::string& = "" );
uf::stl::vector<const Shader*> getShaders( const uf::stl::vector<Shader>&, const uf::stl::string& = "" ) const;
void collectBuffers( const Shader& shader, const RenderMode& renderMode, const Graphic& graphic, const std::function<void(const Buffer&)>& lambda ) const;
};
@ -73,6 +72,8 @@ namespace ext {
Shader& getShader( const uf::stl::string& type, const uf::stl::string& pipeline = "" );
const Shader& getShader( const uf::stl::string& type, const uf::stl::string& pipeline = "" ) const;
uf::stl::vector<const Shader*> getShaders( const uf::stl::string& = "" ) const;
bool validate();
};

View File

@ -2,6 +2,7 @@
#include <uf/ext/vulkan/vulkan.h>
#include <uf/ext/vulkan/device.h>
#include <uf/ext/vulkan/graphic.h>
#include <uf/ext/vulkan/initializers.h>
#include <uf/utils/window/window.h>
#include <uf/utils/string/ext.h>
@ -1178,8 +1179,8 @@ void ext::vulkan::Device::initialize() {
}
struct BaseStructure {
VkStructureType sType;
void* pNext;
VkStructureType sType;
void* pNext;
};
std::queue<void*> chain = {};
@ -1457,7 +1458,7 @@ void ext::vulkan::Device::initialize() {
if ( uf::io::exists( uf::io::root + "/cache/vulkan/cache.bin" ) ) {
uf::io::readAsBuffer( buffer, uf::io::root + "/cache/vulkan/cache.bin" );
pipelineCacheCreateInfo.initialDataSize = buffer.size();
pipelineCacheCreateInfo.pInitialData = buffer.data();
pipelineCacheCreateInfo.pInitialData = buffer.data();
}
VK_CHECK_RESULT(vkCreatePipelineCache( device, &pipelineCacheCreateInfo, nullptr, &this->pipelineCache));
@ -1507,6 +1508,10 @@ void ext::vulkan::Device::initialize() {
VK_REGISTER_HANDLE( allocator );
}
{
descriptorAllocator.initialize( logicalDevice );
}
{
vkGetBufferDeviceAddressKHR = reinterpret_cast<PFN_vkGetBufferDeviceAddressKHR>(vkGetDeviceProcAddr(device, "vkGetBufferDeviceAddressKHR"));
vkCmdBuildAccelerationStructuresKHR = reinterpret_cast<PFN_vkCmdBuildAccelerationStructuresKHR>(vkGetDeviceProcAddr(device, "vkCmdBuildAccelerationStructuresKHR"));
@ -1569,6 +1574,12 @@ void ext::vulkan::Device::destroy() {
VK_UNREGISTER_HANDLE( this->pipelineCache );
this->pipelineCache = nullptr;
}
for ( auto& pair : Pipeline::pipelines ) pair.second.destroy();
Pipeline::pipelines.clear();
descriptorAllocator.destroy();
for ( auto& pair : this->commandPool.graphics.container() ) {
vkDestroyCommandPool( this->logicalDevice, pair.second, nullptr );
VK_UNREGISTER_HANDLE( pair.second );
@ -1608,4 +1619,50 @@ void ext::vulkan::Device::destroy() {
VK_UNREGISTER_HANDLE( allocator );
}
void ext::vulkan::DescriptorAllocator::initialize(VkDevice inDevice) {
device = inDevice;
currentPool = createPool();
}
void ext::vulkan::DescriptorAllocator::destroy() {
if ( currentPool ) vkDestroyDescriptorPool(device, currentPool, nullptr);
for ( auto p : usedPools ) vkDestroyDescriptorPool(device, p, nullptr);
usedPools.clear();
}
VkDescriptorPool ext::vulkan::DescriptorAllocator::createPool() {
VkDescriptorPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
poolInfo.maxSets = 1000;
poolInfo.poolSizeCount = (uint32_t) poolSizes.size();
poolInfo.pPoolSizes = poolSizes.data();
VkDescriptorPool pool;
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &poolInfo, nullptr, &pool));
return pool;
}
bool ext::vulkan::DescriptorAllocator::allocate( VkDescriptorSet* set, VkDescriptorSetLayout layout ) {
if ( currentPool == VK_NULL_HANDLE ) currentPool = createPool();
VkDescriptorSetAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = currentPool;
allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = &layout;
VkResult result = vkAllocateDescriptorSets(device, &allocInfo, set);
if ( result == VK_ERROR_OUT_OF_POOL_MEMORY || result == VK_ERROR_FRAGMENTED_POOL ) {
usedPools.push_back(currentPool);
currentPool = createPool();
allocInfo.descriptorPool = currentPool;
result = vkAllocateDescriptorSets(device, &allocInfo, set);
}
return result == VK_SUCCESS;
}
#endif

View File

@ -19,6 +19,8 @@ namespace {
uint32_t VERTEX_BUFFER_BIND_ID = 0;
}
uf::stl::unordered_map<ext::vulkan::GraphicDescriptor, ext::vulkan::Pipeline> ext::vulkan::Pipeline::pipelines;
PFN_vkGetBufferDeviceAddressKHR ext::vulkan::vkGetBufferDeviceAddressKHR = NULL; // = reinterpret_cast<PFN_vkGetBufferDeviceAddressKHR>(vkGetDeviceProcAddr(device, "vkGetBufferDeviceAddressKHR"));
PFN_vkCmdBuildAccelerationStructuresKHR ext::vulkan::vkCmdBuildAccelerationStructuresKHR = NULL; // = reinterpret_cast<PFN_vkCmdBuildAccelerationStructuresKHR>(vkGetDeviceProcAddr(device, "vkCmdBuildAccelerationStructuresKHR"));
PFN_vkBuildAccelerationStructuresKHR ext::vulkan::vkBuildAccelerationStructuresKHR = NULL; // = reinterpret_cast<PFN_vkBuildAccelerationStructuresKHR>(vkGetDeviceProcAddr(device, "vkBuildAccelerationStructuresKHR"));
@ -41,7 +43,7 @@ void ext::vulkan::Pipeline::initialize( const Graphic& graphic, const GraphicDes
this->metadata.type = descriptor.pipeline;
Device& device = *graphic.device;
auto shaders = getShaders( graphic.material.shaders, descriptor.pipeline );
auto shaders = graphic.material.getShaders( descriptor.pipeline );
assert( shaders.size() > 0 );
uint32_t subpass = descriptor.subpass;
@ -78,6 +80,7 @@ void ext::vulkan::Pipeline::initialize( const Graphic& graphic, const GraphicDes
offset += len;
}
}
/*
for ( auto& descriptor : descriptorSetLayoutBindings ) {
if ( descriptorTypes.count( descriptor.descriptorType ) < 0 ) descriptorTypes[descriptor.descriptorType] = 0;
descriptorTypes[descriptor.descriptorType] += descriptor.descriptorCount;
@ -92,6 +95,7 @@ void ext::vulkan::Pipeline::initialize( const Graphic& graphic, const GraphicDes
);
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool));
VK_REGISTER_HANDLE( descriptorPool );
*/
VkDescriptorSetLayoutCreateInfo descriptorLayout = ext::vulkan::initializers::descriptorSetLayoutCreateInfo(
descriptorSetLayoutBindings.data(),
@ -100,12 +104,16 @@ void ext::vulkan::Pipeline::initialize( const Graphic& graphic, const GraphicDes
VK_CHECK_RESULT(vkCreateDescriptorSetLayout( device, &descriptorLayout, nullptr, &descriptorSetLayout ));
VK_REGISTER_HANDLE( descriptorSetLayout );
UF_ASSERT(device.descriptorAllocator.allocate( &descriptorSet, descriptorSetLayout ));
/*
VkDescriptorSetAllocateInfo allocInfo = ext::vulkan::initializers::descriptorSetAllocateInfo(
descriptorPool,
&descriptorSetLayout,
1
);
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet));
*/
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = ext::vulkan::initializers::pipelineLayoutCreateInfo(
&descriptorSetLayout,
@ -396,7 +404,7 @@ void ext::vulkan::Pipeline::record( const Graphic& graphic, VkCommandBuffer comm
return record( graphic, descriptor, commandBuffer, pass, draw, offset );
}
void ext::vulkan::Pipeline::record( const Graphic& graphic, const GraphicDescriptor& descriptor, VkCommandBuffer commandBuffer, size_t pass, size_t draw, size_t offset ) const {
auto shaders = getShaders( graphic.material.shaders, descriptor.pipeline );
auto shaders = graphic.material.getShaders( descriptor.pipeline );
// create dynamic offset ranges
STATIC_THREAD_LOCAL(uf::stl::vector<uint32_t>, dynamicOffsets);
@ -507,7 +515,7 @@ void ext::vulkan::Pipeline::update( const Graphic& graphic, const GraphicDescrip
RenderMode& renderMode = ext::vulkan::getRenderMode(descriptor.renderMode, true);
auto& renderTarget = renderMode.getRenderTarget(/*descriptor.renderTarget*/);
auto shaders = getShaders( graphic.material.shaders, descriptor.pipeline );
auto shaders = graphic.material.getShaders( descriptor.pipeline );
uf::stl::vector<VkWriteDescriptorSet> writeDescriptorSets;
uf::stl::vector<uf::renderer::AccelerationStructure> tlases;
@ -948,32 +956,17 @@ void ext::vulkan::Pipeline::destroy() {
}
*/
}
uf::stl::vector<ext::vulkan::Shader*> ext::vulkan::Pipeline::getShaders( uf::stl::vector<ext::vulkan::Shader>& shaders, const uf::stl::string& type ) {
uf::stl::unordered_map<uf::stl::string, ext::vulkan::Shader*> map;
uf::stl::vector<ext::vulkan::Shader*> res;
bool isCompute = false;
for ( auto& shader : shaders ) {
if ( shader.metadata.pipeline != "" && shader.metadata.pipeline != (type == "" ? metadata.type : type) ) continue;
if ( shader.descriptor.stage == VK_SHADER_STAGE_COMPUTE_BIT ) isCompute = true;
}
for ( auto& shader : shaders ) {
if ( shader.metadata.pipeline != "" && shader.metadata.pipeline != (type == "" ? metadata.type : type) ) continue;
if ( isCompute && shader.descriptor.stage != VK_SHADER_STAGE_COMPUTE_BIT ) continue;
map[shader.metadata.type] = &shader;
}
for ( auto pair : map ) res.insert( res.begin(), pair.second);
return res;
}
uf::stl::vector<const ext::vulkan::Shader*> ext::vulkan::Pipeline::getShaders( const uf::stl::vector<ext::vulkan::Shader>& shaders, const uf::stl::string& type ) const {
uf::stl::vector<const ext::vulkan::Shader*> ext::vulkan::Material::getShaders( const uf::stl::string& type ) const {
uf::stl::unordered_map<uf::stl::string, const ext::vulkan::Shader*> map;
uf::stl::vector<const ext::vulkan::Shader*> res;
bool isCompute = false;
for ( auto& shader : shaders ) {
if ( shader.metadata.pipeline != "" && shader.metadata.pipeline != (type == "" ? metadata.type : type) ) continue;
if ( shader.metadata.pipeline != "" && shader.metadata.pipeline != type ) continue;
if ( shader.descriptor.stage == VK_SHADER_STAGE_COMPUTE_BIT ) isCompute = true;
}
for ( auto& shader : shaders ) {
if ( shader.metadata.pipeline != "" && shader.metadata.pipeline != (type == "" ? metadata.type : type) ) continue;
if ( shader.metadata.pipeline != "" && shader.metadata.pipeline != type ) continue;
if ( isCompute && shader.descriptor.stage != VK_SHADER_STAGE_COMPUTE_BIT ) continue;
map[shader.metadata.type] = &shader;
}
@ -1852,7 +1845,7 @@ void ext::vulkan::Graphic::record( VkCommandBuffer commandBuffer, const GraphicD
if ( !pipeline.metadata.process ) return;
pipeline.record(*this, descriptor, commandBuffer, pass, draw, offset);
auto shaders = pipeline.getShaders( material.shaders, descriptor.pipeline );
auto shaders = material.getShaders( descriptor.pipeline );
for ( auto* shader : shaders ) {
if ( shader->descriptor.stage == VK_SHADER_STAGE_COMPUTE_BIT ) return;
if (