Vulkan加载器接口架构
Table of Contents
-
- Layer Discovery
- Layer Version Negotiation
- Layer Call Chains and Distributed Dispatch
- Layer Unknown Physical Device Extensions
- Layer Intercept Requirements
- Distributed Dispatching Requirements
- Layer Conventions and Rules
- Layer Dispatch Initialization
- Example Code for CreateInstance
- Example Code for CreateDevice
- Meta-layers
- Special Considerations
- Layer Manifest File Format
- Layer Library Versions
Overview
Vulkan is a layered architecture, made up of the following elements:
- The Vulkan Application
- The Vulkan Loader
- Vulkan Layers
- Installable Client Drivers (ICDs)
The general concepts in this document are applicable to the loaders available for Windows, Linux and Android based systems.
Who Should Read This Document
这份文档主要的目标读者是Vulkan应用开发者,驱动和layer开发者,然而本文的信息对于想要深入了解Vulkan运行时 lib的人都是有帮助的。
The Loader
应用程序处于加载器的一端,是加载器直接的上层。 应用程序中加载器的另外一侧是ICD,它控制了vulkan兼容的硬件。需要谨记的一点是Vulkan兼容的硬件可以基于图形的、基于计算的,或者二者兼有。 在应用程序和ICD之间,加载器可以插入多个可选的 layers,这些layer各自提供了某些功能。
加载器负责和各种layers打交道,并负责支持多GPU及驱动程序。任何Vulkan函数最终都会访问到各个模块:加载器、layers和ICDs。加载器肩负把Vulkan函数转发到合适layers和ICDs的重责。Vulkan对象模型允许加载器想调用链中插入layers,以便layers可以在ICD被调用之前处理Vulkan函数。
此文档旨在提供在这些概念之间必需的接口的概览。
Goals of the Loader
加载器设计之初衷如下:
- 在用户的计算机系统上支持多个Vulkan兼容的ICD,且互相之间不影响。
- 支持Vulkan 层,它是可选的模块,可以被应用程序、用户或者系统设定所启用。
- Impact the overall performance of a Vulkan application in the lowest possible fashion.
Layers
Layers是增强Vulkan系统可选的组件。它们可以在已有的Vulkan函数从应用程序到硬件之间进行拦截、求值,甚至修改。 Layers通过libraries来实现,可以通过多种不同的方式被启用(包括应用程序的请求),在CreateInstance的时候被载入。 每一个layer可以选择hook(拦截)任何Vulkan函数,终造成vulkan函数被忽略或者增强。一个layer可以不拦截任何Vulkan函数。它可以选择拦截任何已知的的函数,或者可以选择只拦截一个函数。
layers能带来的特性案例可以包括如下几个:
- 验证API的使用
- 增强Vulkan API的跟踪和调试能力
- 给应用程序的surfaces覆盖追加的内容
因为layers是可选的,你可以选择启用layers来调试你的应用程序,但是在发布产品的时候关闭任何layer。
Installable Client Drivers
Vulkan允许多个 Installable Client Drivers (ICDs) 的一个系统中共存,每一个支持一个或者多个物理设备(通过 VkPhysicalDevice
对象表示)。 加载器负责查找系统上可用的Vulkan ICDs。当给出一组可用的ICDs,加载器可以遍历所有可用的物理设备,并把这些信息返回给应用程序。
Instance Versus Device
在此文档中你可能一直看到一个非常重要的概念。很多函数,拓展和Vulkan中其他的东西都被分为两类:
- Instance-related Objects
- Device-related Objects
Instance-related Objects
一个Vulkan实例是一个高层次的构造,用来提供Vulkan系统层次的信息或者功能。和instance直接相关的Vulkan对象有:
VkInstance
VkPhysicalDevice
一个instance函数的定义是任何把Instance列表的其中一个作为第一个参数,或者没有参数的函数。一些Vulkan Instance函数有:
vkEnumerateInstanceExtensionProperties
vkEnumeratePhysicalDevices
vkCreateInstance
vkDestroyInstance
你可以使用vkGetInstanceProcAddr
查询Vulkan实例函数。vkGetInstanceProcAddr
可以被用来查询设备或者实例的入口函数(包括核心入口函数)。 返回的函数指针对实例以及在该实例下创建的对象(包括所有的l VkDevice
对象)来说是有效的。
同样,一个实例拓展是拓展了Vulkan语言的一系列实例函数。这些将在后续小节中讨论。
Device-related Objects
一个Vulkan设备,从另外一方面讲,是一个用来关联函数和用户系统上特定物理设备的逻辑标识符。 与设备直接关联的Vulkan数据结构有:
VkDevice
VkQueue
VkCommandBuffer
- Any dispatchable object that is a child of a one of the above.
一个设备函数是任何把设备对象作为第一个参数的函数。一些设备函数有:
vkQueueSubmit
vkBeginCommandBuffer
vkCreateEvent
你可以使用 vkGetInstanceProcAddr
或vkGetDeviceProcAddr
来查询Vulkan设备函数。 如果你选择使用vkGetInstanceProcAddr
,它将在调用链上有附加的层,这将些许降低程序性能。 然而,返回的函数指针可以用在以后创建的任何设备上,只要它关联到同一个Vulkan实例上。 如果你使用 vkGetDeviceProcAddr
,调用链将针对特定设备被优化,但是它将 只 能在查询到该函数的设备上使用。 还有,不像 vkGetInstanceProcAddr
,vkGetDeviceProcAddr
只能用于核心的Vulkan设备函数,或者设备拓展函数。
最佳解决方案是使用vkGetInstanceProcAddr
来查询实例拓展函数,使用vkGetDeviceProcAddr
来查询设备拓展函数。 参考Best Application Performance Setup 以获取更多信息。
和Instance拓展一样,一个设备拓展是一系列的拓展了Vulkan语言的Vulkan设备函数。 你可以在本文档后面有更多的了解。
Dispatch Tables and Call Chains
Vulkan使用对象模型来控制特定动作/操作的生命周期。被操作的对象一般都作为Vulkan调用的第一个参数,而且是一个可分发对象(参看Vulkan规范 2.3节 对象模型)。在底层,可分发对象的handle是一个指向数据结构的指针,数据结构反过来包含了一个由加载器维护的分发表。这个转发表包含了能够获取到这个对象的Vulkan函数的指针。
加载器维护了两种类型的分发表:
- Instance转发表
- 在
vkCreateInstance
调用中加载器创建的 - 设备转发表
- 在
vkCreateDevice
调用中加载器创建的
在此时,应用程序或者系统可以指定可选的将被包括的layers。加载器将初始化指定的layers,来为每一个Vulkan函数创建一个调用链,转发表的每一条将指向该调用链的第一个元素。故,加载器为每一个被创建的 VkInstance
建立了instance调用链,为每一个被创建的VkDevice
建立了设备调用链。
当应用程序调用一个Vulkan函数,通常这将先访问加载器的trampoline 函数。这些trampoline 函数很小、简短,能跳转到给定对象的分发表的某一条。另外,对于在instance调用链的函数,加载器有额外的函数,称为 terminator,它在所有即将启用的layers把合适的信息存放到可选的ICDs之后被调用。
Instance Call Chain Example
例如,如下的图展示了vkCreateInstance
的调用链发生了什么。在初始化链之后,加载器将调用第一层的vkCreateInstance
,它将再次调用加载器,这个函数将调用每一个ICD的vkCreateInstance
并保存运行结果。这允许调用链中每一个被启用的层都能基于VkInstanceCreateInfo
数据结构建立自己所需的信息。
这也强调出来加载器在使用instance调用链时必须管理好各种复杂性。如下所示,加载器的 terminator 必须在可见的多个ICD中收集或发送分类信息。这表示加载器必须获知实例层的各个拓展,以便更好的分类。
Device Call Chain Example
设备调用链是在vkCreateDevice
调用中创建的,通常较为简单,因为他们只和一个物理设备打交道,ICD也总是调用链的 terminator。