vulkan: Recycle command buffers in vk_command_pool

This is similar to what RADV and several other drivers have been doing
for quite some time.  It's good to have it common.

Reviewed-by: Dave Airlie <airlied@redhat.com>
Reviewed-by: Samuel Pitoiset <samuel.pitoiset@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/18324>
This commit is contained in:
Jason Ekstrand 2022-08-30 11:13:03 -05:00 committed by Marge Bot
parent 11f3323492
commit b825c566bb
4 changed files with 107 additions and 6 deletions

View file

@ -72,6 +72,20 @@ vk_command_buffer_finish(struct vk_command_buffer *command_buffer)
vk_object_base_finish(&command_buffer->base);
}
void
vk_command_buffer_recycle(struct vk_command_buffer *cmd_buffer)
{
/* Reset, returning resources to the pool. The command buffer object
* itself will be recycled but, if the driver supports returning other
* resources such as batch buffers to the pool, it should do so so they're
* not tied up in recycled command buffer objects.
*/
cmd_buffer->ops->reset(cmd_buffer,
VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
vk_object_base_recycle(&cmd_buffer->base);
}
VKAPI_ATTR VkResult VKAPI_CALL
vk_common_ResetCommandBuffer(VkCommandBuffer commandBuffer,
VkCommandBufferResetFlags flags)

View file

@ -176,6 +176,9 @@ vk_command_buffer_reset_render_pass(struct vk_command_buffer *cmd_buffer);
void
vk_command_buffer_reset(struct vk_command_buffer *command_buffer);
void
vk_command_buffer_recycle(struct vk_command_buffer *command_buffer);
void
vk_command_buffer_finish(struct vk_command_buffer *command_buffer);

View file

@ -30,6 +30,23 @@
#include "vk_device.h"
#include "vk_log.h"
static bool
should_recycle_command_buffers(struct vk_device *device)
{
/* They have to be using the common allocation implementation, otherwise
* the recycled command buffers will never actually get re-used
*/
const struct vk_device_dispatch_table *disp = &device->dispatch_table;
if (disp->AllocateCommandBuffers != vk_common_AllocateCommandBuffers)
return false;
/* We need to be able to reset command buffers */
if (device->command_buffer_ops->reset == NULL)
return false;
return true;
}
VkResult MUST_CHECK
vk_command_pool_init(struct vk_device *device,
struct vk_command_pool *pool,
@ -44,7 +61,9 @@ vk_command_pool_init(struct vk_device *device,
pool->queue_family_index = pCreateInfo->queueFamilyIndex;
pool->alloc = pAllocator ? *pAllocator : device->alloc;
pool->command_buffer_ops = device->command_buffer_ops;
pool->recycle_command_buffers = should_recycle_command_buffers(device);
list_inithead(&pool->command_buffers);
list_inithead(&pool->free_command_buffers);
return VK_SUCCESS;
}
@ -58,6 +77,12 @@ vk_command_pool_finish(struct vk_command_pool *pool)
}
assert(list_is_empty(&pool->command_buffers));
list_for_each_entry_safe(struct vk_command_buffer, cmd_buffer,
&pool->free_command_buffers, pool_link) {
cmd_buffer->ops->destroy(cmd_buffer);
}
assert(list_is_empty(&pool->free_command_buffers));
vk_object_base_finish(&pool->base);
}
@ -132,6 +157,38 @@ vk_common_ResetCommandPool(VkDevice device,
return VK_SUCCESS;
}
static void
vk_command_buffer_recycle_or_destroy(struct vk_command_pool *pool,
struct vk_command_buffer *cmd_buffer)
{
assert(pool == cmd_buffer->pool);
if (pool->recycle_command_buffers) {
vk_command_buffer_recycle(cmd_buffer);
list_del(&cmd_buffer->pool_link);
list_add(&cmd_buffer->pool_link, &pool->free_command_buffers);
} else {
cmd_buffer->ops->destroy(cmd_buffer);
}
}
static struct vk_command_buffer *
vk_command_pool_find_free(struct vk_command_pool *pool)
{
if (list_is_empty(&pool->free_command_buffers))
return NULL;
struct vk_command_buffer *cmd_buffer =
list_first_entry(&pool->free_command_buffers,
struct vk_command_buffer, pool_link);
list_del(&cmd_buffer->pool_link);
list_addtail(&cmd_buffer->pool_link, &pool->command_buffers);
return cmd_buffer;
}
VKAPI_ATTR VkResult VKAPI_CALL
vk_common_AllocateCommandBuffers(VkDevice device,
const VkCommandBufferAllocateInfo *pAllocateInfo,
@ -144,10 +201,12 @@ vk_common_AllocateCommandBuffers(VkDevice device,
assert(device == vk_device_to_handle(pool->base.device));
for (i = 0; i < pAllocateInfo->commandBufferCount; i++) {
struct vk_command_buffer *cmd_buffer;
result = pool->command_buffer_ops->create(pool, &cmd_buffer);
if (unlikely(result != VK_SUCCESS))
goto fail;
struct vk_command_buffer *cmd_buffer = vk_command_pool_find_free(pool);
if (cmd_buffer == NULL) {
result = pool->command_buffer_ops->create(pool, &cmd_buffer);
if (unlikely(result != VK_SUCCESS))
goto fail;
}
cmd_buffer->level = pAllocateInfo->level;
@ -159,7 +218,7 @@ vk_common_AllocateCommandBuffers(VkDevice device,
fail:
while (i--) {
VK_FROM_HANDLE(vk_command_buffer, cmd_buffer, pCommandBuffers[i]);
cmd_buffer->ops->destroy(cmd_buffer);
vk_command_buffer_recycle_or_destroy(pool, cmd_buffer);
}
for (i = 0; i < pAllocateInfo->commandBufferCount; i++)
pCommandBuffers[i] = VK_NULL_HANDLE;
@ -173,14 +232,27 @@ vk_common_FreeCommandBuffers(VkDevice device,
uint32_t commandBufferCount,
const VkCommandBuffer *pCommandBuffers)
{
VK_FROM_HANDLE(vk_command_pool, pool, commandPool);
for (uint32_t i = 0; i < commandBufferCount; i++) {
VK_FROM_HANDLE(vk_command_buffer, cmd_buffer, pCommandBuffers[i]);
if (cmd_buffer == NULL)
continue;
vk_command_buffer_recycle_or_destroy(pool, cmd_buffer);
}
}
void
vk_command_pool_trim(struct vk_command_pool *pool,
VkCommandPoolTrimFlags flags)
{
list_for_each_entry_safe(struct vk_command_buffer, cmd_buffer,
&pool->free_command_buffers, pool_link) {
cmd_buffer->ops->destroy(cmd_buffer);
}
assert(list_is_empty(&pool->free_command_buffers));
}
VKAPI_ATTR void VKAPI_CALL
@ -188,5 +260,7 @@ vk_common_TrimCommandPool(VkDevice device,
VkCommandPool commandPool,
VkCommandPoolTrimFlags flags)
{
/* No-op is a valid implementation but may not be optimal */
VK_FROM_HANDLE(vk_command_pool, pool, commandPool);
vk_command_pool_trim(pool, flags);
}

View file

@ -46,8 +46,14 @@ struct vk_command_pool {
/** Command buffer vtable for command buffers allocated from this pool */
const struct vk_command_buffer_ops *command_buffer_ops;
/** True if we should recycle command buffers */
bool recycle_command_buffers;
/** List of all command buffers */
struct list_head command_buffers;
/** List of freed command buffers for trimming. */
struct list_head free_command_buffers;
};
VK_DEFINE_NONDISP_HANDLE_CASTS(vk_command_pool, base, VkCommandPool,
@ -62,6 +68,10 @@ vk_command_pool_init(struct vk_device *device,
void
vk_command_pool_finish(struct vk_command_pool *pool);
void
vk_command_pool_trim(struct vk_command_pool *pool,
VkCommandPoolTrimFlags flags);
#ifdef __cplusplus
}
#endif