Queues are the means by which an application and a physical device communicate. The application provides the jobs in the form of a command buffer that is submitted to the queues. These are read by the physical device and processed asynchronously.
A physical device may support four types of queues, as shown in the following diagram. There could be multiple queues of the same type on a physical device; this allows the application to choose the number of queues and what type of queue it needs. For example, a simple application may require two queues: compute and graphics; here, the former is used for convolution computing and the second renders the computed blur image.
A physical device may consist of one or more queue families exposing what types of queue exist inside each queue family. Further, each queue family may have one or more queue count. The following diagram shows three queue families with their respective multiple queues:
A physical device is capable of exposing multiple queue families.
The number of queue family properties are exposed by the vkGetPhysicalDeviceQueueFamilyProperties()
API, as described here:
VkResult vkGetPhysicalDeviceQueueFamilyProperties ( VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties* pQueueFamilyProperties);
Here are the fields for this API:
Parameters |
Description |
|
This is the physical device handle whose queue properties are to be retrieved. |
|
This refers to the number of queue families exposed by the device. |
|
This field retrieves the queue family properties in an array of size equal to |
Queues are divided into families according to capabilities that are similar in nature. The following code snippet from the VulkanDevice
class shows how to query the queue families and their properties in the VkQueueFamilyProperties
control structure object, namely queueFamilyProps
.
In our implementation, the queue family properties are queried in a wrapper function called getPhysicalDeviceQueuesAndProperties()
, defined in the VulkanDevice
class. The following is the implementation:
void VulkanDevice::getPhysicalDeviceQueuesAndProperties(){ // Query queue families count by passing NULL as second parameter vkGetPhysicalDeviceQueueFamilyProperties(*gpu, &queueFamilyCount, NULL); // Allocate space to accomodate Queue properties queueFamilyProps.resize(queueFamilyCount); // Get queue family properties vkGetPhysicalDeviceQueueFamilyProperties (*gpu, &queueFamilyCount, queueFamilyProps.data()); }
The queueFlag
field of this structure contains the family information in the form of the following flag bits:
Flag bit |
Queue family meaning |
|
This is a graphics queue; it supports graphics-related operations. |
|
This is a compute queue; it offers computation capabilities. |
|
This is a transfer queue; it supports transfers. |
|
This is a sparse queue; it is capable of sparse memory management. |
Each queue family may support one or more queue types, which are indicated by the queueFlag
field of VkQueueFamilyProperties
. The queueCount
specifies the number of queues in the queue family. The third field timestampVaildBits
is used to time the command execution. The last parameter minImageTransferGranularity
specifies the minimum granularity image transfer operations in the present queue family support. Here is the syntax for this:
typedef struct VkQueueFamilyProperties { VkQueueFlags queueFlags; uint32_t queueCount; uint32_t timestampValidBits; VkExtent3D minImageTransferGranularity; } VkQueueFamilyProperties;
The following diagram shows how the queue and queue families are related in a physical device. In this particular illustration, a physical device comprises four types of queue family, where each of these contains different capabilities in terms of the queue type (queueFlags
) it supports and the number of queues (queueCount
) in each family.
The creation of a logical device object also needs a valid queue handle (in the form of an index) in order to create the associated queue with it. For this, iterate through all the queried queue family properties and check for the VkQueueFamilyProperties::queueFlags
bit information to find the appropriate queue. For example, we are interested in the graphics queue handle. The following code stores the handle of the graphics queue in graphicsQueueIndex
, which is used in the creation of the logical device (VkDevice
) object:
uint32_t VulkanDevice::getGrahicsQueueHandle(){ bool found = false; // 1. Iterate number of Queues supported by the Physical device for (unsigned int i = 0; i < queueFamilyCount; i++){ // 2. Get the Graphics Queue type if (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT){ // 3. Get the handle/index ID of graphics queue family. found = true; graphicsQueueIndex = i; break; } } return 0; }
Queues are created implicitly when a logical device object is created using the vkCreateDevice()
API. This API also intakes the queue information in the form of VkDeviceQueueCreateInfo
. The following is the syntax and associated field's description:
typedef struct VkDeviceQueueCreateInfo { VkStructureType type; const void* pNext; VkDeviceQueueCreateFlags flags; uint32_t queueFamilyIndex; uint32_t queueCount; const float* pQueuePriorities; } VkDeviceQueueCreateInfo;
The following table describes each field of this API:
Parameters |
Description |
|
This is the type information of this control structure. It must be specified as |
|
This field could be a valid pointer to an extension-specific structure or |
|
These are unused flags, which are reserved for future use. |
|
This queues family information specified in the form of a 32-bit |
|
This refers to the number of queue families to be created. |
|
This field represents an array of normalized floating-point values that specify the priority of the work submitted to each created queue. |
As we already know, queues are automatically created when a logical device object is created. The created queue can then be retrieved by the application using the vkGetDeviceQueue()
API. The following function (getDeviceQueue()
) from the VulkanDevice
class provides a high-level wrapper function to get the device's associated queue:
void VulkanDevice::getDeviceQueue(){ vkGetDeviceQueue(device, graphicsQueueWithPresentIndex, 0, &queue); }
Here is the syntax for this:
void vkGetDeviceQueue ( VkDevice logicalDevice, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue);
For more information about the API fields, refer to the following table:
Parameters |
Description |
|
This refers to the logical device ( |
|
This field indicates the index number of the family to which the queue ( |
|
There could be multiple queues within a queue family, where each queue is identified by a unique index. This field indicates the index of the queue within the queue family (indicated by |
|
This is the retrieved queue object returned by this API. |
The desired queue object that is intended to be queried from the logical device object will be deferred at this point. This is because we are interested in a queue that is capable of providing presentation capabilities, and for this, we need to wait until Chapter 6, Allocating Image Resources and Building a Swapchain with WSI. In this chapter, we will learn how to implement the swapchain for presentation purposes.
52.15.245.1