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:
- physicalDeviceis the handle to the physical device whose properties will be queried.
- pQueueFamilyPropertyCountis a pointer to an integer related to the number of queue families available or queried.
- pQueueFamilyPropertiesis either- NULLor a pointer to an array of- VkQueueFamilyPropertiesstructures.
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:
- physicalDeviceis the physical device.
- queueFamilyIndexis the queue family.
- surfaceis the surface.
- pSupportedis a pointer to a- VkBool32, which is set to- VK_TRUEto indicate support, and- VK_FALSEotherwise.
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:
- physicalDeviceis the physical device that will be associated with the swapchain to be created.
- surfaceis the surface that will be associated with the swapchain.
- pSurfaceFormatCountis a pointer to an integer related to the number of format pairs available or queried.
- pSurfaceFormatsis either- NULLor a pointer to an array of- VkSurfaceFormatKHRstructures.
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;