准备加载Vulkan API函数
译者注:示例代码点击此处
当我们想在我们的应用程序中使用Vulkan API时,我们需要获取Vulkan文档中指定的过程。为了做到这一点,我们可以向Vulkan Loader库添加一个依赖项,在我们的项目中静态链接它,并使用vulkan.h头文件中定义的函数原型。第二种方法是禁用vulkan.h头文件中定义的函数原型,并在我们的应用程序中动态加载函数指针。
第一种方法稍微容易一点,但它使用Vulkan Loader库中直接定义的函数。当我们在给定设备上执行操作时,Vulkan Loader需要根据我们提供的设备句柄将函数调用重定向到正确的实现。此重定向需要一些时间,因此会影响性能。
第二个选项需要在应用程序端进行更多工作,但允许我们跳过前面的重定向(跳转)并保留一些性能。它是通过直接从我们想要使用的设备加载功能来执行的。这样,如果我们不需要它们,我们也可以只选择Vulkan函数的子集。
在本书中,介绍了第二种方法,因为这使开发人员可以更好地控制应用程序中的内容。要从Vulkan Loader库动态加载函数,可以方便地将所有Vulkan API函数的名称包装到一组简单的宏中,并将声明定义和函数加载分成多个文件。
怎么做...
1.在项目中定义VK_NO_PROTOTYPES预处理器定义:在项目属性中执行此操作(使用Microsoft Visual Studio或Qt Creator等开发环境时),或者在包含vulkan.h文件之前使用#define VK_NO_PROTOTYPES预处理器指令 在我们的应用程序的源代码中。
2.创建一个名为ListOfVulkanFunctions.inl的新文件。
3.在文件中键入以下内容:
#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
4.创建一个新文件名为VulkanFunctions.h。
5.将以下内容插入到文件中:
#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
6.创建一个名为VulkanFunctions.cpp.源代码文件。
7.在文件中插入以下内容:
#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
这个怎么运作...
VulkanFunctions.h和VulkanFunctions.cpp的宏定义似乎是不必要的,也的确不是不必要的。这两个文件应该用于声明和定义变量,但为了减少拼写错误,函数名称会在ListOfVulkanFunctions.inl中使用宏定义添加,不需要在多个地方多次重复函数的名称,我们将在其中存储指向Vulkan API函数的指针。
我们如何知道用于存储指向Vulkan API函数的指针的变量类型? 这很简单。 每个函数原型的类型直接来自函数的名称。 当函数名为<name>时,其类型为PFN_<name>。 例如,创建图像的函数称为vkCreateImage(),因此该函数的类型为PFN_vkCreateImage。 这就是为什么在VulkanFunctions.h和VulkanFunctions.cpp中定义的宏只有一个函做为作为参数,这可以很容易的拼出类型。
最后也很重要的是,请记住,我们应该将存储Vulkan函数地址的变量的声明和定义放在命名空间,类或结构中。 这是因为,如果它们是全局的,这可能会导致某些操作系统出现问题。 最好记住命名空间并增加代码的可移植性。
提示:将包含Vulkan API函数指针的变量的声明和定义放在结构,类或命名空间中。
现在已经准备好了,我们可以开始加载Vulkan函数了。