Day 5 (Dec 17, 2020) ~~~ Presenting Images to a Surface Using Swap Chains - kwilson33/learning-vulkan GitHub Wiki
Progress Today
I made it through these pages today of the official Vulkan tutorial. I started the Presentation part of Drawing a triangle after a 2 day hiatus.
- Drawing a triangle --> Presentation --> Window surface MEDIUM LENGTH
- Drawing a triangle --> Presentation --> Swap chain VERY LONG
- Drawing a triangle --> Presentation --> Image views VERY SHORT
Here are some major takeaways
1. Vulkan can't interface directly with the window system on its own.
- Remember, the GLFW library is used to create a window to begin with, since Vulkan doesn't include tools for the creation of a window either.
- To make a connection between the two, you need to use the WSI (Window System Integration) extensions.
- The first WSI extension is
VK_KHR_surface
. It exposes aVkSurfaceKHR
object that represents an abstract type of surface to present rendered images to. - GLFW has a function (
glfwCreateWindowSurface
) to take care of the filling in of the VkSurfaceKHR object. The creation is platform specific (as in Windows vs Mac vs Linux).
2. The window surface can influence physical device creation (as in what queue families the device needs).
- It's possible that the queue family supporting drawing commands (called
graphicsFamily
in the tutorial code) doesn't overlap with queue family supporting presentation. If there are two separate queue families, this may change the possibilities for physical devices. - So, the window surface must be created right after instance creation and before physical device selection.
3. Swap chains are essentially queues of images that are waiting to be presented to the screen.
- Vulkan doesn't have the concept of a "default framebuffer", so it requires an infrastructure that will contain the buffers that will we will render to before we present them to the screen. This infrastructure in Vulkan is a swap chain.
- The general purpose of a swap chain is to synchronize the presentation of images with the refresh rate of the screen.
- Render images --> swap chain (queue of images) --> present images to screen.
- Must check if physical device (ie. a GPU) is capable of presenting images directly to the screen by checking if the physical device allows the
VK_KHR_SWAPCHAIN_EXTENSION_NAME
extension (macro is defined asVK_KHR_swapchain
) - Furthermore, just checking if a swap chain is available on a physical device is NOT enough. You must also check if the swap chain is compatible with the window surface.
- Need to check 3 kind of properties for a swap chain
- Basic surface capabilities (min/max # of images in swap chain, min/max width & height of images)
- Surface formats (pixel format, color space).
- Available presentation modes.
- Need to check 3 kind of properties for a swap chain
4. Even if a swap chain is adequate, there are varying levels of adequate-ness for the best possible swap chain.
- The 3 types of settings to consider are:
- Surface format (color depth)
- A surface format (
VkSurfaceFormatKHR
) is composed offormat
andcolorSpace
members. We would like theformat
to beVK_FORMAT_B8G8R8A8_SRGB
, meaning 8 bits in that order for 32 bits per pixel, and is in the SRGB format. We would like thecolorSpace
to support the SRGB color space.
- A surface format (
- Presentation mode (conditions for "swapping" images to the screen)
- Arguably the most important setting, as it represents actual conditions for putting images on the screen. Click here to see the 4 possible modes and their pros/cons.
- Swap extent (resolution of images in swap chain)
- This is the resolution of the images and it's almost always = to the resolution of the window that we're drawing to in pixels. GLFW uses 2 units when measuring sizes, pixels and screen coords. Vulkan works with pixels, so the swap chain extend must be specified in pixels as well. For high DPI displays like Apple Retina's display, the screen coords don't correspond to pixels (the resolution of the window in pixels will be > resolution in screen coords). So, we have to use
glfwGetFramebufferSize
to query the resolution of the window in pixel before matching it against the min and max image extent. NOTE! You only have to do this if not using the default width and height. If either the default width or height (capabilities.currentExtent.width
inchooseSwapExtent()
) are set toUINT32_MAX
, then that means to do some special logic to figure out the swap extent like explained above.
- This is the resolution of the images and it's almost always = to the resolution of the window that we're drawing to in pixels. GLFW uses 2 units when measuring sizes, pixels and screen coords. Vulkan works with pixels, so the swap chain extend must be specified in pixels as well. For high DPI displays like Apple Retina's display, the screen coords don't correspond to pixels (the resolution of the window in pixels will be > resolution in screen coords). So, we have to use
- Surface format (color depth)
5. There are two ways to handle images that are accessed from multiple queues for a swap chain.
- For example, in this tutorial there may be one queue family that draws on the images in the swap chain (
graphics queue
) and then submitting them on a separatepresentation queue
. - The 2 modes are
VK_SHARING_MODE_EXCLUSIVE
andVK_SHARING_MODE_CONCURRENT
VK_SHARING_MODE_EXCLUSIVE
offers the best performance and requires explicit transfers of ownership of an image.VK_SHARING_MODE_CONCURRENT
allows using images across multiple queue families w/o explicit ownership transfers. Not as fast, but easier to use with multiple queue families thanVK_SHARING_MODE_EXCLUSIVE
.
6. If a swap chain becomes un-optimized while the application is still running, need to recreate it from scratch by providing reference to an old one.
- For example, this can happen if the window is resized and the swap chain doesn't know how to handle it. For this chapter, ignoring this feature, but it is important according to the tutorial.
VkImage
, including those in the swap chain, you must create a VkImageView
object.
7. To use any - An image view basically is a view into an image, describing how to access the image and which part of the image to access.
- For example, if it should be treated as a 2D depth texture w/o any mipmapping levels.
- Unlike images (
VkImage
) the image views (VkImageView
) are explicitly created by the programmer and they must be explicitly destroyed.
See ya!
WOW! Got a ton done today. I was frustrated I wasn't able to keep up with the momentum (I missed two days), but I definitely made up for that today. Also, by keeping these notes it for sure makes me remember the content way better. I'm 5 days in and tons of lines of code in, and there is still a ways to go before I can draw a triangle. Things just keep piling on and on, but the content from this section (Drawing a triangle --> Presentation) made way more sense to me than the hardest content in the previous section (Drawing a triangle --> Setup) which was validation layers
. The swap chain stuff took awhile to setup, but the things required to specify for it had to do with concepts I could visualize (image format, color space, screen width/height, how to draw an image, etc.). I'm having a lot of fun with this work and I hope it stays enjoyable. I'm at the tip of the iceberg, but so far this idea (keeping track of my progress) has been a great idea.