Command Buffers and Command Pools
In Vulkan, whenever we want to draw a 3D scene from vertices and vertex attributes, we will use command buffers. Command buffers cannot be allocated directly. Instead, they are provided by a command pool. They are passed to the GPU to execute operations in the form of queues. The command buffer is not limited to a subset of operations based on its intended use. For example, if we want, we can use graphics operations and compute commands within the same buffer. However, queues are separated into different types. We'll call these queue families. All queues are specialized as we've seen previously (checking for graphics queues).
Command Pool Create Info
We'll first need a command pool before we can get access to a command buffer. We'll be adding a new variable to our VulkanExample
class:
VkCommandPool cmdPool;
And, we'll also create a new method called initCommandPool
:
void VulkanExample::createCommandPool() {}
This method will create a command pool and nothing else. For this section, we won't worry about command buffers just yet. Of course, we'll use a VkCommandPoolCreateInfo
to inform Vulkan of how we want the pool to be setup.
Definition for VkCommandPoolCreateInfo
:
typedef struct VkCommandPoolCreateInfo {
VkStructureType sType;
const void* pNext;
VkCommandPoolCreateFlags flags;
uint32_t queueFamilyIndex;
} VkCommandPoolCreateInfo;
Documentation for VkCommandPoolCreateInfo
:
sType
is the type of this structure.pNext
isNULL
or a pointer to an extension-specific structure.flags
is a combination of bitfield flags indicating usage behavior for the pool and command buffers allocated from it.
Usage for VkCommandPoolCreateInfo
:
VkCommandPoolCreateInfo cmdPoolInfo = {};
cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
cmdPoolInfo.pNext = NULL;
cmdPoolInfo.queueFamilyIndex = swapchain.queueIndex;
cmdPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
VkResult result = vkCreateCommandPool(device, &cmdPoolInfo, NULL, &cmdPool);
assert(result == VK_SUCCESS);
Command Buffer Allocation Info
For this section, we'll be adding a new variable to our VulkanExample
class:
VkCommandBuffer initialCmdBuffer;
In addition to that, we'll also be writing the body of a new method for the next few sections:
void VulkanExample::createInitialCommandBuffer() {}
In the introduction, I mentioned that we can not create command buffers directly. Thus, we'll need an object that will tell Vulkan about the command pool we're using. For this job, we have VkCommandBufferAllocateInfo
.
Definition for VkCommandBufferAllocateInfo
:
typedef struct VkCommandBufferAllocateInfo {
VkStructureType sType;
const void* pNext;
VkCommandPool commandPool;
VkCommandBufferLevel level;
uint32_t commandBufferCount;
} VkCommandBufferAllocateInfo;
Documentation for VkCommandBufferAllocateInfo
:
sType
is the type of this structure.pNext
isNULL
or a pointer to an extension-specific structure.commandPool
is the name of the command pool that the command buffers allocate their memory from.level
determines whether the command buffers are primary or secondary command buffers.
Usage for VkCommandBufferAllocateInfo
:
The command buffer we'll have allocated will be considered a primary command buffer. Thus, we'll say that the level
is VK_COMMAND_BUFFER_LEVEL_PRIMARY
. We'll make use of the command pool we created earlier in this chapter.
VkCommandBufferAllocateInfo cmdBufAllocInfo = {};
cmdBufAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
cmdBufAllocInfo.pNext = NULL;
cmdBufAllocInfo.commandPool = cmdPool;
cmdBufAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
cmdBufAllocInfo.commandBufferCount = 1;
Command Buffer Allocation
Now that we have done all the steps necessary to allocate a command buffer, we can go right ahead! We'll be using the vkAllocateCommandBuffers
method.
Definition for vkAllocateCommandBuffers
:
VkResult vkAllocateCommandBuffers(
VkDevice device,
const VkCommandBufferAllocateInfo* pAllocateInfo,
VkCommandBuffer* pCommandBuffers);
Documentation for vkAllocateCommandBuffers
:
device
is the logical device that owns the command pool.pAllocateInfo
is a pointer to an instance of the VkCommandBufferAllocateInfo structure describing parameters of the allocation.pCommandBuffers
is a pointer to an array ofVkCommandBuffer
handles in which the resulting command buffer objects are returned. The array must be at least the length specified by thecommandBufferCount
member ofpAllocateInfo
. Each allocated command buffer begins in the initial state.
Usage for vkAllocateCommandBuffers
:
Per usual, in addition to calling the method, we'll also verify it was successful.
VkResult result = vkAllocateCommandBuffers(device, &cmdBufAllocInfo,
&initialCmdBuffer);
assert(result == VK_SUCCESS);
Preparing Command Buffer for Recording
Before we can start recording to our command buffer, we'll have to first create a VkCommandBufferBeginInfo
object.
Definition for VkCommandBufferBeginInfo
:
typedef struct VkCommandBufferBeginInfo {
VkStructureType sType;
const void* pNext;
VkCommandBufferUsageFlags flags;
const VkCommandBufferInheritanceInfo* pInheritanceInfo;
} VkCommandBufferBeginInfo;
Documentation for VkCommandBufferBeginInfo
:
sType
is the type of this structure.pNext
isNULL
or a pointer to an extension-specific structure.flags
is a combination of bitfield flags indicating usage behavior for the command buffer.pInheritanceInfo
is a pointer to aVkCommandBufferInheritanceInfo
structure, which is used ifcommandBuffer
is a secondary command buffer. If this is a primary command buffer, then this value is ignored.
Usage for VkCommandBufferBeginInfo
:
VkCommandBufferBeginInfo cmdBufBeginInfo = {};
cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
cmdBufBeginInfo.flags = 0;
cmdBufBeginInfo.pNext = NULL;
Begin Recording to a Command Buffer
Now that we've allocated a command buffer and readied the information Vulkan needs to begin recording, we can call vkBeginCommandBuffer
. This method will take a handle to our command buffer and put it in a recording state.
Definition for vkBeginCommandBuffer
:
VkResult vkBeginCommandBuffer(
VkCommandBuffer commandBuffer,
const VkCommandBufferBeginInfo* pBeginInfo);
Documentation for vkBeginCommandBuffer
:
commandBuffer
is the handle of the command buffer which is to be put in the recording state.pBeginInfo
is an instance of theVkCommandBufferBeginInfo
structure, which defines additional information about how the command buffer begins recording.
Usage for vkBeginCommandBuffer
:
result = vkBeginCommandBuffer(initialCmdBuffer, &cmdBufBeginInfo);
assert(result == VK_SUCCESS);