Skip to content

Latest commit

 

History

History
230 lines (152 loc) · 13.8 KB

02 - 基础.md

File metadata and controls

230 lines (152 loc) · 13.8 KB

02 - 基础

主机和设备环境

Vulkan 规范假定并要求:与 Vulkan 实现有关的主机环境具有以下属性:

  1. 主机必须具备对 8、16、32 和 64 位有符号和无符号二乘整数的运行时支持,所有整数均可按字节大小寻址。
  2. 主机必须支持 32 位和 64 位浮点类型的运行,并满足浮点计算部分中的范围和精度限制。
  3. 这些类型在主机上的表示法和结束符必须与支持的每个物理设备上相同类型的表示法和结束符一致。

Note

由于主机和物理设备操作都可以访问 Vulkan 中的各种数据类型和结构,因此实施应能在两种路径中有效地访问这些数据,以便编写可移植且性能良好的应用程序。

执行模式

本节概述了 Vulkan 系统的执行模型。

Vulkan 公开(expose)了一个或多个设备,每个设备公开(expose)了一个或多个队列,这些队列可以彼此异步处理工作。 设备支持的队列集被划分为多个族(families)。每个族(families)支持一种或多种类型的功能,并可能包含具有类似特性的多个队列。单个族(families)内的队列被认为是相互兼容的,为一个族(families)的队列生成的工作可在该族(families)内的任何队列上执行。 本规范定义了队列可支持的以下功能类型:图形、计算、视频解码、视频编码、受保护内存管理、稀疏内存管理和传输。

Note

单个设备可能会报告多个类似的队列族,而不是或同时报告一个或多个队列族的多个成员。这表明,虽然这些族(families)的成员具有相似的功能,但它们之间并不直接兼容。

设备内存由应用程序明确管理。每个设备都可以声明(advertise )一个或多个堆,代表不同的内存区域。内存堆可以是设备本地(device-local)的,也可以是主机本地(host-local)的,但设备始终可以看见它们。 有关内存堆的更多细节可通过堆上可用的内存类型查看。实现中可用内存区域的示例包括:

  1. 设备本地(device-local)是与设备物理连接的内存。
  2. device-local, host visible(设备本地,主机可见)是主机可见的设备本地内存。
  3. 主机本地、主机可见内存是主机本地内存,设备和主机均可见。

在其他架构中,可能只有一个堆,可用于任何目的

队列操作

Vulkan 队列为设备的执行引擎提供了一个接口。这些执行引擎的命令会在执行之前记录到命令缓冲区中,然后提交到队列中执行。一旦提交到队列,命令缓冲区就会开始并完成执行,而无需应用程序的进一步干预,不过执行顺序取决于一系列隐式和显式排序约束。

vulkan 通过使用队列提交命令来提交任务,队列提交命令的形式通常为 vkQueue*(如 vkQueueSubmit、vkQueueBindSparse),并可接收工作开始前需要等待的信号量(semaphores )列表,以及工作完成后需要发出的信号量(semaphores )列表。 工作本身、发出信号和等待信号都是队列操作。一旦队列操作提交完毕,队列提交命令就会将控制权返回给应用程序,而不会等待完成。

不同队列上的队列操作之间或队列与主机之间的执行顺序没有隐式排序限制,这些操作可以任意顺序进行。不同队列之间或队列与主机之间执行顺序的显式排序约束可以用 semaphores 和栅栏(fence)来表示。

向单个队列提交的命令缓冲区会遵守提交顺序和其他隐式排序的保证(guarantees),但在其他情况下可能会出现重叠或执行顺序混乱。其他类型的批次和针对单一队列的队列提交(如稀疏内存绑定)与其他队列提交或批次没有隐式排序约束。队列提交和单个批次之间的其他显式排序约束可以用信号和栅栏(fence)来表示。

在栅栏(fence)或信号量(semaphore )发出信号时,可以保证之前提交的队列操作已经执行完毕,并且这些队列操作的内存写入可供未来的队列操作使用。等待信号量(semaphore )或栅栏(fence)的激活,可保证后续命令也能看到之前的可用写入。

介于相同或不同批次或提交的主命令缓冲区之间,以及主命令缓冲区和次级(secondary)命令缓冲区之间的命令缓冲区边界不会引入任何额外的排序限制。换句话说,在任何信号量(semaphore )或栅栏(fence)操作之间提交命令缓冲集合(可包括执行二级命令缓冲区)时,除非每个缓冲区边界重置当前的状态,执行所记录的命令就像将所有命令都记录到一个主命令缓冲区中一样。 显式排序约束可以用显式同步原语来表达。

命令缓冲区内的命令之间有一些隐式排序保证,但只涉及执行的一个子集。其他显式排序约束可通过各种显式同步原语来表达。

Note

由于 Vulkan 设备具有深度流水线和并行性,因此实现过程中可以非常自由地重叠执行提交到队列中的工作。

记录在命令缓冲区中的命令可以执行操作、设置跨命令的持续状态、同步其他命令或间接启动其他命令,其中有些命令可以同时执行上述几种操作。每条此类命令的 "命令属性 "部分都列出了该命令所扮演的角色:

  1. 操作:操作命令执行可更新内存值的操作。例如绘图命令、调度命令。
  2. 状态:状态设置命令更新命令缓冲区的当前状态,从而影响未来操作命令的运行。
  3. 同步:同步命令通过引入明确的执行和内存依赖关系,对操作命令施加排序限制。
  4. 定向(Indirection):定向命令执行未直接记录在同一命令缓冲区中的其他命令。

Note

在没有显式同步或隐式排序保证的情况下,操作命令可能会重叠执行或执行顺序混乱,从而可能导致数据竞争(data races)。不过,这种重新排序不会影响任何操作命令所观察到的当前状态。每条操作命令都会使用命令缓冲区中出现该命令时的有效状态,与执行时间无关。

对象模型

Vulkan 中的设备、队列和其他实体由 Vulkan 对象表示。在 API 层面,所有对象都由句柄(handle)来表示。

句柄分为两类:可调度句柄和不可调度句柄。可调度句柄类型是指向不透明类型的指针。该指针可被各层用作拦截 API 命令的一部分,因此每条 API 命令的第一个参数都是可调度类型。可调度类型的每个对象在其生命周期内都必须有一个唯一的句柄值。

不可调度的句柄类型是一个 64 位整数类型,其含义取决于实现情况。如果 VkDevice 启用了私有数据(privateData)功能,那么在该设备上创建的每个不可调度类型的对象在其生命周期内都必须有一个在该设备上创建的对象中唯一的句柄值。否则,不可调度句柄可能会在句柄中直接编码对象信息,而不是作为底层对象的引用,因此句柄值可能并不唯一。如果句柄值不是唯一的,那么销毁一个这样的句柄不得导致其他类型的相同句柄失效,如果该句柄值被创建的次数多于被销毁的次数,也不得导致相同类型的相同句柄失效。

所有从 VkDevice(即以 VkDevice 作为第一个参数)创建或分配的对象都是该设备的私有对象,不得在其他设备上使用。

对象寿命

对象分别由 vkCreate* 和 vkAllocate* 命令创建或分配。对象一旦被创建或分配,其 "结构 "就被认为是不可改变的,但某些对象类型的内容仍可自由更改。vkDestroy* 和 vkFree* 命令可分别销毁或释放对象。

分配 Allocate(而非创建 Create)的对象会从现有的对象池或内存堆中获取资源,释放后又会将资源返回给该对象池或内存堆。在运行期间,创建和销毁对象的频率通常较低,而分配和释放对象的频率却很高。 池对象有助于提高分配和释放的性能。

应用程序有责任跟踪 Vulkan 对象的生命周期,而不是在它们仍在使用时将其销毁。

应用程序拥有的内存的所有权会立即被任何 Vulkan 命令获取。这些内存的所有权必须在命令持续时间结束时释放回应用程序,除非该命令被延迟,以便应用程序可以在所有获取内存的命令返回后立即更改或释放内存。如果命令被延迟,则在延迟操作完成后,内存的所有权将被释放回应用程序。

以下对象类型在传入 Vulkan 命令时就会被消耗掉,并且不可以使用创建它们的对象对它们进行进一步的访问。在传入任何 API 命令的持续时间内,不得销毁这些对象: VkShaderModule VkPipelineCache VkValidationCacheEXT

作为参数传递以创建另一个对象的 VkRenderPass 或 VkPipelineLayout 对象,在传入该对象的命令持续时间结束后,该对象不会被进一步访问。在命令缓冲区中使用的 VkRenderPass 遵循以下规则。

例如,使用 RenderPass 创建 GraphicsPipeline,在 API vkCreateGraphicsPipeline()结束之后,被创建出来的 GraphicsPipeline 对象不会再访问 RenderPass。

VkDescriptorSetLayout 对象可被对使用该布局分配的描述符集进行操作的命令访问,并且在描述符集布局被销毁后,不得使用 vkUpdateDescriptorSets 更新这些描述符集。否则,一旦命令的主机执行完成,VkDescriptorSetLayout 对象将不再被它传入的 API 命令引用。

在设备完成对任何其他类型的 Vulkan 对象的所有使用(如通过命令缓冲区执行)之前,应用程序不得销毁该对象。

当使用以下 Vulkan 对象的命令缓冲区处于待处理状态时,不得销毁该对象: VkEvent VkQueryPool VkBuffer VkBufferView VkImage VkImageView VkPipeline VkSampler VkSamplerYcbcrConversion VkDescriptorPool VkFramebuffer VkRenderPass VkCommandBuffer VkCommandPool VkDeviceMemory VkDescriptorSet VkIndirectCommandsLayoutNV VkAccelerationStructureNV VkAccelerationStructureKHR VkVideoSessionKHR VkVideoSessionParametersKHR

销毁这些对象会将处于记录或可执行状态并使用这些对象的任何命令缓冲区移至无效状态。

当任何队列正在执行使用以下 Vulkan 对象的命令时,不得销毁这些对象: VkFence VkSemaphore VkCommandBuffer VkCommandPool

一般而言,对象可以任何順序被銷毀或釋放,即使被釋放的物件涉及另一個物件的使用(例如 在视图中使用资源、在描述符集中使用视图、在另一个管道中使用管线库、在另一个管道中将引用的管道用于额外的图形着色器组、在由顶级加速结构引用的实例中使用底层加速结构、 在命令缓冲区中使用对象、将内存分配绑定到资源),只要任何使用释放对象的对象不再以任何方式使用,除非被销毁或以不再使用其他对象的方式重置(如重置命令缓冲区)。如果该对象已被重置,则可以像从未使用过被释放对象一样使用它。 如果对象之间存在父/子关系,则属于例外情况。在这种情况下,应用程序不得先于其子对象销毁父对象,除非父对象被明确定义为在销毁时释放其子对象(如下面定义的池对象)。

VkCommandPool 对象是 VkCommandBuffer 对象的父对象。VkDescriptorPool 对象是 VkDescriptorSet 对象的父对象。VkDevice 对象是许多对象类型的父对象(所有对象类型在创建时都将 VkDevice 作为参数)。

以下 Vulkan 对象对何时销毁有特定限制:

VkQueue 对象不能显式销毁。相反,当从 VkDevice 对象中检索到的对象被销毁时,它们会被隐式销毁。

销毁一个池对象会隐式释放从该池分配的所有对象。具体来说,销毁 VkCommandPool 会释放所有从中分配的 VkCommandBuffer 对象,而销毁 VkDescriptorPool 则会释放所有从中分配的 VkDescriptorSet 对象。

当从 VkDevice 获取的所有 VkQueue 对象都处于空闲状态,并且从这些对象创建的所有对象都已销毁时,VkDevice 对象就可以被销毁。

这包括以下对象: VkFence VkSemaphore VkEvent VkQueryPool VkBuffer VkBufferView VkImage VkImageView Vk 着色模块 VkPipelineCache Vkipeline VkPipelineLayout Vk 采样器 VkSamplerYcbcrConversion VkDescriptorSetLayout VkDescriptorPool VkFramebuffer VkRenderPass VkCommandPool VkCommandBuffer VkDeviceMemory VkValidationCacheEXT VkAccelerationStructureNV VkAccelerationStructureKHR VkVideoSessionKHR VkVideoSessionParametersKHR

VkPhysicalDevice 对象不能显式销毁。相反,当从 VkInstance 对象中获取的对象被销毁时,它们会被隐式销毁。

一旦从任何 VkPhysicalDevice 对象创建的所有 VkDevice 对象都已销毁,VkInstance 对象就可以被销毁。

外部对象句柄

如上所述,从 VkDevice 创建或分配的对象句柄的作用域仅限于该逻辑设备。不在作用域内的对象被称为外部对象。要将外部对象纳入作用域,必须从源作用域中的对象导出外部句柄,并将其导入目标作用域。

Note

外部句柄及其关联资源的作用域可能因其类型而异,但通常可以跨进程和 API 边界共享。

应用程序二进制接口

命令语法和持续时间

检索结果的生命周期

线程行为

有效使用

使用验证

隐式有效使用

VkResult 返回代码

数值表示和计算

浮点计算

浮点格式转换

16 位浮点数

无符号 11 位浮点数

无符号 10 位浮点数

一般要求

定点数据转换

从归一化定点到浮点的转换

从浮点数到归一化定点数的转换

字符串表示法

常见对象类型

偏移量

扩展

矩形

结构类型

API 名称别名