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)

Loading global-level functions

We have acquired a vkGetInstanceProcAddr() function, through which we can load all other Vulkan API entry points in an OS-independent way.

Vulkan functions can be divided into three levels, which are global, instance, and device. Device-level functions are used to perform typical operations such as drawing, shader-modules creation, image creation, or data copying. Instance-level functions allow us to create logical devices. To do all this, and to load device and instance-level functions, we need to create an Instance. This operation is performed with global-level functions, which we need to load first.

How to do it...

  1. Create a variable of type PFN_vkEnumerateInstanceExtensionProperties named vkEnumerateInstanceExtensionProperties.
  2. Create a variable of type PFN_vkEnumerateInstanceLayerProperties named vkEnumerateInstanceLayerProperties.
  3. Create a variable of type PFN_vkCreateInstance named vkCreateInstance.
  4. Call vkGetInstanceProcAddr( nullptr, "vkEnumerateInstanceExtensionProperties" ), cast the result of this operation onto the PFN_vkEnumerateInstanceExtensionProperties type, and store it in a vkEnumerateInstanceExtensionProperties variable.
  5. Call vkGetInstanceProcAddr( nullptr, "vkEnumerateInstanceLayerProperties" ), cast the result of this operation onto the PFN_vkEnumerateInstanceLayerProperties type, and store it in a vkEnumerateInstanceLayerProperties variable.
  6. Call vkGetInstanceProcAddr( nullptr, "vkCreateInstance" ), cast the result of this operation onto a PFN_vkCreateInstance type, and store it in the vkCreateInstance variable.
  7. Confirm that the operation succeeded by checking whether, values of all the preceding variables are not equal to nullptr.

How it works...

In Vulkan, there are only three global-level functions: vkEnumerateInstanceExtensionProperties(), vkEnumerateInstanceLayerProperties(), and vkCreateInstance(). They are used during Instance creation to check, what instance-level extensions and layers are available and to create the Instance itself.

The process of acquiring global-level functions is similar to the loading function exported from the Vulkan Loader. That's why the most convenient way is to add the names of global-level functions to the ListOfVulkanFunctions.inl file as follows:

#ifndef GLOBAL_LEVEL_VULKAN_FUNCTION 
#define GLOBAL_LEVEL_VULKAN_FUNCTION( function ) 
#endif 

GLOBAL_LEVEL_VULKAN_FUNCTION( vkEnumerateInstanceExtensionProperties ) 
GLOBAL_LEVEL_VULKAN_FUNCTION( vkEnumerateInstanceLayerProperties ) 
GLOBAL_LEVEL_VULKAN_FUNCTION( vkCreateInstance ) 

#undef GLOBAL_LEVEL_VULKAN_FUNCTION

We don't need to change the VulkanFunctions.h and VulkanFunctions.h files, but we still need to implement the preceding recipe and load global-level functions as follows:

#define GLOBAL_LEVEL_VULKAN_FUNCTION( name )                      \
name = (PFN_##name)vkGetInstanceProcAddr( nullptr, #name );       \
if( name == nullptr ) {                                           \
  std::cout << "Could not load global-level function named: "     \
    #name << std::endl;                                           \
  return false;                                                   \
} 

#include "ListOfVulkanFunctions.inl" 

return true;

A custom GLOBAL_LEVEL_VULKAN_FUNCTION macro takes the function name and provides it to a vkGetInstanceProcAddr() function. It tries to load the given function and, in the case of a failure, returns nullptr. Any result returned by the vkGetInstanceProcAddr() function is cast onto a PFN_<name> type and stored in a proper variable.

In the case of a failure, a message is displayed so the user knows which function couldn't be loaded.

See also

The following recipes in this chapter:

  • Preparing for loading Vulkan API functions
  • Loading function exported from a Vulkan Loader library
  • Loading instance-level functions
  • Loading device-level functions