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:
- sTypeis the type of this structure.
- pNextis- NULLor a pointer to an extension-specific structure.
- flagsis 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:
- sTypeis the type of this structure.
- pNextis- NULLor a pointer to an extension-specific structure.
- commandPoolis the name of the command pool that the command buffers allocate their memory from.
- leveldetermines 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:
- deviceis the logical device that owns the command pool.
- pAllocateInfois a pointer to an instance of the VkCommandBufferAllocateInfo structure describing parameters of the allocation.
- pCommandBuffersis a pointer to an array of- VkCommandBufferhandles in which the resulting command buffer objects are returned. The array must be at least the length specified by the- commandBufferCountmember of- pAllocateInfo. 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:
- sTypeis the type of this structure.
- pNextis- NULLor a pointer to an extension-specific structure.
- flagsis a combination of bitfield flags indicating usage behavior for the command buffer.
- pInheritanceInfois a pointer to a- VkCommandBufferInheritanceInfostructure, which is used if- commandBufferis 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:
- commandBufferis the handle of the command buffer which is to be put in the recording state.
- pBeginInfois an instance of the- VkCommandBufferBeginInfostructure, which defines additional information about how the command buffer begins recording.
Usage for vkBeginCommandBuffer:
result = vkBeginCommandBuffer(initialCmdBuffer, &cmdBufBeginInfo);
assert(result == VK_SUCCESS);