Loading functions exported from a Vulkan Loader library
When we load (connect with) a Vulkan Loader library, we need to load its functions to be able to use the Vulkan API in our application. Unfortunately, different operating systems have different ways of acquiring the addresses of functions exported from dynamic libraries (.dll
files on Windows or .so
files on Linux). However, the Vulkan API strives to be portable across many operating systems. So, to allow developers to load all functions available in the API, no matter what operating system they are targeting, Vulkan introduced a function which can be used to load all other Vulkan API functions. However, this one single function can only be loaded in an OS specific way.
How to do it...
On the Windows operating system family:
- Create a variable of type
PFN_vkGetInstanceProcAddr
namedvkGetInstanceProcAddr
. - Call
GetProcAddress( vulkan_library, "vkGetInstanceProcAddr" )
, cast the result of this operation onto aPFN_vkGetInstanceProcAddr
type, and store it in thevkGetInstanceProcAddr
variable. - Confirm that this operation succeeded by checking if a value of the
vkGetInstanceProcAddr
variable does not equal tonullptr
.
On the Linux operating system family:
- Create a variable of type
PFN_vkGetInstanceProcAddr
namedvkGetInstanceProcAddr
. - Call
dlsym( vulkan_library, "vkGetInstanceProcAddr" )
, cast the result of this operation onto aPFN_vkGetInstanceProcAddr
type, and store it in thevkGetInstanceProcAddr
variable. - Confirm that this operation succeeded by checking if a value of the
vkGetInstanceProcAddr
variable does not equal tonullptr
.
How it works...
GetProcAddress()
is a function available on Windows operating systems. dlsym()
is a function available on Linux operating systems. They both acquire an address of a specified function from an already loaded dynamic-link library. The only function that must be publicly exported from all Vulkan implementations is called vkGetInstanceProcAddr()
. It allows us to load any other Vulkan function in a way that is independent of the operating system we are working on.
To ease and automate the process of loading multiple Vulkan functions, and to lower the probability of making mistakes, we should wrap the processes of declaring, defining, and loading functions into a set of convenient macro definitions, as described in the Preparing for loading Vulkan API functions recipe. This way, we can keep all Vulkan API functions in just one file which contains a list of macro-wrapped names of all Vulkan functions. We can then include this single file in multiple places and get use of the C/C++ preprocessor. By redefining macros, we can declare and define the variables in which we will store function pointers, and we can also load all of them.
Here is the updated fragment of the ListOfVulkanFunctions.inl
file:
#ifndef EXPORTED_VULKAN_FUNCTION #define EXPORTED_VULKAN_FUNCTION( function ) #endif EXPORTED_VULKAN_FUNCTION( vkGetInstanceProcAddr ) #undef EXPORTED_VULKAN_FUNCTION
The rest of the files (VulkanFunctions.h
and VulkanFunctions.h
) remain unchanged. Declarations and definitions are automatically performed with preprocessor macros. However, we still need to load functions exported from the Vulkan Loader library. The implementation of the preceding recipe may look as follows:
#if defined _WIN32 #define LoadFunction GetProcAddress #elif defined __linux #define LoadFunction dlsym #endif #define EXPORTED_VULKAN_FUNCTION( name ) \ name = (PFN_##name)LoadFunction( vulkan_library, #name ); \ if( name == nullptr ) { \ std::cout << "Could not load exported Vulkan function named: " \ #name << std::endl; \ return false; \ } #include "ListOfVulkanFunctions.inl" return true;
First we define a macro that is responsible for acquiring an address of a vkGetInstanceProcAddr()
function. It gets it from the library represented by the vulkan_library
variable, casts the result of this operation onto a PFN_kGetInstanceProcAddr
type, and stores it in a variable named vkGetInstanceProcAddr
. After that, the macro checks whether the operation succeeded, and displays the proper message on screen in the case of a failure.
All the preprocessor "magic" is done when the ListOfVulkanFunctions.inl
file is included and the preceding operations are performed for each function defined in this file. In this case, it is performed for only the vkGetInstanceProcAddr()
function, but the same behavior is achieved for functions from other levels.
Now, when we have a function loading function, we can acquire pointers to other Vulkan procedures in an OS-independent way.
See also
The following recipes in this chapter:
- Connecting with a Vulkan Loader library
- Preparing for loading Vulkan API functions
- Loading global-level functions
- Loading instance-level functions
- Loading device-level functions