Queues
In Vulkan, we have a concept called command buffers. We'll get to this later, but for now, all you need to know is they make use of queues for executing operations. Queues are specific in the operations they support. Because we'll be rendering a 3D scene, we'll be looking to use a queue that supports graphics operations. And, because we want to display the images, we'll need to verify we have presentation support.
Checking Graphics / Present Support
In this section, we're going to find a queue that supports both graphics operations and presenting images. But first, we'll need to get the number of queues to store properties in. We'll be using vkGetPhysicalDeviceQueueFamilyProperties
.
Definition for vkGetPhysicalDeviceQueueFamilyProperties
:
void vkGetPhysicalDeviceQueueFamilyProperties(
VkPhysicalDevice physicalDevice,
uint32_t* pQueueFamilyPropertyCount,
VkQueueFamilyProperties* pQueueFamilyProperties);
Documentation for vkGetPhysicalDeviceQueueFamilyProperties
:
physicalDevice
is the handle to the physical device whose properties will be queried.pQueueFamilyPropertyCount
is a pointer to an integer related to the number of queue families available or queried.pQueueFamilyProperties
is eitherNULL
or a pointer to an array ofVkQueueFamilyProperties
structures.
Usage for vkGetPhysicalDeviceQueueFamilyProperties
:
Per usual, we'll call it with NULL
as the last argument to get the number of queues before we allocate memory.
uint32_t queueCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, NULL);
assert(queueCount >= 1);
std::vector<VkQueueFamilyProperties> queueProperties(queueCount);
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount,
queueProperties.data());
Now let's add a variable in our VulkanExample
class called queueIndex
to store the index of the queue we want:
uint32_t queueIndex;
We'll make the default value UINT32_MAX
so we can later check if we found any suitable queue. Next, we need to look for a device queue that allows for drawing images and presenting images like I mentioned before. You can use two different queues, but we'll discuss that in a later chapter. We can use the fpGetPhysicalDeviceSurfaceSupportKHR
function pointer from earlier. The definition is the same as vkGetPhysicalDeviceSurfaceSupportKHR
.
Definition for vkGetPhysicalDeviceSurfaceSupportKHR
:
VkResult vkGetPhysicalDeviceSurfaceSupportKHR(
VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex,
VkSurfaceKHR surface,
VkBool32* pSupported);
Documentation for vkGetPhysicalDeviceSurfaceSupportKHR
:
physicalDevice
is the physical device.queueFamilyIndex
is the queue family.surface
is the surface.pSupported
is a pointer to aVkBool32
, which is set toVK_TRUE
to indicate support, andVK_FALSE
otherwise.
Usage for vkGetPhysicalDeviceSurfaceSupportKHR
:
std::vector<VkBool32> supportsPresenting(queueCount);
for (uint32_t i = 0; i < queueCount; i++) {
fpGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface,
&supportsPresenting[i]);
}
To check if our queue supports graphics operations, we can check if the queue flag contains VK_QUEUE_GRAPHICS_BIT
. You may or may not be aware that this can be done using the &
operator. We can use the following syntax:
if ((QUEUE_FLAG & VK_QUEUE_GRAPHICS_BIT) != 0)
// We support graphics operations!
Now, let's finish the body of the loop:
if ((queueProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) {
if (supportsPresenting[i] == VK_TRUE) {
queueIndex = i;
break;
}
}
If we didn't find anything, the value of queueIndex
will still be UINT32_MAX
. Let's handle that possibility before moving on:
assert(queueIndex != UINT32_MAX);
Color Formats and Color Spaces
For rendering purposes, we'll need some information on the surface formats our device supports. Specifically, we're going to check right now for color support. Vulkan breaks up this into two categories: color formats and color spaces. Color formats can describe the number of components, size of components, compression types, etc. In contrast, color spaces tells the Vulkan implementation how to interpret that data. For example, if we are telling Vulkan we have an RGBA image, it would need to understand what 0
and 255
mean. A color space describes the range of colors or gamut if you prefer.
A common and well supported color format in Vulkan is VK_FORMAT_B8G8R8A8_UNORM
. You can find documentation on available formats here. Note, that page is a stub and lacks full documentation right now. We can use fpGetPhysicalDeviceSurfaceFormatsKHR
which has the definition is the same as vkGetPhysicalDeviceSurfaceFormatsKHR
.
Definition for vkGetPhysicalDeviceSurfaceFormatsKHR
:
VkResult vkGetPhysicalDeviceSurfaceFormatsKHR(
VkPhysicalDevice physicalDevice,
VkSurfaceKHR surface,
uint32_t* pSurfaceFormatCount,
VkSurfaceFormatKHR* pSurfaceFormats);
Documentation for vkGetPhysicalDeviceSurfaceFormatsKHR
:
physicalDevice
is the physical device that will be associated with the swapchain to be created.surface
is the surface that will be associated with the swapchain.pSurfaceFormatCount
is a pointer to an integer related to the number of format pairs available or queried.pSurfaceFormats
is eitherNULL
or a pointer to an array ofVkSurfaceFormatKHR
structures.
Usage for vkGetPhysicalDeviceSurfaceFormatsKHR
:
uint32_t formatCount = 0;
result = fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface,
&formatCount, NULL);
assert(result == VK_SUCCESS);
assert(formatCount >= 1);
Now we can get the actual formats and verify success again:
std::vector<VkSurfaceFormatKHR> surfaceFormats(formatCount);
result = fpGetPhysicalDeviceSurfaceFormatsKHR(
physicalDevice, surface, &formatCount, surfaceFormats.data());
assert(result == VK_SUCCESS);
Now that we've found the formats our device supports, we can go ahead and set them. Let's add two variables to our VulkanExample
class:
VkFormat colorFormat;
VkColorSpaceKHR colorSpace;
Here is the process we should follow when choosing a color format and color space:
- Check if we support only one format and that format is
VK_FORMAT_UNDEFINED
- Yes: We should set our own format because the device does not default to any
- No: Take the first color format we support.
- Take the first color space that's available
The code would look like this:
if (formatCount == 1 && surfaceFormats[0].format == VK_FORMAT_UNDEFINED)
colorFormat = VK_FORMAT_B8G8R8A8_UNORM;
else
colorFormat = surfaceFormats[0].format;
colorSpace = surfaceFormats[0].colorSpace;