Vulkan divides the representation of a device into two forms known as the logical and physical device:
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.
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(); };
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 |
|
This is the handle of the Vulkan instance. |
|
This specifies the number of physical devices. |
|
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:
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 |
|
This represents the physical device to which the extension properties will be queried. |
|
This is the name of the layer for which the extension needs to be queried. |
|
This refers to the number of extension properties exposed by the current |
|
This represents a retrieved array; it contains the extension's property objects that correspond to the |
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; }
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 |
|
This is the GPU handle whose memory properties need to be retrieved. |
|
This is the structure that will retrieve the GPU memory properties. |
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 |
|
This is the GPU handle whose memory properties need to be queried. |
|
This is for retrieving the memory properties. |
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.
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 |
|
This represents the physical device handle whose logical device is to be created. |
|
This is the |
|
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. |
|
This refers to the created logical device pointer that contains the newly created |
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.
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; }
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);
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.
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.
3.144.93.141