Understanding queues and queue families

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.

Understanding queues and queue families

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:

Understanding queues and queue families

Querying queue families

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

physicalDevice

This is the physical device handle whose queue properties are to be retrieved.

pQueueFamilyPropertyCount

This refers to the number of queue families exposed by the device.

pQueueFamilyProperties

This field retrieves the queue family properties in an array of size equal to queueFamilyPropertyCount.

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

VK_QUEUE_GRAPHICS_BIT

This is a graphics queue; it supports graphics-related operations.

VK_QUEUE_COMPUTE_BIT

This is a compute queue; it offers computation capabilities.

VK_QUEUE_TRANSFER_BIT

This is a transfer queue; it supports transfers.

VK_QUEUE_SPARSE_BINDING_BIT

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.

Querying queue families

Storing the graphics queue handle

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; 
} 

Creating a queue

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

type

This is the type information of this control structure. It must be specified as VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO.

pNext

This field could be a valid pointer to an extension-specific structure or NULL.

flags

These are unused flags, which are reserved for future use.

queueFamilyIndex

This queues family information specified in the form of a 32-bit unsigned int queue index type. For example, in our case we supplied the graphicsQueueIndex variable, which contains the graphics queue index.

queueCount

This refers to the number of queue families to be created.

pQueuePriorities

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

logicalDevice

This refers to the logical device (VkDevice) object that owns the queue.

queueFamilyIndex

This field indicates the index number of the family to which the queue (pQueue) belongs.

queueIndex

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 queueFamilyIndex).

pQueue

This is the retrieved queue object returned by this API.

Note

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.245.1