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_vkEnumerateInstanceExtensionPropertiesnamedvkEnumerateInstanceExtensionProperties. - Create a variable of type
PFN_vkEnumerateInstanceLayerPropertiesnamedvkEnumerateInstanceLayerProperties. - Create a variable of type
PFN_vkCreateInstancenamedvkCreateInstance. - Call
vkGetInstanceProcAddr( nullptr, "vkEnumerateInstanceExtensionProperties" ), cast the result of this operation onto thePFN_vkEnumerateInstanceExtensionPropertiestype, and store it in avkEnumerateInstanceExtensionPropertiesvariable. - Call
vkGetInstanceProcAddr( nullptr, "vkEnumerateInstanceLayerProperties" ), cast the result of this operation onto thePFN_vkEnumerateInstanceLayerPropertiestype, and store it in avkEnumerateInstanceLayerPropertiesvariable. - Call
vkGetInstanceProcAddr( nullptr, "vkCreateInstance" ), cast the result of this operation onto aPFN_vkCreateInstancetype, and store it in thevkCreateInstancevariable. - 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