Vulkan Loader接口的架构

本文翻译自Vulkan-Loader的LoaderInterfaceArchitecture.md

概要

Vulkan是一个分层的架构,由以下部分组成:

  • Vulkan应用程序(Vulkan Application)
  • Vulkan加载器(Vulkan Loader)
  • Vulkan层(Vulkan Layers)
  • 驱动(Drivers)
  • 配置(VkConfig)

它们之间的关系如下图所示:
Vulkan Loader概要

加载器 Loader

Vulkan应用程序(application)位于最上层,直接与加载器(loader)交互,而Vulkan驱动(driver)位于底层。驱动能够控制一个或多个能够使用Vulkan进行渲染的物理设备(通常理解为显卡GPU,不过CPU也是可以实现软渲染的),实现从Vulkan转换为本机图形API(例如MoltenVK)。

在应用程序与驱动之间,加载器可以注入任意数量的可选的层,以提供特殊功能。加载器对于将Vulkan函数正确分配到合适的一组层和驱动程序的管理至关重要。Vulkan对象模型允许加载器在调用链中插入层,这样一来这些层就能够在驱动被调用之前处理Vulkan函数了。

加载器的作用:

  • 在一个用户系统上,支持一个或多个Vulkan驱动同时存在而不相互影响;
  • 支持Vulkan层,这些层是一些可选的功能模块(由应用程序、开发人员或标准系统设置来使能);
  • 降低开销。

层 Layers

层(layers)是一些可选的模块,可以用来增强Vulkan的开发环境。层可以在Vulkan函数从应用程序传递分配到驱动这一路的中间对其进行拦截、评估、以及修改。层是以动态库/静态库的形式实现,在CreateInstance的过程中被加载。

每一层都可以选择挂钩(hook)或拦截(intercept)Vulkan函数,对应的Vulkan功能可以被忽略,检查或增强。只要是没有被层hook的函数就会被该层略过,控制流会继续向前执行到下一个支持的层或者驱动,这样一来,层就可以选择是拦截所有已知的Vulkan函数还是仅拦截它感兴趣的那一部分。

部分层的作用:

  • 验证API的使用/用法;
  • 跟踪API的调用;
  • 辅助调试;
  • 分析。

由于层是可选且动态加载的,开发人员可以随时对其使能或禁用。例如:当开发调试一个应用程序时,使能特定的层能够帮助确认该应用程序是否正确地使用了Vulkan API;而release应用程序时,这些层就没必要使能了,禁用掉可以加快应用程序的执行速度。

驱动 Drivers

驱动是一组实现了Vulkan功能的库,这组库能够直接支持物理硬件设备、将Vulkan命令转换成本地图形命令、以及通过软件模拟Vulkan。最常见的驱动类型是可安装客户端驱动(Installable Client Driver, ICD)。加载器负责发现系统上可用的Vulkan驱动。只要给定可用驱动程序的列表,加载器就可以枚举所有可用的物理设备,并将这些信息提供给应用程序。

Vulkan允许系统上有多个可安装客户端驱动(Installable Client Driver, ICD),每一个ICD都支持一个或多个硬件设备。每一个设备由Vulkan中的VkPhysicalDevice对象来表示。加载器能够通过搜索系统上的标准驱动来发现可用的Vulkan ICD。

配置 VkConfig

VkConfig是由LunarG开发的一套工具,用于辅助开发人员修改本地系统上的Vulkan开发环境。它可以发现层、使能层、修改层的设置等。只要安装Vulkan SDK或者编译LunarG VulkanTools Github Repo的源码就可以拥有VkConfig了~

VkConfig会产生3个输出,其中2个与Vulkan加载器和层相关,这3个输出分别是:

  • Vulkan Override Layer:是VkConfig的重要部分,当加载器找到这个层时,这个层会强制加载在VkConfig中启用的所需层,并禁用那些在设置中禁用的层(包括隐式层);
  • Vulkan层设置文件:可用于指定每个启用层期望执行的某些行为和动作。这些设置也可以由VkConfig控制,或者可以手动启用;
  • VkConfig配置。

这些文件在系统中的存放位置如下表所示:

系统平台输出存放位置
LinuxVulkan Override Layer$USER/.local/share/vulkan/implicit_layer.d/VkLayer_override.json
Vulkan层设置文件$USER/.local/share/vulkan/settings.d/vk_layer_settings.txt
VkConfig配置$USER/.local/share/vulkan/settings.d/vk_layer_settings.txt
WindowsVulkan Override Layer%HOME%\AppData\Local\LunarG\vkconfig\override\VkLayerOverride.json
Vulkan层设置文件(registry) HKEY_CURRENT_USER\Software\Khronos\Vulkan\Settings
VkConfig配置(registry) HKEY_CURRENT_USER\Software\LunarG\vkconfig

Vulkan的重要概念

Instance vs. Device

Vulkan中的对象(objects)、函数(functions)、扩展(extensions)和其它行为能够划分为两个部分:

  • 基于实例(Instance)的;
  • 基于设备(Device)的。

基于实例(Instance)的

“Vulkan instance”(VkInstance)是一个高级结构,用于提供Vulkan系统级信息和功能。

Instance对象

与Instance直接关联的Vulkan对象如下:

  • VkInstance
  • VkPhysicalDevice
  • VkPhysicalDeviceGroup
Instance函数

Instance函数是指以Instance对象作为第一个参数或者没有对象的任意Vulkan函数,部分Instance函数如下:

  • vkEnumerateInstanceExtensionProperties
  • vkEnumeratePhysicalDevices
  • vkCreateInstance
  • vkDestroyInstance

应用程序能够通过Vulkan加载器的头文件直接连接到所有核心Instance函数。应用程序也可以通过vkGetInstanceProcAddr来查询函数指针,该函数可以查询任意Instance或Device的入口点。

如果vkGetInstanceProcAddr函数是通过VkInstance调用的,那么该函数返回的任何函数指针都是基于这个VkInstance的。

Instance扩展

Vulkan有哪些扩展是基于Vulkan提供了哪些类型函数。扩展可以分为Instance扩展和Device扩展,Instance扩展里面大部分是Instance相关类型的函数,Device扩展里面大部分是Device相关类型的函数。

基于设备(Device)的

Vulkan设备(VkDevice)是一个逻辑标识符,这个逻辑标识符通过操作系统中特定的驱动与Vulkan物理设备(VkPhysicalDevice)关联。

Deivce对象

与Device直接关联的Vulkan对象如下:

  • VkDevice
  • VkQueue
  • VkCommandBuffer
Device函数

Device函数是指任意以Device对象或Device的子对象为第一个参数的Vulkan函数,大多数Vulkan函数都是Device函数。部分Deivce函数如下:

  • vkQueueSubmit
  • vkBeginCommandBuffer
  • vkCreateEvent

Device函数可以使用vkGetInstanceProcAddr或者vkGetDeviceProcAddr来查询:①如果在应用程序中选择使用vkGetInstanceProcAddr,那么在每次调用该函数时会额外调用内置在调用链(call chain)中的函数,这样会稍微降低性能。②如果应用程序选择使用vkGetDeviceProcAddr,那么调用链会基于特定设备进行优化,但返回的就是仅用于该特定设备的函数指针了。③与vkGetInstanceProcAddr既能查询Instance函数又能查询Device函数不同,vkGetDeviceProcAddr只能用于查询Device函数。

总而言之,最佳方案就是使用vkGetInstanceProcAddr查询Instance函数,使用vkGetDeviceProcAddr查询Device函数。

Device扩展

和Instance扩展一样,Device扩展是一组Vulkan设备相关的函数。

Dispatch Tables和Call Chains

Vulkan使用对象模型(object model)来控制行为或操作的作用范围。被作用的对象总是Vulkan函数的第一个参数,而且这个对象是可分派的(参考Vulkan Specification Section 3.3 Object Model)。可分派对象的句柄是一个指向结构的指针,该结构包含了指向加载器维护的分派表(一组函数指针)的指针,分派表包含指向相应对象的Vulkan函数的指针。

加载器维护了两类分派表:

  • Instance分派表:加载器在调用vkCreateInstance期间创建的;
  • Device分派表:加载器在调用vkCreateDevice期间创建的。

应用程序和系统可以各自指定要包含的可选层。然后,由加载器初始化指定的层,为每个Vulkan函数创建一个调用链,分派表的每个条目(入口)将指向该链的第一个元素。这样一来,加载器就为其创建的每个VkInstance建立了一个Instance调用链,为其创建的每个VkDevice建立了一个Device调用链。

当应用程序调用Vulkan函数时,在加载器中首先会命中trampoline函数,这些trampoline函数非常短小简单,主要用于跳转到指定对象的合适的分派表入口。此外,加载器在调用链中还有一类函数叫做terminatorterminator函数会在加载器使能了指定层之后被调用,主要用于将信息传递给驱动。

Instance调用链示例

下图描述了应用程序调用vkCreateInstance函数之后调用链中发生的事情。在初始化调用链之后,加载器会调用第一层的vkCreateInstance,接着调用下一层的vkCreateInstance,一直到最后一个层,最后再调用每个驱动程序的vkCreateInstance。这样一来,调用链中的每个启用了的层都能够根据应用程序中的VkInstanceCreateInfo结构设置所需的内容。
Instance调用链示例
这样同样也突出了当使用Instance调用链时加载器必须解决的某些复杂问题。如上图所示,加载器的terminator需要从多个驱动中收集信息,这意味着加载器必须知道作用于VkInstance上的任意实例级的扩展,以正确地收集它们的信息。

Device调用链示例

Device调用链创建于vkCreateDevice函数中,但通常要比Instance调用链简单,因为它只作用于一个Device,使得暴露Device的特定驱动始终是调用链的terninator
Device调用链示例

权限提升下的注意事项

提升权限:在计算机系统中,提升权限是指用户或程序获得比其正常权限更高的访问权限,以执行特定任务或访问特定资源。

为了在开发过程中保证系统的安全,需要对运行在权限提升条件下的Vulkan应用程序的某些操作进行限制,例如:从不安全的位置读取环境变量和在用户控制的路径中搜索文件。这样一来,就可以保证以提升权限运行的Vulkan应用程序就不会使用未安装在适当位置的组件。

Vulkan Loader会使用特定平台的机制(例如secure_getenv以及类似方式)来查询敏感的环境变量以避免使用不受信任的结果。

在特权条件下会忽略一下环境变量:

  • VK_DRIVER_FILES / VK_ICD_FILENAMES
  • VK_ADD_DRIVER_FILES
  • VK_LAYER_PATH
  • VK_ADD_LAYER_PATH
  • XDG_CONFIG_HOME(Linux/Mac-specific)
  • XDG_DATA_HOME(Linux/Mac-specific)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值