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...
- Create a variable of type
PFN_vkEnumerateInstanceExtensionProperties
namedvkEnumerateInstanceExtensionProperties
. - Create a variable of type
PFN_vkEnumerateInstanceLayerProperties
namedvkEnumerateInstanceLayerProperties
. - Create a variable of type
PFN_vkCreateInstance
namedvkCreateInstance
. - Call
vkGetInstanceProcAddr( nullptr, "vkEnumerateInstanceExtensionProperties" )
, cast the result of this operation onto thePFN_vkEnumerateInstanceExtensionProperties
type, and store it in avkEnumerateInstanceExtensionProperties
variable. - Call
vkGetInstanceProcAddr( nullptr, "vkEnumerateInstanceLayerProperties" )
, cast the result of this operation onto thePFN_vkEnumerateInstanceLayerProperties
type, and store it in avkEnumerateInstanceLayerProperties
variable. - Call
vkGetInstanceProcAddr( nullptr, "vkCreateInstance" )
, cast the result of this operation onto aPFN_vkCreateInstance
type, and store it in thevkCreateInstance
variable. - 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