From 7297eba8899fbfdab9ffe5c580786ccd53cadbd3 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Sat, 13 Jan 2024 01:41:12 +0000 Subject: [PATCH] Uniform buffers are now slices of dedicated allocs (#48) This should fix an issue where draw calls could flicker if a defrag was in progress and a uniform buffer was being used. Uniform buffer "pools" are now just a single dedicated VulkanBuffer, and the uniform buffer objects are offsets into that buffer. Reviewed-on: https://gitea.moonside.games/MoonsideGames/Refresh/pulls/48 --- src/Refresh_Driver_Vulkan.c | 122 +++++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 50 deletions(-) diff --git a/src/Refresh_Driver_Vulkan.c b/src/Refresh_Driver_Vulkan.c index 5055166..3e00de1 100644 --- a/src/Refresh_Driver_Vulkan.c +++ b/src/Refresh_Driver_Vulkan.c @@ -79,7 +79,8 @@ typedef struct VulkanExtensions #define ALLOCATION_INCREMENT 16000000 /* 16MB */ #define TRANSFER_BUFFER_STARTING_SIZE 8000000 /* 8MB */ #define POOLED_TRANSFER_BUFFER_SIZE 16000000 /* 16MB */ -#define UBO_BUFFER_SIZE 16000 /* 16KB */ +#define UBO_BUFFER_SIZE 16000000 /* 16MB */ +#define UBO_SECTION_SIZE 4000 /* 4KB */ #define DESCRIPTOR_POOL_STARTING_SIZE 128 #define DEFRAG_TIME 200 #define WINDOW_DATA "Refresh_VulkanWindowData" @@ -719,8 +720,8 @@ typedef struct VulkanUniformBufferPool VulkanUniformBufferPool; typedef struct VulkanUniformBuffer { VulkanUniformBufferPool *pool; - VulkanBufferContainer *vulkanBufferContainer; /* use container to avoid defrag issues */ - VkDeviceSize offset; + VkDeviceSize poolOffset; /* memory offset relative to the pool buffer */ + VkDeviceSize offset; /* based on uniform pushes */ VkDescriptorSet descriptorSet; } VulkanUniformBuffer; @@ -746,10 +747,13 @@ typedef struct VulkanUniformDescriptorPool uint32_t availableDescriptorSetCount; } VulkanUniformDescriptorPool; +/* This is actually just one buffer that we carve slices out of. */ struct VulkanUniformBufferPool { VulkanUniformBufferType type; VulkanUniformDescriptorPool descriptorPool; + VulkanBuffer *buffer; + VkDeviceSize nextAvailableOffset; SDL_mutex *lock; VulkanUniformBuffer **availableBuffers; @@ -1776,6 +1780,8 @@ typedef struct VulkanRenderer VkDescriptorSetLayout vertexUniformDescriptorSetLayout; VkDescriptorSetLayout fragmentUniformDescriptorSetLayout; VkDescriptorSetLayout computeUniformDescriptorSetLayout; + + VulkanBuffer *dummyBuffer; VulkanUniformBuffer *dummyVertexUniformBuffer; VulkanUniformBuffer *dummyFragmentUniformBuffer; VulkanUniformBuffer *dummyComputeUniformBuffer; @@ -4197,6 +4203,36 @@ static VulkanUniformBufferPool* VULKAN_INTERNAL_CreateUniformBufferPool( VulkanUniformBufferType uniformBufferType ) { VulkanUniformBufferPool* uniformBufferPool = SDL_malloc(sizeof(VulkanUniformBufferPool)); + VulkanResourceAccessType resourceAccessType; + + if (uniformBufferType == UNIFORM_BUFFER_VERTEX) + { + resourceAccessType = RESOURCE_ACCESS_VERTEX_SHADER_READ_UNIFORM_BUFFER; + } + else if (uniformBufferType == UNIFORM_BUFFER_FRAGMENT) + { + resourceAccessType = RESOURCE_ACCESS_FRAGMENT_SHADER_READ_UNIFORM_BUFFER; + } + else if (uniformBufferType == UNIFORM_BUFFER_COMPUTE) + { + resourceAccessType = RESOURCE_ACCESS_COMPUTE_SHADER_READ_UNIFORM_BUFFER; + } + else + { + Refresh_LogError("Unrecognized uniform buffer type!"); + return 0; + } + + uniformBufferPool->buffer = VULKAN_INTERNAL_CreateBuffer( + renderer, + UBO_BUFFER_SIZE, + resourceAccessType, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + 0, + 1 + ); + + uniformBufferPool->nextAvailableOffset = 0; uniformBufferPool->type = uniformBufferType; uniformBufferPool->lock = SDL_CreateMutex(); @@ -4230,12 +4266,6 @@ static void VULKAN_INTERNAL_BindUniformBuffer( commandBuffer->boundUniformBuffers[commandBuffer->boundUniformBufferCount] = uniformBuffer; commandBuffer->boundUniformBufferCount += 1; - - VULKAN_INTERNAL_TrackBuffer( - renderer, - commandBuffer, - uniformBuffer->vulkanBufferContainer->vulkanBuffer - ); } /* Buffer indirection so we can cleanly defrag */ @@ -4304,22 +4334,18 @@ static uint8_t VULKAN_INTERNAL_CreateUniformBuffer( VulkanUniformBuffer *uniformBuffer = SDL_malloc(sizeof(VulkanUniformBuffer)); uniformBuffer->pool = bufferPool; - uniformBuffer->vulkanBufferContainer = VULKAN_INTERNAL_CreateBufferContainer( - renderer, - UBO_BUFFER_SIZE, - resourceAccessType, - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - 0 - ); + uniformBuffer->poolOffset = bufferPool->nextAvailableOffset; + uniformBuffer->offset = 0; - if (uniformBuffer->vulkanBufferContainer == NULL) + bufferPool->nextAvailableOffset += UBO_SECTION_SIZE; + + if (bufferPool->nextAvailableOffset >= UBO_BUFFER_SIZE) { - Refresh_LogError("Failed to create buffer for uniform buffer!"); + Refresh_LogError("Uniform buffer overflow!"); + SDL_free(uniformBuffer); return 0; } - uniformBuffer->offset = 0; - /* Allocate a descriptor set for the uniform buffer */ if (bufferPool->descriptorPool.availableDescriptorSetCount == 0) @@ -4329,6 +4355,7 @@ static uint8_t VULKAN_INTERNAL_CreateUniformBuffer( &bufferPool->descriptorPool )) { Refresh_LogError("Failed to add uniform descriptor pool!"); + SDL_free(uniformBuffer); return 0; } } @@ -4393,15 +4420,7 @@ static VulkanUniformBuffer* VULKAN_INTERNAL_CreateDummyUniformBuffer( } VulkanUniformBuffer *uniformBuffer = SDL_malloc(sizeof(VulkanUniformBuffer)); - uniformBuffer->pool = NULL; - uniformBuffer->vulkanBufferContainer = VULKAN_INTERNAL_CreateBufferContainer( - renderer, - 1, - resourceAccessType, - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - 1 - ); - + uniformBuffer->poolOffset = 0; uniformBuffer->offset = 0; /* Allocate a descriptor set for the uniform buffer */ @@ -4416,7 +4435,7 @@ static VulkanUniformBuffer* VULKAN_INTERNAL_CreateDummyUniformBuffer( /* Update the descriptor set for the first and last time! */ - descriptorBufferInfo.buffer = uniformBuffer->vulkanBufferContainer->vulkanBuffer->buffer; + descriptorBufferInfo.buffer = renderer->dummyBuffer->buffer; descriptorBufferInfo.offset = 0; descriptorBufferInfo.range = VK_WHOLE_SIZE; @@ -4463,11 +4482,11 @@ static void VULKAN_INTERNAL_DestroyUniformBufferPool( /* This is always destroyed after submissions, so all buffers are available */ for (i = 0; i < uniformBufferPool->availableBufferCount; i += 1) { - VULKAN_INTERNAL_DestroyBuffer(renderer, uniformBufferPool->availableBuffers[i]->vulkanBufferContainer->vulkanBuffer); - SDL_free(uniformBufferPool->availableBuffers[i]->vulkanBufferContainer); SDL_free(uniformBufferPool->availableBuffers[i]); } + VULKAN_INTERNAL_DestroyBuffer(renderer, uniformBufferPool->buffer); + SDL_DestroyMutex(uniformBufferPool->lock); SDL_free(uniformBufferPool->availableBuffers); SDL_free(uniformBufferPool); @@ -4502,8 +4521,8 @@ static VulkanUniformBuffer* VULKAN_INTERNAL_AcquireUniformBufferFromPool( /* Update the descriptor set with the correct range */ - descriptorBufferInfo.buffer = uniformBuffer->vulkanBufferContainer->vulkanBuffer->buffer; - descriptorBufferInfo.offset = 0; + descriptorBufferInfo.buffer = uniformBuffer->pool->buffer->buffer; + descriptorBufferInfo.offset = uniformBuffer->poolOffset; descriptorBufferInfo.range = blockSize; writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; @@ -5183,13 +5202,7 @@ static void VULKAN_DestroyDevice( SDL_free(renderer->submittedCommandBuffers); - VULKAN_INTERNAL_DestroyBuffer(renderer, renderer->dummyVertexUniformBuffer->vulkanBufferContainer->vulkanBuffer); - VULKAN_INTERNAL_DestroyBuffer(renderer, renderer->dummyFragmentUniformBuffer->vulkanBufferContainer->vulkanBuffer); - VULKAN_INTERNAL_DestroyBuffer(renderer, renderer->dummyComputeUniformBuffer->vulkanBufferContainer->vulkanBuffer); - - SDL_free(renderer->dummyVertexUniformBuffer->vulkanBufferContainer); - SDL_free(renderer->dummyFragmentUniformBuffer->vulkanBufferContainer); - SDL_free(renderer->dummyComputeUniformBuffer->vulkanBufferContainer); + VULKAN_INTERNAL_DestroyBuffer(renderer, renderer->dummyBuffer); SDL_free(renderer->dummyVertexUniformBuffer); SDL_free(renderer->dummyFragmentUniformBuffer); @@ -7815,7 +7828,7 @@ static uint32_t VULKAN_PushVertexShaderUniforms( if ( vulkanCommandBuffer->vertexUniformBuffer->offset + graphicsPipeline->vertexUniformBlockSize >= - UBO_BUFFER_SIZE + UBO_SECTION_SIZE ) { /* We're out of space in this buffer, bind the old one and acquire a new one */ VULKAN_INTERNAL_BindUniformBuffer( @@ -7833,8 +7846,8 @@ static uint32_t VULKAN_PushVertexShaderUniforms( offset = vulkanCommandBuffer->vertexUniformBuffer->offset; VULKAN_INTERNAL_SetBufferData( - vulkanCommandBuffer->vertexUniformBuffer->vulkanBufferContainer->vulkanBuffer, - vulkanCommandBuffer->vertexUniformBuffer->offset, + vulkanCommandBuffer->vertexUniformBuffer->pool->buffer, + vulkanCommandBuffer->vertexUniformBuffer->poolOffset + vulkanCommandBuffer->vertexUniformBuffer->offset, data, dataLengthInBytes ); @@ -7859,7 +7872,7 @@ static uint32_t VULKAN_PushFragmentShaderUniforms( if ( vulkanCommandBuffer->fragmentUniformBuffer->offset + graphicsPipeline->fragmentUniformBlockSize >= - UBO_BUFFER_SIZE + UBO_SECTION_SIZE ) { /* We're out of space in this buffer, bind the old one and acquire a new one */ VULKAN_INTERNAL_BindUniformBuffer( @@ -7877,8 +7890,8 @@ static uint32_t VULKAN_PushFragmentShaderUniforms( offset = vulkanCommandBuffer->fragmentUniformBuffer->offset; VULKAN_INTERNAL_SetBufferData( - vulkanCommandBuffer->fragmentUniformBuffer->vulkanBufferContainer->vulkanBuffer, - vulkanCommandBuffer->fragmentUniformBuffer->offset, + vulkanCommandBuffer->fragmentUniformBuffer->pool->buffer, + vulkanCommandBuffer->fragmentUniformBuffer->poolOffset + vulkanCommandBuffer->fragmentUniformBuffer->offset, data, dataLengthInBytes ); @@ -7902,7 +7915,7 @@ static uint32_t VULKAN_PushComputeShaderUniforms( if ( vulkanCommandBuffer->computeUniformBuffer->offset + computePipeline->uniformBlockSize >= - UBO_BUFFER_SIZE + UBO_SECTION_SIZE ) { /* We're out of space in this buffer, bind the old one and acquire a new one */ VULKAN_INTERNAL_BindUniformBuffer( @@ -7920,8 +7933,8 @@ static uint32_t VULKAN_PushComputeShaderUniforms( offset = vulkanCommandBuffer->computeUniformBuffer->offset; VULKAN_INTERNAL_SetBufferData( - vulkanCommandBuffer->computeUniformBuffer->vulkanBufferContainer->vulkanBuffer, - vulkanCommandBuffer->computeUniformBuffer->offset, + vulkanCommandBuffer->computeUniformBuffer->pool->buffer, + vulkanCommandBuffer->computeUniformBuffer->poolOffset + vulkanCommandBuffer->computeUniformBuffer->offset, data, dataLengthInBytes ); @@ -11992,6 +12005,15 @@ static Refresh_Device* VULKAN_CreateDevice( /* Dummy Uniform Buffers */ + renderer->dummyBuffer = VULKAN_INTERNAL_CreateBuffer( + renderer, + 1, + RESOURCE_ACCESS_GENERAL, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + 0, + 1 + ); + renderer->dummyVertexUniformBuffer = VULKAN_INTERNAL_CreateDummyUniformBuffer( renderer, UNIFORM_BUFFER_VERTEX