Creating a Render Pass

A render pass is an object which defines the characteristics and configuration of a framebuffer.

What is a render pass?

A render pass contains information about how rendering is performed. This includes the number and type of the attachments that are going to be used while rendering, the rendering subpasses that will be performed, and descriptions of the dependencies between these subpasses.

The attachments, such as colour, depth, and stencil buffers, are what the application will render into. The render pass describes the characteristics of these attachments through attachment descriptions, but it does not define the actual images that will ultimately be written into. This is handled by the frame buffers which will be discussed next.

Render passes also contain a number of subpasses which are essentially batches of rendering. They represent a phase of rendering where a subset of the render passes' attachments are read and written into. The actual rendering work is recorded into one or more of these subpasses. Multiple subpasses can be used to create multi-stage rendering sequences where the data written into an attachment by one subpass can be read by the next subpass.

Creating a render pass

To create a render pass, the attributes of the required attachments and subpasses need to be described.

The descriptions of the render pass attachments are created using two structs: VkAttachmentReference and VkAttachmentDescription.

The VkAttachmentReference struct describes the layout of the attachment and gives it an identifying index in the render pass.

The VkAttachmentDescription struct includes information about the attachment and how to handle it during rendering, such as:
  • the image format and colour space of the attachment
  • whether any multi-sampling needs to be applied to the attachment
  • what to do with the contents of the attachment at the beginning and end of a subpass
    • At the beginning of the subpass the contents can be loaded from memory, cleared to a uniform colour, or left uninitialised. This behaviour is determined by the loadOp member of this struct.
    • At the end of the sub pass the contents can be stored into memory or discarded. This behaviour is determined by the storeOp member.
Note: loadOp and storeOp are critical for performance for PowerVR and other tile-based architectures, as setting an operation to "don't care" or "clear" allows PowerVR to keep results cached into super fast on chip memory and not write them out to the main memory. This saves a lot of power and memory bandwidth.

The descriptions of the required subpasses, created using a VkSubpassDescription struct, contain references to the required attachments and also the type of pipeline which will be performing the rendering operations for the subpass.

To create the render pass object itself, a VkRenderPassCreateInfo struct is used. This references all of the attachment and subpass descriptions that will be contained within the render pass.

It also identifies any dependencies between subpasses. Subpass dependencies describe the relationship between pairs of subpasses which operate on the same attachment. This ensures the subpasses read from and write to the attachment in the correct order. Explicitly defining these dependencies during render pass creation helps the application determine when the optimum time is to flush/clear/store memory. This is a clear advantage of Vulkan's explicitness compared to other APIs.

Example: initRenderPass()

In the example code, the render pass that is being created is very simple. It only has one colour attachment (swapchain image) and one subpass. The contents of the colour attachment are cleared at the beginning of the subpass and stored. Since there is only one subpass, there can be no dependencies between sub passes.