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)

Checking available Instance extensions

Vulkan Instance gathers per application state and allows us to create a logical device on which almost all operations are performed. Before we can create an Instance object, we should think about the instance-level extensions we want to enable. An example of one of the most important instance-level extensions are swapchain related extensions, which are used to display images on screen.

Extensions in Vulkan, as opposed to OpenGL, are enabled explicitly. We can't create a Vulkan Instance and request extensions that are not supported, because the Instance creation operation will fail. That's why we need to check which extensions are supported on a given hardware platform.

How to do it...

  1. Prepare a variable of type uint32_t named extensions_count.
  2. Call vkEnumerateInstanceExtensionProperties( nullptr, &extensions_count, nullptr ). All parameters should be set to nullptr, except for the second parameter, which should point to the extensions_count variable.
  3. If a function call is successful, the total number of available instance-level extensions will be stored in the extensions_count variable.
  4. Prepare a storage for the list of extension properties. It must contain elements of type VkExtensionProperties. The best solution is to use a std::vector container. Call it available_extensions.
  5. Resize the vector to be able to hold at least the extensions_count elements.
  1. Call vkEnumerateInstanceExtensionProperties( nullptr, &extensions_count, &available_extensions[0] ). The first parameter is once again set to nullptr; the second parameter should point to the extensions_count variable; the third parameter must point to an array of at least extensions_count elements of type VkExtensionProperties. Here, in the third parameter, provide an address of the first element of the available_extensions vector.
  2. If the function returns successfully, the available_extensions vector variable will contain a list of all extensions supported on a given hardware platform.

How it works...

Code that acquires instance-level extensions can be divided into two stages. First we get the total number of available extensions as follows:

uint32_t extensions_count = 0; 
VkResult result = VK_SUCCESS; 

result = vkEnumerateInstanceExtensionProperties( nullptr, &extensions_count, nullptr ); 
if( (result != VK_SUCCESS) || 
    (extensions_count == 0) ) { 
  std::cout << "Could not get the number of Instance extensions." << std::endl; 
  return false; 
}

When called with the last parameter set to nullptr, the vkEnumerateInstanceExtensionProperties() function stores the number of available extensions in the variable pointed to in the second parameter. This way, we know how many extensions are on a given platform and how much space we need to be able to store parameters for all of them.

When we are ready to acquire extensions' properties, we can call the same function once again. This time the last parameter should point to the prepared space (an array of VkExtensionProperties elements, or a vector, in our case) in which these properties will be stored:

available_extensions.resize( extensions_count ); 
result = vkEnumerateInstanceExtensionProperties( nullptr, &extensions_count, &available_extensions[0] ); 
if( (result != VK_SUCCESS) || 
    (extensions_count == 0) ) { 
  std::cout << "Could not enumerate Instance extensions." << std::endl; 
  return false; 
} 

return true;
The pattern of calling the same function twice is common in Vulkan. There are multiple functions, which store the number of elements returned in the query when their last argument is set to nullptr. When their last element points to an appropriate variable, they return the data itself.

Now that we have the list, we can look through it and check whether the extensions we would like to enable are available on a given platform.

See also

  • The following recipes in this chapter:
    • Checking available device extensions
  • The following recipe in Chapter 2, Image Presentation:
    • Creating a Vulkan Instance with WSI extensions enabled