diff --git a/include/vulkan/basalt_command_buffer.h b/include/vulkan/basalt_command_buffer.h new file mode 100644 index 0000000..7adf3b7 --- /dev/null +++ b/include/vulkan/basalt_command_buffer.h @@ -0,0 +1,59 @@ +#pragma once +#include "vulkan/basalt_pipeline.h" + +namespace basalt +{ + class CommandPool; + + class CommandBuffer + { + public: + CommandBuffer(const CommandBuffer&) = delete; + CommandBuffer& operator =(const CommandBuffer&) = delete; + CommandBuffer(CommandBuffer&& other) noexcept; + CommandBuffer& operator =(CommandBuffer&& other) noexcept; + + CommandBuffer(VkCommandBuffer buffer, CommandPool* pool); + ~CommandBuffer(); + + // TODO: + // Break renderpass stuff out of start/end and into its own start/end function + void start(basalt::RenderPass& render_pass, VkFramebuffer framebuffer, VkRect2D render_area); + void bind_pipeline(basalt::Pipeline& pipeline, VkPipelineBindPoint bind); + void set_viewport(VkViewport port); + void set_scissor(VkRect2D scissor); + void stop (void); + void reset(void); + + VkCommandBuffer vk = VK_NULL_HANDLE; + CommandPool* pool = nullptr; + bool should_free = true; + }; + + + class CommandPool + { + public: + CommandPool(const CommandPool&) = delete; + CommandPool& operator =(const CommandPool&) = delete; + CommandPool(CommandPool&&) noexcept; + CommandPool& operator =(CommandPool&&) noexcept; + + + CommandPool(basalt::Device& device, u32 index); + ~CommandPool(); + + // TODO: + //void get_buffers(VkCommandBufferLevel level, u32 num_buffers, CommandBuffer** buffers_out); + //basalt::darray get_buffers(VkCommandBufferLevel level, u32 num_buffers); + CommandBuffer get_buffer(VkCommandBufferLevel level); + + basalt::Device* device; + VkCommandPool vk; + u32 index; + u32 num_active_buffers : 31; + u32 should_free : 1; + }; + + +} \ No newline at end of file diff --git a/src/vulkan/basalt_command_buffer.cpp b/src/vulkan/basalt_command_buffer.cpp new file mode 100644 index 0000000..f87844b --- /dev/null +++ b/src/vulkan/basalt_command_buffer.cpp @@ -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); }