加载实例级函数
译者注:示例代码点击此处
我们创建了一个Vulkan Instance对象。 下一步是枚举物理设备,选择其中一个,然后从中创建逻辑设备。 这些操作是使用实例级函数执行的,我们需要从中获取地址。
怎么做...
1.获取之前创建的Vulkan Instance的句柄,是名为instance的VkInstance类型变量。
2.选择要添加的实例级函数的名称(表示为<function name>)。
3.创建名为<function name>的PFN_<function name>类型的变量。
4.调用vkGetInstanceProcAddr(instance,"<functionname>")。在第一个参数中为Instance提供句柄,在第二个参数中提供函数名。将此操作的结果转为PFN_<function name>类型并赋值给<function name>变量。
5.通过检查<function name>变量的值判断是否不等于nullptr来确认此操作是否成功。
这个怎么运作...
实例级函数主要用于操作物理设备,有多个实例级函数,其中包括vkEnumeratePhysicalDevices()、vkGetPhysicalDeviceProperties()、vkGetPhysicalDeviceFeatures()、vkGetPhysicalDeviceQueueFamilyProperties()、vkCreateDevice()、vkGetDeviceProcAddr()、vkDestroyInstance()和vkEnumerateDeviceExtensionProperties()这里并没有列出所有实例级函数。
我们如何判断函数是实例级还是设备级?所有设备级函数都有第一个类型为VkDevice,VkQueue或VkCommandBuffer的参数。因此,如果函数没有这样的参数且不是来自全局级别,则它来自实例级别。如前所述,实力级别函数用于操作物理设备,检查其属性、功能以及创建逻辑设备。
请记住,扩展还可以引入新功能。您需要将他们的函数添加到加载代码中,以便能够在应用程序中使用扩展。但是,如果创建实例期间不先启用扩展,则不应该加载给定的扩展引入的函数。如果给定平台不支持这些函数,则加载他们将失败(它将返回空指针)
因此,为了加载实例级别的函数,我们应该更新ListOfVulkanFunctions.inl文件:
//译者注:这些是Vulkan实例级别的核心函数
#ifndef INSTANCE_LEVEL_VULKAN_FUNCTION
#define INSTANCE_LEVEL_VULKAN_FUNCTION( function )
#endif
INSTANCE_LEVEL_VULKAN_FUNCTION( vkEnumeratePhysicalDevices )
INSTANCE_LEVEL_VULKAN_FUNCTION( vkGetPhysicalDeviceProperties )
INSTANCE_LEVEL_VULKAN_FUNCTION( vkGetPhysicalDeviceFeatures )
INSTANCE_LEVEL_VULKAN_FUNCTION( vkCreateDevice )
INSTANCE_LEVEL_VULKAN_FUNCTION( vkGetDeviceProcAddr )
//...
#undef INSTANCE_LEVEL_VULKAN_FUNCTION
//译者注:这些是Vulkan实例级别的扩展函数
#ifndef INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION
#define INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( function, extension )
#endif
INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( vkGetPhysicalDeviceSurfaceSupportKHR, VK_KHR_SURFACE_EXTENSION_NAME )
INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( vkGetPhysicalDeviceSurfaceCapabilitiesKHR, VK_KHR_SURFACE_EXTENSION_N
INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( vkGetPhysicalDeviceSurfaceFormatsKHR, VK_KHR_SURFACE_EXTENSION_NAME )
//译者注:这个判断是为了保证一些特定扩展函数是被我们选定的平台支持的,否则获取函数地址会为nullptr。
#ifdef VK_USE_PLATFORM_WIN32_KHR
INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( vkCreateWin32SurfaceKHR, VK_KHR_WIN32_SURFACE_EXTENSION_NAME )
#elif defined VK_USE_PLATFORM_XCB_KHR
INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( vkCreateXcbSurfaceKHR, VK_KHR_XLIB_SURFACE_EXTENSION_NAME )
#elif defined VK_USE_PLATFORM_XLIB_KHR
INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( vkCreateXlibSurfaceKHR, VK_KHR_XCB_SURFACE_EXTENSION_NAME )
#endif
#undef INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION
上面的代码中,我们添加了几个(单不是全部)实例级函数的名称。他们中每个都包装在INSTANCE_LEVEL_VULKAN_FUNCTION或INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION宏中,并放在#ifndef和#undef预处理器定义之间。
要使用上面的宏实现加载实例级的函数,我们应该编写以下代码:
#define INSTANCE_LEVEL_VULKAN_FUNCTION( name ) \
name = (PFN_##name)vkGetInstanceProcAddr( instance, #name ); \
if( name == nullptr ) { \
std::cout << "Could not load instance-level Vulkan function named: "\
#name << std::endl; \
return false; \
}
#include "ListOfVulkanFunctions.inl"
return true;
上面的宏调用vkGetInstanceProcAddr()函数。 它与用于加载全局级函数的函数相同,但这次第一个参数中提供了Vulkan实例的句柄。 这样我们就可以加载只有在创建Instance对象时才能正常工作的函数。
其第二个参数为要获取的函数名称的指针。将结果转换为对应的函数的类型。
提示:给定函数原型的类型是根据其名称前加PFN_定义的,。 因此,在该示例中,vkEnumeratePhysicalDevices()函数的原型的类型将被定义为PFN_vkEnumeratePhysicalDevices。
如果vkGetInstanceProcAddr()函数找不到所请求过程的地址,则返回nullptr。 这就是为什么我们应该检查并输出适当的消息,以防出现任何问题。
下一步是加载由扩展引入的函数。我们的函数加载代码是为了给ListOfVulkanFunctions.inl文件中用宏定义的函数获取函数指针。但是我们不能用INSTANCE_LEVEL_VULKAN_FUNCTION以相同的方式提供扩展函数,因为它们只能在启用适当的扩展时才能加载。当不启用任何扩展时,只能加载核心Vulkan API函数。这就是为什么需要区分核心API函数和扩展特定函数的原因。我们还需要知道哪些扩展被启用,哪些函数来自哪个扩展。所以需要用准备另一个宏来指定函数名,同时指定函数的扩展名。为了加载这些函数,我们可以使用以下代码:
#define INSTANCE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( name, extension ) \
for( auto & enabled_extension : enabled_extensions ) { \
if( std::string( enabled_extension ) == std::string( extension ) ) { \
name = (PFN_##name)vkGetInstanceProcAddr( instance, #name ); \
if( name == nullptr ) { \
std::cout << "Could not load instance-level Vulkan function named: " \
#name << std::end; \
return false; \
} \
} \
}
#include "ListOfVulkanFunctions.inl"
return true;
enabled_extension是std::vector<char const*>类型的变量,它包含所有已经启用的实例级扩展的名称。我们遍历其所有元素并检查给定扩展名是否与引入所提供函数的扩展名相匹配。如果是,我们以与普通核心API函数相同的方式加载函数,否则,我们跳过加载。如果我们不启用给定的扩展,就无法加载引入的函数。