Chapter 5: swapchain

Warning

This chapter is being written currently

In the previous chapter, we learned how to do GPU-based rendering, although an important limitation remained: we were not able to do real-time rendering, where the renderer runs continuously and the results are streamed to the screen. Offline renderers have all sorts of cool applications, but as we will see, bridging the gap to real-time rendering is not too much of a hassle: the hard parts of the Vulkan API are behind us!

Nonetheless, we should not underestimate the task at hand either. Naively, we may think that rendering could performed through a simple while loop such as the one below:

while !should_exit() { vulkan.send_draw_call(); let image = vulkan.extract_draw_call_result(); system.send_to_screen(image); }

The performance of such a process would be quite poor for two main reasons:

  1. Unexploited opportunities for parallelism: the process described above is entirely sequential. We could be working on the render of two different images in parallel (e.g., while the image corresponding to t=0s is in the later stage of the rendering pipeline, we could start working on the image for t=0.01667s).
  2. Costly CPU/GPU synchronization: extracting the results to the CPU and printing them to the screen takes time. The system could setup a bridge that lets the GPU communicate directly with the portion of memory on the screen-side to which the display is mapped. Then, our while loop could be reduced to a single function call (vulkan.draw_next_frame()), which would handle everything while avoiding back-and-forths with the CPU.

The swapchain answers both of these concerns.

In this chapter, we learn how to do pipelined rendering by juggling around multiple copies of rendering resources, and we see how to rely on low-level extensions that implement this mechanism in a way that is optimized for your operating system.

A. A high-level overview

A.1. Windows and surfaces

We want to render to windows, but we have not yet seen how to handle window objects. Of course, windows are a system-side feature. They are not managed by Vulkan, and they need to be created from the CPU. Different systems handle windows in different ways, we use cross-platform libraries that let us manipulate windows in an abstracted way. One such library is GLFW, which supports Windows, macOS, Wayland and X11. In addition to abstracting away the implementation details of window handling, they also provide a cross-platform way of handling inputs. Alternatives include SFML and SDL, but GLFW is the most lightweight option. GLFW was originally designed for use with OpenGL. We load it in a specific manner that enables interacting with Vulkan: we need to make sure that it can find the Vulkan headers and we have to load some specific extensions required to interact with the operating system.

With GLFW in place, we can create a window. From this window, we can extract a surface, i.e., a view of this window for use as a rendering target by Vulkan. The system impose limitations on what kind of images can be rendered to the window (e.g., you can't display colored images on a black-and-white screen). Surfaces are not a native feature of Vulkan: they are defined in an extension that we need to enable (in fact, GLFW requires that we load this extension when creating the instance).

A.2. Swapchains and presentation

The swapchain is Vulkan's abstraction for the subsystem that handles rendering to a surface. This is not a core part of Vulkan: like for introducing surfaces, we need to enable a specific extension for this to work. This extension introduces new constructs and present operations, as well as a queue capacity that we need to check for before running present operations on a queue.

To configure a swapchain, we need to specify three things: the image's resolution, its format, and a presentation mode. All of these may be constrained by the surface-specific limitations. The presentation mode dictates how the contents of the window is updated: does the application write directly to the image buffer (which may lead to tearing), should a form of buffering be used, etc.

A.3. Juggling resources

rendering loop vkGetSwapchainImagesKHR Framebuffers Image views, like before

A.4. Handling resizes

Resizes

B. The swapchain in more detail

VkSurfaceCapabilitiesKHR

GLFW was originally designed for use with OpenGL. We load it in a specific manner that enables interacting with Vulkan: we need to make sure that it can find the Vulkan headers and we have to load some specific extensions.

: we need to include the GLFW headers in a context where the Vulkan headers are already included (or define GLFW_INCLUDE_VULKAN, which makes GLFW responsible for including them). Furthermore, Depending on whether the

glfwCreateWindow