In this section, we use the created Render Pass and framebuffer object and implement the Render Pass instance. This Render Pass instance is very simple and will only clear the background image with a specified color. For each swapchain image, different colors can be specified using the pClearValues
field of the VkRenderPassBeginInfo
structure; this structure is then passed to the Render Pass instance.
The Render Pass instance is implemented during the preparation stage in which the command buffers are created. For each swapchain image, a corresponding command buffer object is created. This means, for n swapchain images, we need to create n command buffer objects.
The preparation is done using the VulkanDrawable::prepare()
function, and the rendering of the swapchain images will be performed using the VulkanDrawable::render()
function.
The following diagram shows the call stack of the prepare()
and render()
function:
The following class declaration shows the new member variables and functions added to the VulkanDrawable
class. The prepare()
function produces the command buffers in the vecCmdDraw
vector and records the drawing commands; these are used in the render()
function where the command buffer executes and renders the swapchain images. The recordCommandBuffer()
function records the commands in the Render Pass instance.
For a detailed understanding of the preparation and rendering of a drawing object, refer to the Preparing the drawing object and Rendering the drawing object sections in Chapter 9, Drawing Objects.
class VulkanDrawable { public: // Prepares the drawing object before rendering // Allocate, create, record command buffer void prepare(); // Renders the drawing object void render(); private: // Command buffer for drawing std::vector<VkCommandBuffer> vecCmdDraw; // Prepares render pass instance void recordCommandBuffer(int currentImage, VkCommandBuffer* cmdDraw); };
In this section, we will implement the prepare()
function of VulkanDrawable
. Inside this, the command buffer wrapper class (CommandBufferMgr
) is used to manage (allocate, record, and submit) the command buffers (vecCmdDraw
). The following code implements the prepare()
function:
void VulkanDrawable::prepare() { VulkanDevice* deviceObj = rendererObj->getDevice(); vecCmdDraw.resize(rendererObj->getSwapChain()->scPublicVars .colorBuffer.size()); // For each swapbuffer color image buffer // allocate the corresponding command buffer for (int i = 0; i < rendererObj->getSwapChain()->scPublicVars. colorBuffer.size(); i++){ // Allocate, create and start command buffer recording CommandBufferMgr::allocCommandBuffer(&deviceObj->device, *rendererObj->getCommandPool(), &vecCmdDraw[i]); CommandBufferMgr::beginCommandBuffer(vecCmdDraw[i]); // Create the render pass instance recordCommandBuffer(i, &vecCmdDraw[i]); // Finish the command buffer recording CommandBufferMgr::endCommandBuffer(vecCmdDraw[i]); } }
The implementation first checks the number of color images supported by the swapchain and creates the same number of command buffer objects, associating each one of them logically with the corresponding swapchain image. The clearing of the swapchain image is always performed on the back image (back buffer), while the front image (front buffer) is used to display the rendered contents. Once the back image is cleared and rendered (if any), it is swapped with the front buffer.
Use the created command buffer (vecCmdDraw
) and record the commands inside the recordCommandBuffer()
function. This function specifies the clear color value for each swapchain image using the pClearValues
field of the VkRenderPassBeginInfo
data structure that is passed into the vkCmdBeginRenderPass()
API. The vkCmdBeginRenderPass()
and vkCmdEndRenderPass()
APIs define a scope under which the Render Pass instance commands are recorded.
For more information on Render Pass commands and its associated APIs, refer to the Recording the Render Pass commands section in Chapter 9, Drawing Objects.
There are two objects of the clear values (VkClearValue
). The first one specifies the clear color value of the associated swapchain image indicated by the currentImage
index. The second object specifies the clear color value to be used for the depth image:
void VulkanDrawable::recordCommandBuffer(int currentImage, VkCommandBuffer* cmdDraw) { // Specify the clear color value VkClearValue clearValues[2]; switch (currentImage) { case 0: clearValues[0].color = { 1.0f,0.0f,0.0f,1.0f };break; case 1: clearValues[0].color = { 0.0f,1.0f,0.0f,1.0f };break; case 2: clearValues[0].color = { 0.0f,0.0f,1.0f,1.0f };break; default:clearValues[0].color = { 0.0f,0.0f,0.0f,1.0f };break; } // Specify the depth/stencil clear value clearValues[1].depthStencil.depth = 1.0f; clearValues[1].depthStencil.stencil = 0; // Define the VkRenderPassBeginInfo control structure VkRenderPassBeginInfo renderPassBegin = {}; renderPassBegin.sType=VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; renderPassBegin.renderPass = rendererObj->renderPass; renderPassBegin.framebuffer = rendererObj-> framebuffers[currentImage]; renderPassBegin.renderArea.extent.width = rendererObj->width; renderPassBegin.renderArea.extent.height= rendererObj->height; renderPassBegin.clearValueCount = 2; renderPassBegin.pClearValues = clearValues; // Start recording the render pass instance vkCmdBeginRenderPass(*cmdDraw, &renderPassBegin, VK_SUBPASS_CONTENTS_INLINE); // End of render pass instance recording vkCmdEndRenderPass(*cmdDraw); }
The cleared swapchain images are rendered one by one inside the render()
function, as implemented in the following code snippet. The WSI windowing system extension vkAcquireNextImageKHR()
is used to query the next available swapchain image index. This index indicates which swapchain image will be available for drawing. Using this index, the corresponding command buffer is selected and submitted to the queue. Once processed on the GPU, the swapchain image is ready for displaying purposes using the presentation engine. The presentation is performed using the WSI extension fpQueuePresentKHR
. This API intakes the VkPresentInfoKHR
structure; this contains the swapchain object and the index of the swapchain image that needs to be displayed on the window.
Rendering the drawing object is a separate topic altogether and is out of the scope of this chapter. For more information on this topic and the associated WSI extensions with their related data structures, refer to the Rendering the drawing object section in Chapter 9, Drawing Objects.
The following code implements the clearing where the background color is cleared every second with red, blue and green background color:
void VulkanDrawable::render() { VulkanDevice* deviceObj = rendererObj->getDevice(); VulkanSwapChain* swapChainObj = rendererObj->getSwapChain(); uint32_t& currentColorImage = swapChainObj->scPublicVars. currentColorBuffer; VkSwapchainKHR& swapChain = swapChainObj->scPublicVars. swapChain; // Render each background color for 1 second. Sleep(1000); // Get the index of the next available swapchain image: VkResult result = swapChainObj->fpAcquireNextImageKHR (deviceObj->device, swapChain, UINT64_MAX, VK_NULL_HANDLE, VK_NULL_HANDLE, ¤tColorImage); // Queue the command buffer for execution CommandBufferMgr::submitCommandBuffer(deviceObj->queue, &vecCmdDraw[currentColorImage], NULL); // Present the image in the window VkPresentInfoKHR present = {}; present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; present.swapchainCount = 1; present.pSwapchains = &swapChain; present.pImageIndices = ¤tColorImage; // Queue the image for presentation, result = swapChainObj->fpQueuePresentKHR (deviceObj->queue, &present); assert(result == VK_SUCCESS); }
The following is the output of the implemented exercise. The output will display various background colors, and each image will be displayed for one second.
18.226.133.49