Creating the Framebuffers#
A Framebuffer wraps a collection of images – called attachments in this context –, so that they can be used for rendering. They are bound when beginning a render pass, and form the render target for that render pass.
Whereas a render pass describes the type of attachments accessed during rendering and what to do with them, it does not refer to any specific resources. This is where framebuffers come into the picture.
Framebuffers contain the attachments used by the render pass. These attachments, namely Colour, Depth, Input, Resolve, and Stencil, are image objects that are written into by a subpass during rendering. Color attachments are where the pixel values are stored into and normally what is displayed, depth attachments are used for depth-testing, and stencil attachments for stencil-testing. A framebuffer object will contain references to these objects via associated image views.
Note
Framebuffers and render passes will sometimes use the additional Input and Resolve attachments. Input attachments are images read from rather than written to, and are generally used in multi-subpass rendering processes such as deferred shading. Resolve attachments are used to implement multi-sampling.
Separating descriptions in render passes and definitions in framebuffers allows you to use the same render pass with different framebuffers, as long as these framebuffers are all compatible with the render pass.
In initFrameBuffers()
in the example code, a different framebuffer needs to be created for each image in the swapchain. As mention before, these swapchain images are the only render targets of the application. They are colour attachments, as we do not require depth or stencil testing in this example. Each framebuffer will therefore only have a single colour attachment, making it compatible with the render pass. View in context.
void VulkanHelloAPI::initFrameBuffers()
{
// This is a placeholder handle for the attachment which will be stored in the VkFramebufferCreateInfo.
VkImageView attachment = VK_NULL_HANDLE;
// Populate a framebuffer info struct with the information that is needed to create the framebuffers. This includes its dimensions, its attachments, and the associated render
// pass that will use the specified attachments. The attachment member will be a null variable for now.
VkFramebufferCreateInfo frameBufferInfo = {};
frameBufferInfo.flags = 0;
frameBufferInfo.pNext = nullptr;
frameBufferInfo.attachmentCount = 1;
frameBufferInfo.height = appManager.swapchainExtent.height;
frameBufferInfo.width = appManager.swapchainExtent.width;
frameBufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
frameBufferInfo.renderPass = appManager.renderPass;
frameBufferInfo.pAttachments = &attachment;
frameBufferInfo.layers = 1;
// Resize the vector which will contain all of the framebuffers based on the number of images in the swapchain.
appManager.frameBuffers.resize(appManager.swapChainImages.size());
// Create as many framebuffer objects as swapchain images and assign each image to a framebuffer.
// Note that, above, the pAttachments variable has been assigned to the address of the
// local variable "attachment". Every time pAttachments is reassigned a new image is used during framebuffer creation.
for (size_t i = 0; i < appManager.swapChainImages.size(); ++i)
{
attachment = appManager.swapChainImages[i].view;
debugAssertFunctionResult(vk::CreateFramebuffer(appManager.device, &frameBufferInfo, nullptr, &appManager.frameBuffers[i]), "Swapchain Frame buffer creation");
}
}