Understanding physical and logical devices

Vulkan divides the representation of a device into two forms known as the logical and physical device:

  • Physical device: A physical device represents a single workforce that may comprise a single GPU along with other hardware parts that work together to help the system accomplish the submitted jobs. On a very simple system, a physical device can be considered to represent the physical GPU unit.
  • Logical device: A logical device represents the application view of the actual device.

Physical devices

OpenGL does not expose physical devices; it connects them behind the curtains. Vulkan, on the other hand, exposes the system real computing device or GPU to the application. It allows the application to enumerate the physical devices available on the system.

Note

In this section, we will add a new user-defined class called VulkanDevice; this class is implemented in VulkanDevice.h/.cpp. It is responsible for managing the physical (VkPhysicalDevice) and logical device (VkDevice). In addition, it also manages the physical device's queue families.

The following is the declaration of the VulkanDevice class; as we proceed through this chapter, we will uncover most of the functions used in this class. Refer to the accompanying source code for the full implementation of this header file declaration:

class VulkanDevice{ 
 public: 
  VulkanDevice(VkPhysicalDevice* gpu); ~VulkanDevice(); 
 
  // Device related member variables  
  VkDevice               device;        // Logical device 
  VkPhysicalDevice*      gpu;           // Physical device 
  VkPhysicalDeviceProperties gpuProps;  // Physical device attributes 
  VkPhysicalDeviceMemoryProperties memoryProperties; 
 
  // Queue related properties

  // Vulkan Queues object 
  VkQueue      queue;       
                
  // Store all queue families exposed by the physical device. 
  vector<VkQueueFamilyProperties>queueFamilyProps; 
 
  // Stores graphics queue index 
  uint32_t     graphicsQueueFamilyIndex;  
   
  // Number of queue family exposed by device 
  uint32_t     queueFamilyCount;        
   
  // Device specific extensions 
  VulkanLayerAndExtension   layerExtension; 
 
  // This class exposes the below function to the outer world 
  createDevice(),             memoryTypeFromProperties() 
  destroyDevice(),            getGrahicsQueueHandle(),   
  initializeDeviceQueue(),  getPhysicalDeviceQueuesAndProperties(); 
}; 

Enumerating physical devices

In order to establish a connection with the available physical devices, an application has to enumerate them. Physical device enumeration is a process by which Vulkan exposes the number of actual devices, which are available on the system, to the application. A list of physical devices can be retrieved using vkEnumeratePhysicalDevices().

The following is the syntax of this API:

VkResult  (  
           VkInstance                   instance, 
           uint32_t                     pPhysicalDeviceCount, 
           VkPhysicalDevice*            pPhysicalDevice); 

The following are the fields associated with this API:

Parameters

Description

instance

This is the handle of the Vulkan instance.

pPhysicalDeviceCount

This specifies the number of physical devices.

pPhysicalDevice

This is the Vulkan physical device object.

This API is wrapped in the Application class's enumeratePhysicalDevices function. It returns the number of physical device objects on the available system:

  VkResult VulkanApplication::enumeratePhysicalDevices 
               (std::vector<VkPhysicalDevice>& gpuList){ 
     
   // Holds the gpu count  
   uint32_t gpuDeviceCount;           
     
    
    // Get the gpu count 
    vkEnumeratePhysicalDevices 
    (instanceObj.instance, &gpuDeviceCount, NULL);          
 
    // Make space for retrieval 
    gpuList.resize(gpuDeviceCount);  
    
    // Get Physical device object 
    return vkEnumeratePhysicalDevices 
    (instanceObj.instance, &gpuDeviceCount, gpuList.data()); 
 
  } 

The following diagram shows the enumerated physical devices on the system and is associated with the VkInstance object while querying:

Enumerating physical devices

Querying physical device extensions

A physical device exposes extensions similar to Vulkan instances. For each retrieved instance layer property (VkLayerProperties), there may exist extension properties for each physical device that can be queried using the vkEnumerateDeviceExtensionProperties() API.

The following is the syntax of this API:

VkResult vkEnumerateDeviceExtensionProperties ( 
                VkPhysicalDevice             physicalDevice, 
                const char*                  pLayerName, 
                uint32_t*                    pExtensionCount, 
                VkExtensionProperties*       pProperties); 

Here are the fields associated with this API:

Parameters

Description

physicalDevice

This represents the physical device to which the extension properties will be queried.

pLayerName

This is the name of the layer for which the extension needs to be queried.

pExtensionCount

This refers to the number of extension properties exposed by the current physicalDevice for a corresponding pLayerName.

pProperties

This represents a retrieved array; it contains the extension's property objects that correspond to the pLayerName.

The process of querying device-based extension properties is very similar to that of an instance-based one. The following is the implementation of this function:

VkResult VulkanLayerAndExtension::getDeviceExtensionProperties 
                                      (VkPhysicalDevice* gpu) 
{ 
   // Variable to check Vulkan API result status 
   VkResult result; 
 
   // Query all the extensions for each layer and store it. 
   std::cout << "Device extensions" << std::endl; 
   std::cout << "===================" << std::endl; 
   VulkanApplication* appObj = VulkanApplication::GetInstance(); 
   std::vector<LayerProperties>* instanceLayerProp =  
                            &appObj->GetInstance()->instanceObj. 
                            layerExtension.layerPropertyList; 
 
   for (auto globalLayerProp : *instanceLayerProp) { 
         LayerProperties layerProps; 
         layerProps.properties = globalLayerProp.properties; 
 
         if (result = getExtensionProperties(layerProps, gpu)) 
               continue; 
 
         layerPropertyList.push_back(layerProps); 
 
        // Many lines skipped.. 
   } 
   return result; 
} 

Getting the properties of a physical device

The properties of a physical device can be retrieved using the vkGetPhysicalDeviceProperties() API; the attributes are retrieved in the VkPhysicalDeviceProperties control structure. Here's the syntax of this process:

void vkGetPhysicalDeviceMemoryProperties ( 
      VkPhysicalDevice                       physicalDevice, 
      VkPhysicalDeviceMemoryProperties*      pMemoryProperties ); 

Here are the fields for vkGetPhysicalDeviceMemoryProperties:

Parameters

Description

physicalDevice

This is the GPU handle whose memory properties need to be retrieved.

pMemoryproperties

This is the structure that will retrieve the GPU memory properties.

Interrogating memory properties from the physical device

A single physical device may have different memory types, which are further differentiated based on their properties. It's important for an application to know the characteristics of the memory; this facilitates better allocation of the resources, depending upon the application logic or resource type. The following syntax is to retrieve the physical device memory properties:

void vkGetPhysicalDeviceMemoryProperties ( 
             VkPhysicalDevice                   physicalDevice, 
             VkPhysicalDeviceMemoryProperties*  pMemoryProperties ); 

Here are the fields described for vkGetPhysicalDeviceMemoryProperties:

Parameters

Description

physicalDevice

This is the GPU handle whose memory properties need to be queried.

pMemoryProperties

This is for retrieving the memory properties.

Logical device

A logical device is the representation of a physical device, but it is used in the application space; it provides a specialized view of the physical device. For example, a physical device may consist of three queues: graphics, compute, and transfers. However, a logical device can be created with a single queue (say, graphics) attached to it; this makes it very easy to submit the command buffers.

Creating a logical device

A logical device is represented using VkDevice and can be created using the vkCreateDevice API. Here's the syntax of this:

VkResult vkCreateDevice(  
             VkPhysicalDevice                pPhysicalDevice,  
             Const VkDeviceCreateInfo*       pCreateInfo,  
             const VkAllocationCallbacks*    pAllocator,  
             VkDevice*                       pDevice); 

For more information on API fields, refer to the following table:

Parameters

Description

pPhysicalDevice

This represents the physical device handle whose logical device is to be created.

pCreateInfo

This is the VkDeviceCreateInfo structure that contains specific information that will be utilized by the vkCreateDevice() API to control the creation of a logical device.

pAllocator

This specifies how to control host memory allocation. For more information, refer to the Host memory section in Chapter 5, Command Buffer and Memory Management in Vulkan.

pDevice

This refers to the created logical device pointer that contains the newly created VkDevice object.

This API uses the VkDeviceCreateInfo control structure object (deviceInfo), which contains the necessary information required to create a logical device object. For instance, it contains the names of the layers (this feature is deprecated and kept for backward-compatibility) and extensions that need to be enabled on the device. In addition, it also specifies which queue it should be creating and connect to. In our case, we are interested in drawing operations; therefore, we need a queue handle (graphicsQueueIndex) that will represent a queue that has the functionality of drawing capabilities. In other words, we need the graphics queue handle.

Note

The queue information is contained in the VkDeviceQueueCreateInfo structure object, queueInfo. When a logical device is created, it also creates the associated queues with it, using this structure. For more information on queues, how to find the graphics queue index, and the queue creation process, refer to the next section, Understanding queues and queue families.

VulkanDevice::createDevice is a user-defined wrapper method that helps to create the logical device object. Here is its implementation:

  VkResult VulkanDevice::createDevice(vector<const char *>& layers, 
                          vector<const char *>& extensions){ 
 
  VkResult result; 
  float queuePriorities[1]       = { 0.0 }; 
 
  // Create the object information       
  VkDeviceQueueCreateInfo queueInfo    = {}; 
  queueInfo.queueFamilyIndex           = graphicsQueueIndex;   
  queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 
  queueInfo.pNext                      = NULL; 
  queueInfo.queueCount                 = 1; 
  queueInfo.pQueuePriorities           = queuePriorities; 
 
  VkDeviceCreateInfo deviceInfo = {}; 
  deviceInfo.sType   = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 
  deviceInfo.pNext                     = NULL; 
  deviceInfo.queueCreateInfoCount      = 1; 
  deviceInfo.pQueueCreateInfos         = &queueInfo; 
  deviceInfo.enabledLayerCount         = 0; 
  // Device layers are deprecated 
  deviceInfo.ppEnabledLayerNames       = NULL; 
  deviceInfo.enabledExtensionCount     = extensions.size(); 
  deviceInfo.ppEnabledExtensionNames   = extensions.data(); 
  deviceInfo.pEnabledFeatures          = NULL; 
 
  result = vkCreateDevice(*gpu, &deviceInfo, NULL, &device); 
 
  assert(result == VK_SUCCESS); 
  return result; 
} 

Waiting on the host

A device is said to be active as long as it has jobs in the queues to process. Once the queues have no more command buffers to process, the device becomes idle. The following vkDeviceWaitIdle API waits on the host until all the queues for the logical device become idle. This API accepts the argument that takes the handle of the logical device object for which the idle status is to be checked. Here's the syntax for this:

VkResult vkDeviceWaitIdle( VkDevice device); 

Losing the device

While working with logical (VKDevice) and physical (VKPhysicalDevice) devices for a certain reason--such as hardware malfunction, device error, execution timeouts, power management events, and/or platform-specific events--the devices can be lost. This may result in failing to execute the pending commands buffer.

Tip

When a physical device is lost, the attempt to create a logical device object (VKDevice) will fail and it will return VK_ERROR_DEVICE_LOST. If the logical device object is lost, certain commands will return VK_ERROR_DEVICE_LOST upon its use. However, the corresponding physical device may remain unaffected. It is not possible to reset the lost state of the logical device, and this loss of state is local to the logical device object (VKDevice) and does not affect any other active logical device objects.

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

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