Rendering the drawing object

Once the Render Pass instance is prepared and recorded successfully in the drawing object's command buffer, we can reuse it each time to draw the object.

Drawing an object comprises three steps. First, we will need to acquire the index to the next available swapchain image onto which the primitive will be drawn or rasterized. Second, we will need to submit the command buffer to the graphics queue to execute the recorded command on the GPU; the GPU executes these commands and paints the available swapchain drawing images with the draw commands. Finally, the drawn image is handed over to the presentation engine, whichrenders the output onto the attached display window. The following subsections will describe each of these three steps in detail.

Acquiring the swapchain image

Before executing the Render Pass instance-recorded commands, we will need to acquire a swapchain image onto which the drawings will be performed. For this, we will need to query the index of the swapchain image that will be available from the system using the WSI extension vkAcquireNextImageKHR(); this will return the index of the swapchain image that your application will render to. This extension is available in the form of a function pointer of type PFN_vkAcquireNextImageKHR.

Upon the API call, it acquires the presentable image onto which the current command buffer will be used to paint and notifies the application that the target presentable image has changed.

Note

Multiple factors influence the availability of presentable images when the API is called. This includes presentation engine implementation, how the VkPresentModeKHR is being used, the total number of images in the swapchain, the number of images that the application owns at any given time, and of course the application's performance.

VkResult vkAcquireNextImageKHR( 
   VkDevice         device, 
   VkSwapchainKHR   swapchain, 
   uint64_t         timeout, 
   VkSemaphore      semaphore, 
   VkFence          fence, 
   uint32_t*        pImageIndex); 

The fields and a description of each parameter follow:

Parameters

Description

device

This indicates the logical VkDevice, which is associated with the swapchain object.

swapchain

This indicates the swapchain object (VkSwapchainKHR) from which the drawing image will be acquired.

timeout

This parameter indicates whether the API is blocking or nonblocking. When a time-out (in nanosecond) is specified, it indicates for how long the function waits or blocks, if no image is available. If the time-out is 0, then this API will not block and return the success or failure error. This field guarantees that the vkAcquireNextImageKHR() API never blocks the system and returns the ownership after a finite time in case of failure.

semaphore

This is the VkSemaphore object and must be unsignaled and should not have any uncompleted signal or wait operations pending. It will be signaled when the presentation engine has released ownership of the image and the device can modify its contents. If not used in the API, this can be equal to VK_NULL_HANDLE.

fence

This is a VkFence object and must be unsignaled and not have any uncompleted signal operations pending. It will become signaled when the presentation engine has released ownership of the image. If not used in the API, this can be equal to VK_NULL_HANDLE. This can be used to measure the frame generation work that matches the presentation rate.

pImageIndex

This retrieves the index of the next presentable image. This index belongs to the index into the array of image handles returned by the vkGetSwapchainImagesKHR() API. If the API does not return the VK_SUCCESS instance, it means the pointed index is not modified.

Note

vkAcquireNextImageKHR() cannot have both semaphore and fence specified as VK_NULL_HANDLE; one of them must be a valid input.

The following table specifies the return value from the vkAcquireNextImageKHR() API, which depends on the timeout field:

Return value

Description

VK_SUCCESS

This means that it has successfully acquired the presentable image.

VK_ERROR_SURFACE_LOST_KHR

This appears when the presentable surface is no longer available.

VK_NOT_READY

This indicates that no image is available and when timeout is 0.

VK_TIMEOUT

When timeout is a nonzero mean (> 0 and <UINT64_MAX), this indicates that no presentable image is available in the allowed duration.

VK_SUBOPTIMAL_KHR

In this case, the returned presentable image no longer matches with swapchain surface properties, but it can still be used.

VK_ERROR_OUT_OF_DATE_KHR

Here, the returned presentable image is no longer compatible with the swapchain and thus cannot be further used for presentation with swapchain. In this case, the application must query the compatible surface properties and recreate the swapchain with the required surface properties in order to continue with presentation service.

Executing the drawing command buffer object

The drawing command buffer object is executed by submitting it into the graphics queue using the CommandBufferMgr::submitCommandBuffer() function, which internally calls the vkQueueSubmit() API to submit the command buffer.

Note

For more information on the submitCommandBuffer() API and its usage, please refer to Chapter 5, Command Buffer and Memory Management in Vulkan. In this chapter, you can refer to the subsection Submitting the command to queue under Implementing the wrapper class for command buffer.

Displaying the output with the presentation engine

When the drawing object's command buffer is executed, the target presentation image is painted with the recorded commands. This image is then queued to the presentation engine using the vkQueuePresentKHR() API, which renders the output presentation image onto the display output.

VkResult vkQueuePresentKHR( 
  VkQueue                 queue, 
  const VkPresentInfoKHR* pPresentInfo); 

The following parameters are used inside the vkQueuePresentKHR()API:

Parameters

Description

queue

This is a VkQueue object, which is capable of presentation and has graphics queue capabilities. Also, this queue belongs to the same device as the image's swapchain.

pPresentInfo

This is a pointer to a VkPresentInfoKHR structure specifying the presentation metadata.

The following is the syntax and description of VkPresentInfoKHR():

typedef struct VkPresentInfoKHR { 
  VkStructureType         sType; 
  const void*             pNext; 
  uint32_t                waitSemaphoreCount; 
  const VkSemaphore*      pWaitSemaphores; 
  uint32_t                swapchainCount; 
  const VkSwapchainKHR*   pSwapchains; 
  const uint32_t*         pImageIndices; 
  VkResult*               pResults; 
} VkPresentInfoKHR; 

The fields and a description of each parameter follow:

Parameters

Description

sType

This specifies the type of this structure; this must be VK_STRUCTURE_TYPE_PRESENT_INFO_KHR.

pNext

This could be a valid pointer to an extension-specific structure or could be NULL.

waitSemaphoreCount

This field indicates the count of semaphores the presentation engine should wait on before displaying the image.

pWaitSemaphores

This specifies the semaphores to wait on before issuing the present request. This is a Non-NULL VkSemaphore object array with size equal to waitSemaphoreCount.

swapchainCount

There can be more than one swapchain presented; this field indicates the number of swapchains that will be presented using this API command.

pSwapchains

This indicates an array of the VkSwapchainKHR objects with size equal to the swapchainCount entries.

pImageIndices

This is an array of presentable image indices of each swapchain's presentable images, with total entries specified by swapchainCount. Each entry in the index array indicates the presentable image that will be used in the presentation.

pResults

This field, if non-NULL, returns the status (VkResult typed) for each presentable image; the number of entries is equal to swapchainCount. If an application does not require the per-swapchain results, the pResults can be set as NULL.

Note

The vkQueuePresentKHR is capable of presenting multiple presentable images from corresponding swapchains. It releases the ownership of the images (indicated by pImageIndices; refer to the following VkPresentInfoKHR) to the presentation engine. These presented images must not be used again until the application regains control of them using the vkAcquireNextImageKHR() API (and must wait until the returned semaphore is signaled, or fence is completed).

The presentation images sent to the queue are always processed in order; the transfer of the ownership of a presentation image to the presentation engine happens with the submission in the queue. These presentable images are only performed if the submitted semaphore is signaled, indicating that no prior rendering operation is pending. The presentation time is very implementation-specific; it may be affected by the semantics of the presentation engine and the native platform in use.

The following table specifies the return value from the vkQueuePresentKHR() API:

Return value

Description

VK_SUCCESS

This means that it has successfully acquired the presentable image.

VK_ERROR_SURFACE_LOST_KHR

This appears when the presentable surface is no longer available.

VK_SUBOPTIMAL_KHR

In this case, the returned presentable image no longer matches with swapchain surface properties, but it can still be used for paint purposes successfully.

VK_ERROR_OUT_OF_DATE_KHR

Here, the returned presentable image is no longer compatible with the swapchain and thus cannot be further used for presentation with the swapchain. In this case, the application must query the compatible surface properties and recreate the swapchain with the required surface properties in order to continue with the presentation service.

When a presentable image is given to the presentation engine, the presentation does not change the contents of this image. If this image is again reacquired using vkAcquireNextImageKHR() and the transitioning is taken away from the VK_IMAGE_LAYOUT_PRESENT_SRC_KHR layout, even then the contents remain the same as they were before transitioning. In contrast, if some other mechanism modifies the platform window other than Vulkan, then the contents of all the presentable images in the swapchain become undefined.

Implementing drawing object rendering

Let's understand the implemented rendering code. First, acquire the presentable image index using the vkAcquireNextImageKHR() API extension; the index is returned in the currentColorImage variable. This swapchain extension is stored as a function pointer (fpAcquireNextImageKHR) in the VulkanRenderclass.

Note

For more information on querying swapchain extensions, please refer to the subsection Querying swapchain extensions in Chapter 6, Allocating Image Resources and Building a Swapchain with WSI.

You must provide at least one of the sync objects (semaphore or fence); otherwise, you will have no idea when you can use the image. For example, your image will still be read by the presentation engine when you try to acquire it. The vkAcquireNextImageKHR() is permitted to return as soon as it has identified the image that it has to give you next, not when that image is actually usable. For this reason, synchronization is very important at this step. Vulkan provides two ways to synchronize the swapchain image using semaphore and fences. When semaphore and fences are used, they ensure that when the image is acquired it has no previous pending operation (such as the presentation engine reading it).

In this example, we used a semaphore object (presentCompleteSemaphore) for synchronization purposes; this object is passed into the vkAcquireNextImageKHR() to be associated with the image, and this semaphore is signaled when the image can be rendered.

Use the retrieved image index (currentColorImage) and get the corresponding command buffer from the vecCmdDraw vector. Create the VkSubmitInfo control structure and specify the create semaphore object (presentCompleteSemaphore) in order to submit the command buffer. Upon submission, the commands will only begin execution when a semaphore is signaled; in other words, the image is ready to paint with drawing commands.

As a final approach, the painted image is then queued in the presentation engine for display purposes using the fpQueuePresentKHR API (vkQueuePresentKHR), transferring the ownership to the presentation engine. It is very important to ensure that when an image is used by the presentation engine it is not being painted or has any pending operations since the last command buffer submission. This can be simply checked using another semaphore object called drawingCompleteSemaphore; this object is passed into the VkSubmitInfo class pSignalSemaphores field before the command buffer is submitted into the queue. This semaphore is signaled when the command buffer is successfully processed, removing any chance to overlap with the presentation engine's ownership. Once the presentable image is displayed, the presentation engine relinquishes the ownership. vkAcquireNextImageKHR() can query the same image again and get the ownership.

The following is the implementation code that demonstrates the rendering of the object in Vulkan:

void VulkanDrawable::render() 
{ 
  VulkanDevice* deviceObj      = rendererObj->getDevice(); 
  VulkanSwapChain* swapChainObj= rendererObj->getSwapChain(); 
  uint32_t&currentColorImage   = swapChainObj-> 
                scPublicVars.currentColorBuffer; 
  VkSwapchainKHR& swapChain     = swapChainObj-> 
                scPublicVars.swapChain; 
 
  VkFence nullFence             = VK_NULL_HANDLE; 
   
  // Get the index of the next available swapchain image: 
  VkResult result = swapChainObj->fpAcquireNextImageKHR( 
  deviceObj->device, swapChain,UINT64_MAX, 
  presentCompleteSemaphore, VK_NULL_HANDLE, 
  &currentColorImage); 
 
  VkPipelineStageFlags submitPipelineStages = 
  VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; 
 
  // Prepare the submit into control structure 
  VkSubmitInfo submitInfo = {}; 
  submitInfo.sType= VK_STRUCTURE_TYPE_SUBMIT_INFO; 
  submitInfo.pNext= NULL; 
  submitInfo.waitSemaphoreCount = 1;

  submitInfo.pWaitSemaphores= &presentCompleteSemaphore; 
  submitInfo.pWaitDstStageMask= &submitPipelineStages; 
  submitInfo.commandBufferCount= (uint32_t)sizeof(&vecCmdDraw 
  [currentColorImage]) / sizeof(VkCommandBuffer); 
  submitInfo.pCommandBuffers  = &vecCmdDraw[currentColorImage]; 
  submitInfo.signalSemaphoreCount= 1;

  submitInfo.pSignalSemaphores   = &drawingCompleteSemaphore;

  // Queue the command buffer for execution 
  CommandBufferMgr::submitCommandBuffer(deviceObj->queue, 
  &cmdDraw[currentColorImage],&submitInfo);
  // Present the image in the window 
  VkPresentInfoKHR present; 
  present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; 
  present.pNext = NULL; 
  present.swapchainCount= 1; 
  present.pSwapchains= &swapChain; 
  present.pImageIndices= &currentColorImage; 
  present.pWaitSemaphores= &drawingCompleteSemaphore;

  present.waitSemaphoreCount= 1; 
  present.pResults= NULL; 
  // Queue the image for presentation, 
  result = swapChainObj->fpQueuePresentKHR
  (deviceObj->queue, &present); 
  assert(result == VK_SUCCESS); 
} 

The semaphore objects are created in the constructor of the drawable class and reused throughout the application as shown in the following.

VulkanDrawable::VulkanDrawable(VulkanRenderer* parent) { 
  memset(&VertexBuffer, 0, sizeof(VertexBuffer)); 
  rendererObj = parent; 
 
  // Prepare the semaphore create info data structure

  VkSemaphoreCreateInfo presentCompleteSemaphoreCreateInfo; 
  presentCompleteSemaphoreCreateInfo.sType = 
  VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; 
  presentCompleteSemaphoreCreateInfo.pNext = NULL; 
  presentCompleteSemaphoreCreateInfo.flags = 0; 
   
  VkSemaphoreCreateInfo drawingCompleteSemaphoreCreateInfo; 
  drawingCompleteSemaphoreCreateInfo.sType = 
  VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; 
  drawingCompleteSemaphoreCreateInfo.pNext = NULL; 
  drawingCompleteSemaphoreCreateInfo.flags = 0; 
 
  VulkanDevice* deviceObj = VulkanApplication::GetInstance()-> 
  deviceObj; 
 
  vkCreateSemaphore(deviceObj->device,

  &presentCompleteSemaphoreCreateInfo, NULL,

  &presentCompleteSemaphore);
 

  vkCreateSemaphore(deviceObj->device,

  &drawingCompleteSemaphoreCreateInfo, NULL,

  &drawingCompleteSemaphore); 
} 

These semaphore objects can be destroyed using the user-defined destroySynchronizationObjects() function during the de-initialization process:

void VulkanDrawable::destroySynchronizationObjects() 
{ 
  VulkanApplication* appObj = VulkanApplication::GetInstance(); 
  VulkanDevice* deviceObj = appObj->deviceObj; 
  vkDestroySemaphore(deviceObj->device, 
  presentCompleteSemaphore, NULL); 
  vkDestroySemaphore(deviceObj->device, 
  drawingCompleteSemaphore, NULL); 
} 

The following is the output of the program:

Implementing drawing object rendering

..................Content has been hidden....................

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