Preparing for loading Vulkan API functions
When we want to use Vulkan API in our application, we need to acquire procedures specified in the Vulkan documentation. In order to do that, we can add a dependency to the Vulkan Loader library, statically link with it in our project, and use function prototypes defined in the vulkan.h
header file. The second approach is to disable the function prototypes defined in the vulkan.h
header file and load function pointers dynamically in our application.
The first approach is little bit easier, but it uses functions defined directly in the Vulkan Loader library. When we perform operations on a given device, Vulkan Loader needs to redirect function calls to the proper implementation based on the handle of the device we provide as an argument. This redirection takes some time, and thus impacts performance.
The second option requires more work on the application side, but allows us to skip the preceding redirection (jump) and save some performance. It is performed by loading functions directly from the device we want to use. This way, we can also choose only the subset of Vulkan functions if we don't need them all.
In this book, the second approach is presented, as this gives developers more control over the things that are going in their applications. To dynamically load functions from a Vulkan Loader library, it is convenient to wrap the names of all Vulkan API functions into a set of simple macros and divide declarations, definitions and function loading into multiple files.
How to do it...
- Define the
VK_NO_PROTOTYPES
preprocessor definition in the project: do this in the project properties (when using development environments such as Microsoft Visual Studio or Qt Creator), or by using the#define VK_NO_PROTOTYPES
preprocessor directive just before thevulkan.h
file is included in the source code of our application. - Create a new file, named
ListOfVulkanFunctions.inl
. - Type the following contents into the file:
#ifndef EXPORTED_VULKAN_FUNCTION #define EXPORTED_VULKAN_FUNCTION( function ) #endif #undef EXPORTED_VULKAN_FUNCTION // #ifndef GLOBAL_LEVEL_VULKAN_FUNCTION #define GLOBAL_LEVEL_VULKAN_FUNCTION( function ) #endif #undef GLOBAL_LEVEL_VULKAN_FUNCTION // #ifndef INSTANCE_LEVEL_VULKAN_FUNCTION #define INSTANCE_LEVEL_VULKAN_FUNCTION( function ) #endif #undef INSTANCE_LEVEL_VULKAN_FUNCTION // #ifndef INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION #define INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( function, extension ) #endif #undef INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION // #ifndef DEVICE_LEVEL_VULKAN_FUNCTION #define DEVICE_LEVEL_VULKAN_FUNCTION( function ) #endif #undef DEVICE_LEVEL_VULKAN_FUNCTION // #ifndef DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION #define DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( function, extension ) #endif #undef DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION
- Create a new header file, named
VulkanFunctions.h
. - Insert the following contents into the file:
#include "vulkan.h" namespace VulkanCookbook { #define EXPORTED_VULKAN_FUNCTION( name ) extern PFN_##name name; #define GLOBAL_LEVEL_VULKAN_FUNCTION( name ) extern PFN_##name name; #define INSTANCE_LEVEL_VULKAN_FUNCTION( name ) extern PFN_##name name; #define INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( name, extension ) extern PFN_##name name; #define DEVICE_LEVEL_VULKAN_FUNCTION( name ) extern PFN_##name name; #define DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( name, extension ) extern PFN_##name name; #include "ListOfVulkanFunctions.inl" } // namespace VulkanCookbook
- Create a new file with a source code named
VulkanFunctions.cpp
. - Insert the following contents into the file:
#include "VulkanFunctions.h" namespace VulkanCookbook { #define EXPORTED_VULKAN_FUNCTION( name ) PFN_##name name; #define GLOBAL_LEVEL_VULKAN_FUNCTION( name ) PFN_##name name; #define INSTANCE_LEVEL_VULKAN_FUNCTION( name ) PFN_##name name; #define INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( name, extension ) PFN_##name name; #define DEVICE_LEVEL_VULKAN_FUNCTION( name ) PFN_##name name; #define DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( name, extension ) PFN_##name name; #include "ListOfVulkanFunctions.inl" } // namespace VulkanCookbook
How it works...
The preceding set of files may seem unnecessary, or even overwhelming, at first. VulkanFunctions.h
and VulkanFunctions.cpp
files are used to declare and define variables in which we will store pointers to Vulkan API functions. Declarations and definitions are done through a convenient macro definition and an inclusion of a ListOfVulkanFunctions.inl
file. We will update this file and add the names of many Vulkan functions, from various levels. This way, we don't need to repeat the names of functions multiple times, in multiple places, which helps us avoid making mistakes and typos. We can just write the required names of Vulkan functions only once, in the ListOfVulkanFunctions.inl
file, and include it when it's needed.
How do we know the types of variables for storing pointers to Vulkan API functions? It's quite simple. The type of each function's prototype is derived directly from the function's name. When a function is named <name>
, its type is PFN_<name>
. For example, a function that creates an image is called vkCreateImage()
, so the type of this function is PFN_vkCreateImage
. That's why macros defined in the presented set of files have just one parameter for function name, from which the type can be easily derived.
Last, but not least, remember that declarations and definitions of variables, in which we will store addresses of the Vulkan functions, should be placed inside a namespace, a class, or a structure. This is because, if they are made global, this could lead to problems on some operating systems. It's better to remember about namespaces and increase the portability of our code.
Note
Place declarations and definitions of variables containing Vulkan API function pointers inside a structure, class, or namespace.
Now that we are prepared, we can start loading Vulkan functions.
See also
The following recipes in this chapter:
- Loading function exported from a Vulkan Loader library
- Loading global-level functions
- Loading instance-level functions
- Loading device-level functions