Implementing the wrapper class for a command buffer

This section implements the wrapper class for a command buffer called CommandBufferMgr. This class contains static functions that can be directly used like utility functions without requiring a class object. The class is implemented in a new file called wrapper.h/.cpp; this file will contain multiple utility methods.

Most of the implemented functions in this class are provided with a default implementation. This means that each function provides the user with the flexibility to change the control structure parameters from outside the function call and send them as parametric arguments. The function arguments are inherent defaults, so if you are not specifying anything custom, the function will do all the jobs for you.

The following is the header implementation of the CommandBufferMgr class. It declares four static functions that are responsible for allocating memory, recording the command buffer, and submitting the command buffer to the command queue:

      /**************** Wrapper.h ******************/ 
class CommandBufferMgr{ 
public: 
   // Allocate memory for command buffers from the command pool 
   static void allocCommandBuffer(const VkDevice* device,  
   const VkCommandPool cmdPool, VkCommandBuffer* cmdBuf, const 
   VkCommandBufferAllocateInfo* commandBufferInfo); 
   // Start the command buffer recording 
   static void beginCommandBuffer(VkCommandBuffer cmdBuf, 
         VkCommandBufferBeginInfo* inCmdBufInfo = NULL); 
   // End the command buffer recording 
   static void endCommandBuffer(VkCommandBuffer cmdBuf); 
   // Submit the command buffer for execution 
   static void submitCommandBuffer(const VkQueue& queue, const 
         VkCommandBuffer* cmdBufList, const VkSubmitInfo* submit- 
         Info = NULL, const VkFence& fence = VK_NULL_HANDLE); 
}; 

Implementing the command buffer allocation process

The allocCommandBuffer() function allocates a command buffer (cmdBuf) from the specified command pool (cmdPool). The allocation behavior can be controlled using the VkCommandBufferAllocateInfo pointer argument. When the last parameter of this function has its default parameter value (NULL), then VkCommandBufferAllocateInfo is implemented inside, as shown in the following code, and used for command buffer allocation:

void CommandBufferMgr::allocCommandBuffer(const VkDevice* device,  
const VkCommandPool cmdPool, VkCommandBuffer* cmdBuf, const VkCommandBufferAllocateInfo* commandBufferInfo){ 
   VkResult result; 
 
   // If command information is available use it as it is. 
   if (commandBufferInfo) { 
         result = vkAllocateCommandBuffers 
                     (*device, commandBufferInfo, cmdBuf);  
         assert(!result); 
         return; 
   } 
 
   // Default implementation, create the command buffer

   // allocation info and use the supplied parameter into it 
   VkCommandBufferAllocateInfo cmdInfo = {}; 
   cmdInfo.sType = VK_STRUCTRE_TYPE_COMMAND_BUFFER_ALOCATE_INFO; 
   cmdInfo.pNext            = NULL; 
   cmdInfo.commandPool      = cmdPool; 
   cmdInfo.level            = VK_COMMAND_BUFFER_LEVEL_PRIMARY; 
   cmdInfo.commandBufferCount = (uint32_t) sizeof(cmdBuf)/ 
                                  sizeof(VkCommandBuffer); 
   // Allocate the memory 
   result = vkAllocateCommandBuffers(*device, &cmdInfo, cmdBuf); 
   assert(!result); 
  } 

Recording the command buffer allocation process

API calls can be easily recorded with the simple beginCommandBuffer() and endCommandBuffer() wrapper functions. Refer to the following code to understand the implementation:

 
void CommandBufferMgr::beginCommandBuffer(VkCommandBuffer cmdBuf, 
                     VkCommandBufferBeginInfo* inCmdBufInfo){ 
   VkResult  result; 
   // If the user has specified the custom command buffer use it 
   if (inCmdBufInfo) { 
         result = vkBeginCommandBuffer(cmdBuf, inCmdBufInfo); 
         assert(result == VK_SUCCESS); 
         return; 
   } 
 
   // otherwise, use the default implementation. 
   VkCommandBufferInheritanceInfo cmdBufInheritInfo = {}; 
   cmdBufInheritInfo.sType  = VK_STRUCTURE_TYPE_COMMAND- 
                                 _BUFFER_INHERITANCE_INFO; 
   cmdBufInheritInfo.pNext  = NULL; 
   cmdBufInheritInfo.renderPass   = VK_NULL_HANDLE; 
   cmdBufInheritInfo.subpass = 0; 
   cmdBufInheritInfo.framebuffer  = VK_NULL_HANDLE; 
   cmdBufInheritInfo.occlusionQueryEnable = VK_FALSE; 
   cmdBufInheritInfo.queryFlags         = 0; 
   cmdBufInheritInfo.pipelineStatistics  = 0; 
    
   VkCommandBufferBeginInfo cmdBufInfo   = {}; 
   cmdBufInfo.sType=VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; 
   cmdBufInfo.pNext = NULL; 
   cmdBufInfo.flags = 0; 
   cmdBufInfo.pInheritanceInfo    = &cmdBufInheritInfo; 
 
   result = vkBeginCommandBuffer(cmdBuf, &cmdBufInfo); 
   assert(result == VK_SUCCESS); 
} 

The following code describes the end of command buffer recording:

void CommandBufferMgr::endCommandBuffer(VkCommandBuffer commandBuffer){ 
   VkResult  result; 
   result = vkEndCommandBuffer(commandBuffer); 
   assert(result == VK_SUCCESS); 
} 

How to use command buffer recording functions

In this section, we will use the code snippet from Chapter 7, Buffer Resource, Render Pass, Framebuffer, and Shaders with SPIR-V, and demonstrate the use of the implemented command buffer management utility functions. These functions grately simplify the code for the command buffer creation and submission processes.

In the following code, the Render Pass instance is created using command buffers. First, the command pool (cmdPool) is created and used to allocate a command buffer (vecCmdDraw) using allocCommandBuffer(). The command buffer recording scope is managed between the beginCommandBuffer() and endCommandBuffer() functions. Under these scope-defining functions, a series of commands is recorded (vkCmdBeginRenderPass, vkCmdBindPipeline, vkCmdDraw, and more). Ignore the working of the commands used for Render Pass instance creation for now; we will understand them in detail in Chapter 7, Buffer Resource, Render Pass, Framebuffer, and Shaders with SPIR-V. Here, our purpose is only er recording.

Finally, the command buffer is submitted with submitCommandBuffer(); this function is described in detail in the next section:

   vkCreateCommandPool(device, &cmdPoolInfo, NULL, &cmdPool);
       
   CommandBufferMgr::allocCommandBuffer(&device, 
cmdPool,vecCmdDraw);


   // Start recording the command buffer

   CommandBufferMgr::beginCommandBuffer(vecCmdDraw);


   // Render pass instance 
   vkCmdBeginRenderPass( . . . ); 
   vkCmdBindPipeline(. . .); 
   vkCmdBindDescriptorSets(. . .); 
   vkCmdBindVertexBuffers(. . .); 
   vkCmdSetViewport(. . .); 
   vkCmdSetScissor(. . .); 
   vkCmdDraw(. . .); 
   vkCmdEndRenderPass(. . .);
 
   // End recording the command buffer

   CommandBufferMgr::endCommandBuffer(vecCmdDraw);

   CommandBufferMgr::submitCommandBuffer(queue, &vecCmdDraw);

Submitting the command to the queue

Submission of the prepared command buffer is done with the help of the submitCommandBuffer() function. It takes four parameters. The first parameter specifies the submission queue (queue) to which the second parameter, namely the command buffer (cmdBuffer), is to be submitted for execution. The third parameter (inSubmitInfo) specifies the behavior involved in controlling the submission process. The last parameter signals the completion of the submitted command buffers (fence):

void CommandBufferMgr::submitCommandBuffer(const VkQueue& queue,  
         const VkCommandBuffer cmdBuffer, const  
         VkSubmitInfo* inSubmitInfo, const VkFence& fence){ 
   VkResult result; 
   // If Submit information is available use it as it is.

   // This assumes that the commands are already specified

   // in the structure, hence ignore command buffer  
   if (inSubmitInfo){ 
         vkQueueSubmit(queue, 1, inSubmitInfo, fence); 
         result = vkQueueWaitIdle(queue); 
         return; 
   } 
 
   // Else, create the submit info with specified buffer commands 
   VkSubmitInfo submitInfo = {}; 
   submitInfo.sType   = VK_STRUCTURE_TYPE_SUBMIT_INFO; 
   submitInfo.pNext               = NULL; 
   submitInfo.waitSemaphoreCount  = 0; 
   submitInfo.pWaitSemaphores     = NULL; 
   submitInfo.pWaitDstStageMask   = NULL; 
   submitInfo.commandBufferCount  = (uint32_t)  
               sizeof(commandBuffer)/sizeof(VkCommandBuffer); 
   submitInfo.pCommandBuffers     = commandBuffer; 
   submitInfo.signalSemaphoreCount = 0; 
   submitInfo.pSignalSemaphores   = NULL; 
 
   result = vkQueueSubmit(queue, 1, &submitInfo, fence); 
   assert(!result); 
 
   result = vkQueueWaitIdle(queue); assert(!result); 
} 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.245.233