Book Image

Vulkan Cookbook

By : Pawel Lapinski
Book Image

Vulkan Cookbook

By: Pawel Lapinski

Overview of this book

Vulkan is the next generation graphics API released by the Khronos group. It is expected to be the successor to OpenGL and OpenGL ES, which it shares some similarities with such as its cross-platform capabilities, programmed pipeline stages, or nomenclature. Vulkan is a low-level API that gives developers much more control over the hardware, but also adds new responsibilities such as explicit memory and resources management. With it, though, Vulkan is expected to be much faster. This book is your guide to understanding Vulkan through a series of recipes. We start off by teaching you how to create instances in Vulkan and choose the device on which operations will be performed. You will then explore more complex topics such as command buffers, resources and memory management, pipelines, GLSL shaders, render passes, and more. Gradually, the book moves on to teach you advanced rendering techniques, how to draw 3D scenes, and how to improve the performance of your applications. By the end of the book, you will be familiar with the latest advanced techniques implemented with the Vulkan API, which can be used on a wide range of platforms.
Table of Contents (13 chapters)

Enumerating available physical devices

Almost all the work in Vulkan is performed on logical devices: we create resources on them, manage their memory, record command buffers created from them, and submit commands for processing to their queues. In our application, logical devices represent physical devices for which a set of features and extensions were enabled. To create a logical device, we need to select one of the physical devices available on a given hardware platform. How do we know how many and what physical devices are available on a given computer? We need to enumerate them.

How to do it...

  1. Take the handle of a created Vulkan Instance. Provide it through a variable of type VkInstance named instance.
  2. Prepare a variable of type uint32_t named devices_count.
  3. Call vkEnumeratePhysicalDevices( instance, &devices_count, nullptr ). In the first parameter, provide a handle of the Vulkan Instance; in second, provide a pointer to the devices_count variable, and leave the third parameter set to nullptr right now.
  1. If a function call is successful, the devices_count variable will contain the total number of available physical devices.
  2. Prepare storage for the list of physical devices. The best solution is to use a variable of type std::vector with elements of type VkPhysicalDevice. Call it available_devices.
  3. Resize the vector to be able to hold at least the devices_count elements.
  4. Call vkEnumeratePhysicalDevices( instance, &devices_count, &available_devices[0] ). Again, the first parameter should be set to the handle of a Vulkan Instance object, the second parameter should still point to the extensions_count variable, and the third parameter must point to an array of at least devices_count elements of type VkPhysicalDevice. Here, in the third parameter, provide an address of the first element of an available_devices vector.
  5. If the function returns successfully, the available_devices vector will contain a list of all physical devices installed on a given hardware platform that supports a Vulkan API.

How it works...

Enumerating the available physical devices operation is divided into two stages: First, we check how many physical devices are available on any given hardware. This is done by calling the vkEnumeratePhysicalDevices() function with the last parameter set to nullptr, as follows:

uint32_t devices_count = 0; 
VkResult result = VK_SUCCESS; 

result = vkEnumeratePhysicalDevices( instance, &devices_count, nullptr ); 
if( (result != VK_SUCCESS) || 
    (devices_count == 0) ) { 
  std::cout << "Could not get the number of available physical devices." << std::endl; 
  return false; 
}

This way, we know how many devices are supporting Vulkan and how much storage we need to prepare for their handles. When we are ready and have prepared enough space, we can go to the second stage and get the actual handles of physical devices. This is done with the call of the same vkEnumeratePhysicalDevices() function, but this time, the last parameter must point to an array of VkPhysicalDevice elements:

available_devices.resize( devices_count ); 
result = vkEnumeratePhysicalDevices( instance, &devices_count, &available_devices[0] ); 
if( (result != VK_SUCCESS) || 
    (devices_count == 0) ) { 
  std::cout << "Could not enumerate physical devices." << std::endl; 
  return false; 
} 

return true;

When the call is successful, the prepared storage is filled with the handles of physical devices installed on any computer on which our application is executed.

Now that we have the list of devices, we can look through it and check the properties of each device, check operations we can perform on it, and see what extensions are supported by it.

See also

The following recipes in this chapter:

  • Loading instance-level functions
  • Checking available device extensions
  • Checking available queue families and their properties
  • Creating a logical device