Created a command buffer and command pool object for managing and tracking as well as wrapping vulkan functions to reduce boilerplate

This commit is contained in:
2025-07-07 22:56:53 +10:00
parent b687d74e32
commit c65d7fd4e3
2 changed files with 217 additions and 0 deletions

View File

@@ -0,0 +1,158 @@
#include "vulkan/basalt_command_buffer.h"
basalt::CommandPool::CommandPool(CommandPool&& other) noexcept
{
BTRACE("Moved command pool %p to %p\n", &other, this);
this->device = other.device;
this->vk = other.vk;
this->num_active_buffers = other.num_active_buffers;
this->index = other.index;
other.device = nullptr;
other.vk = VK_NULL_HANDLE;
other.num_active_buffers = 0;
other.should_free = false;
}
basalt::CommandPool& basalt::CommandPool::operator=(CommandPool&& other) noexcept
{
if (this == &other)
return *this;
BTRACE("Moved command pool %p to %p\n", &other, this);
if (should_free && vk != VK_NULL_HANDLE)
this->~CommandPool();
this->device = other.device;
this->vk = other.vk;
this->num_active_buffers = other.num_active_buffers;
this->index = other.index;
other.device = nullptr;
other.vk = VK_NULL_HANDLE;
other.num_active_buffers = 0;
other.should_free = false;
return *this;
}
basalt::CommandPool::CommandPool(basalt::Device& device, u32 index) :
device(&device), index(index)
{
this->should_free = true;
this->num_active_buffers = 0;
VkCommandPoolCreateInfo ci = {VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO};
ci.pNext = VK_NULL_HANDLE;
ci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
ci.queueFamilyIndex = index;
VkResult err = VK_SUCCESS;
VK_ASSERT(vkCreateCommandPool(device, &ci, device.ctx->vk_alloc, &vk), "Failed to create command pool\n\tAt %s:%d\n\tError %s\n\tFor queue index %u\n", index);
BTRACE("Created command pool %p for device %p (%p)\n", this, &device, device.logical);
}
basalt::CommandPool::~CommandPool()
{
if (should_free)
{
vkDeviceWaitIdle(device->logical);
BASSERT_WARN(num_active_buffers == 0, "Assertion %s failed at %s:%d\n\t%u command buffer(s) of command pool %p were not freed\n", num_active_buffers, this);
BTRACE("Destroyed command pool %p for device %p (%p)\n", this, device, device->logical);
vkDestroyCommandPool(device->logical, vk, device->ctx->vk_alloc);
}
}
basalt::CommandBuffer basalt::CommandPool::get_buffer(VkCommandBufferLevel level)
{
VkCommandBufferAllocateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
info.commandPool = this->vk;
info.level = level;
info.commandBufferCount = 1;
VkCommandBuffer buffer = VK_NULL_HANDLE;
VkResult err = VK_SUCCESS;
VK_ASSERT(vkAllocateCommandBuffers(device->logical, &info, &buffer), "Failed to allocate command buffer from pool\n\tAt %s:%d\n\tError %s\n\tFor command pool %p\n", this);
return basalt::CommandBuffer(buffer, this);
}
basalt::CommandBuffer::CommandBuffer(CommandBuffer&& other) noexcept
{
BTRACE("Moved command buffer %p to %p\n", &other, this);
this->pool = other.pool;
this->vk = other.vk;
this->should_free = other.should_free;
other.pool = nullptr;
other.vk = VK_NULL_HANDLE;
other.should_free = false;
}
basalt::CommandBuffer& basalt::CommandBuffer::operator=(CommandBuffer&& other) noexcept
{
if (&other == this)
return *this;
// If statement is technically redundant - left in for sanity/clarity
if (this->pool != nullptr)
this->~CommandBuffer();
BTRACE("Moved command buffer %p to %p\n", &other, this);
this->pool = other.pool;
this->vk = other.vk;
this->should_free = other.should_free;
other.pool = nullptr;
other.vk = VK_NULL_HANDLE;
other.should_free = false;
return *this;
}
basalt::CommandBuffer::CommandBuffer(VkCommandBuffer buffer, CommandPool* pool) :
vk(buffer), pool(pool)
{
BTRACE("Created command buffer %p from pool %p\n", this, pool);
if (pool != nullptr)
pool->num_active_buffers++;
}
basalt::CommandBuffer::~CommandBuffer()
{
if (pool != nullptr && should_free)
{
BTRACE("Freed command buffer %p from pool %p\n", this, pool);
vkDeviceWaitIdle(pool->device->logical);
vkFreeCommandBuffers(pool->device->logical, pool->vk, 1, &vk);
if ((pool->num_active_buffers - 1) < pool->num_active_buffers)
pool->num_active_buffers--;
}
}
void basalt::CommandBuffer::start(basalt::RenderPass& render_pass, VkFramebuffer framebuffer, VkRect2D render_area)
{
VkResult err = VK_SUCCESS;
VkCommandBufferBeginInfo beg = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
beg.flags = 0;
beg.pNext = VK_NULL_HANDLE;
beg.pInheritanceInfo = VK_NULL_HANDLE;
VK_ASSERT(vkBeginCommandBuffer(vk, &beg), "Failed to begin command buffer\n\tAt %s:%d\n\tError %s\n\tFor command buffer %p\n", this);
VkRenderPassBeginInfo render = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};
render.pNext = VK_NULL_HANDLE;
render.renderPass = render_pass.render_pass;
render.framebuffer = framebuffer;
render.renderArea = render_area;
VkClearValue clear_colour = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
render.clearValueCount = 1;
render.pClearValues = &clear_colour;
vkCmdBeginRenderPass(vk, &render, VK_SUBPASS_CONTENTS_INLINE);
}
void basalt::CommandBuffer::stop(void)
{
vkCmdEndRenderPass(vk);
VkResult err = VK_SUCCESS;
VK_ASSERT(vkEndCommandBuffer(vk), "Failed to end command buffer recording\n\tAt %s:%d\n\tError %s\n\tFor command buffer %p\n", this);
}
void basalt::CommandBuffer::reset(void)
{ vkResetCommandBuffer(vk, 0); }
void basalt::CommandBuffer::bind_pipeline(basalt::Pipeline& pipeline, VkPipelineBindPoint bind)
{ vkCmdBindPipeline(vk, bind, pipeline.vk); }
void basalt::CommandBuffer::set_viewport(VkViewport port)
{ vkCmdSetViewport(vk, 0, 1, &port); }
void basalt::CommandBuffer::set_scissor(VkRect2D scissor)
{ vkCmdSetScissor(vk, 0, 1, &scissor); }