Vulkan Cookbook 第一章 16 加载设备级函数

加载设备级函数

译者注:示例代码点击此处

我们已经创建了一个逻辑设备,可以在其上执行任何所需的操作,例如渲染3D场景,计算游戏中对象的碰撞或处理视频帧。 这些操作是使用设备级功能执行的,但在我们获取它们之前它们不可用。

怎么做...

1.获取已创建的逻辑设备对象的句柄,将其储存在名为logical_deviceVkDevice类型中。
2.选择要加载的设备函数的名称(表示为<function name>)。
3.调用vkGetDeviceProcAddr( device, "<function name>" ),第一个参数提供已创建逻辑设备的句柄,第二份参数为函数名。强转结果到PFN_<function name>类型并储存在<function name>变脸中。
4.通过检查<function name>变量是否等于nullptr来确认操作是否成功。

这个怎么运作...

几乎在3D渲染应用程序中完成的所有典型工作都是使用设备级函数执行的。 它们用于创建缓冲区,图像,采样器或着色器。 我们使用设备级函数来创建管道对象,同步原语,帧缓冲区和许多其他资源。 而且,最重要的是,它们用于记录稍后提交的操作(使用设备级函数)到队列,其中这些操作由硬件处理。 这一切都是通过设备级函数完成的。

像所有其他Vulkan函数一样,设备级函数可以使用vkGetInstanceProcAddr()函数加载,但这种方法不是最佳的。Vulkan被设计成一个灵活的API。它提供了在单个应用程序中对多个设备执行操作的选项,但是当我们调用vkGetInstanceProcAddr()函数时,我们不能提供与逻辑设备连接的任何参数。因此,这个函数返回的函数指针不能与我们要在其上执行给定操作的设备连接。在调用vkGetInstanceProcAddr()函数时返回一个分派函数,该分派函数基于它的参数调用一个函数的实现,在此我们可以设置给定逻辑设备。然而,这种跳跃具有性能成本:虽然它非常小,但是仍需要一些处理器时间来调用正确的功能。

如果我们想避免这种不必要的跳转并直接获取对应于给定设备的函数指针,我们应该通过使用vkGetDeviceProcAddr()来实现。这样我们就可以避免中间函数调用并提高应用程序的性能。这种方法也有一些缺点:我们需要为应用程序中创建的每个设备获取它的功能函数指针。如果我们想在许多不同的设备上执行操作,我们需要为每个逻辑设备单独创建函数指针列表。我们不能使用从一个设备获取的功能函数来再不同设备上执行操作,但是使用c++语言的预处理器,很容易获取给定设备的函数指针:

我们如何知道函数是来自设备级而不是来自全局级或实例级?该设备级函数的第一个参数是VkDevice,VkQueue或VkCommandBuffer类型。 从现在开始引入的大多数功能都来自设备级别。

要加载设备级函数,我们应该更新ListOfVulkanFunctions.inl文件,如下所示:

#ifndef DEVICE_LEVEL_VULKAN_FUNCTION
#define DEVICE_LEVEL_VULKAN_FUNCTION( function ) 
#endif

DEVICE_LEVEL_VULKAN_FUNCTION( vkGetDeviceQueue ) 
DEVICE_LEVEL_VULKAN_FUNCTION( vkDeviceWaitIdle ) 
DEVICE_LEVEL_VULKAN_FUNCTION( vkDestroyDevice )

DEVICE_LEVEL_VULKAN_FUNCTION( vkCreateBuffer ) 
DEVICE_LEVEL_VULKAN_FUNCTION( vkGetBufferMemoryRequirements ) 
// ...

#undef DEVICE_LEVEL_VULKAN_FUNCTION

//

#ifndef DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION
#define DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( function, extension ) #endif

DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( vkCreateSwapchainKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME ) 
DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( vkGetSwapchainImagesKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME )
DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( vkAcquireNextImageKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME ) 
DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( vkQueuePresentKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME ) 
DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( vkDestroySwapchainKHR, VK_KHR_SWAPCHAIN_EXTENSION_NAME )

#undef DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION

在相面的代码中,我们添加了多个设备级函数的名称,它们中每一个都包装在DEVICE_LEVEL_VULKAN_FUNCTION宏(如果它在核心API中定义)或DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION宏(如果它是由扩展引入)中,并且放在适当的#ifndef和#undef预处理器指令之间。 该列表当然是不完整的,因为有太多的功能可以在这里展示它们。

请记住,在没有首先在逻辑设备创建期间启用扩展的情况下,我们不应加载由给定扩展引入的函数。 如果不支持扩展,则其功能不可用加载它们的操作将失败。 这就是为什么与加载实例级函数类似,我们需要将函数加载代码分成两块。

首先,要使用前面的宏实现加载设备级核心API函数,我们应该编写以下代码:

#define DEVICE_LEVEL_VULKAN_FUNCTION( name )                                             \ 
name = (PFN_##name)vkGetDeviceProcAddr( device, #name );                                 \ 
if( name == nullptr ) {                                                                  \
  std::cout << "Could not load device-level Vulkan function named: " #name << std::endl; \
  return false;                                                                          \ 
}

#include "ListOfVulkanFunctions.inl" 

return true;

在此代码示例中,我们创建了一个宏,对于ListOfVulkanFunctions.inl文件中在DEVICE_LEVEL_VULKAN_FUNCTION()定义的地方每次出现,都会调用vkGetDeviceProcAddr()函数并提供我们要加载的过程的名称。 此操作的结果将转换为适当的类型,并存储在与所获取函数的名称完全相同的变量中。 失败后屏幕上会显示相关信息。

接下来,我们需要加载扩展引入的函数。 必须在创建逻辑设备期间启用这些扩展才可以使用它们:

#define DEVICE_LEVEL_VULKAN_FUNCTION_FROM_EXTENSION( name, extension )                         \ 
  for( auto & enabled_extension : enabled_extensions ) {                                       \
    if( std::string( enabled_extension ) == std::string( extension ) ) {                       \ 
      name = (PFN_##name)vkGetDeviceProcAddr( logical_device, #name );                         \ 
      if( name == nullptr ) {                                                                  \
        std::cout << "Could not load device-level Vulkan function named: " #name << std::endl; \ 
        return false;                                                                          \ 
    }                                                                                          \ 
  }                                                                                            \
}

#include "ListOfVulkanFunctions.inl" 

return true;

在前面的代码中,我们定义了迭代所有已启用扩展的宏。 它们在std::vector<char const *>类型的变量中定义,名为enabled_extensions。 在每次循环迭代中,将向量中的已启用扩展名称与为给定函数指定的扩展名称进行比较。 如果匹配,则加载函数指针。如果没有,则跳过给定的函数,因为我们无法从未启用的扩展中加载函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值