根签名版本 1.1

根签名版本 1.1 的目的是使应用程序能够在描述符堆中的描述符不会更改或数据描述符指向不会更改时向驱动程序指示。 这允许驱动程序的选项进行优化,这些优化可能知道描述符或它指向的内存在一段时间内是静态的。

概述

根签名版本 1.0 允许描述符堆的内容及其指向的内存在命令列表/捆绑包的任何时间由应用程序自由更改,这些命令列表/捆绑包可能会在 GPU 上运行。 但是,在记录引用描述符或内存的命令后,应用程序通常不需要灵活地更改描述符或内存。

应用程序通常能够:

  • 在命令列表或捆绑包上的绑定描述符表或根描述符之前,设置描述符(以及可能指向的内存)。
  • 在引用这些描述符的命令列表 /捆绑包最后一次完成执行之前,请确保这些描述符不会更改。
  • 确保描述符指向的数据不会在相同的完整持续时间内更改。

或者,应用程序只能遵循该数据不会在较短时间内更改。 在命令列表执行期间,特定数据可能是静态的,根参数绑定(描述符表或根描述符)当前指向数据。 换句话说,应用程序可能希望对 GPU 时间线执行执行,该时间线在通过根参数设置数据期间更新某些数据,知道何时设置数据将是静态的。

如果描述符或数据描述符指向,则不会更改,那么特定的优化驱动程序可能确实特定于硬件供应商,而且重要的是,它们不会更改性能以外的行为。 尽可能多地保留有关应用程序意向的知识不会给应用程序带来负担。

一种优化是,如果许多驱动程序知道应用程序可以做出关于描述符和数据静态性的承诺,则许多驱动程序可以通过着色器生成更高效的内存访问。 例如,如果特定硬件对根参数大小不敏感,则驱动程序可以通过将其转换为根描述符来删除访问堆中的描述符的间接级别。

使用版本 1.1 的开发人员的额外任务是尽可能保证数据的波动性和静态性,以便驱动程序能够做出有意义的优化。 开发人员不必对静态性做出任何承诺。

根签名版本 1.0 继续保持不变,不过重新编译根签名的应用程序现在默认为根签名 1.1(如果需要,可以选择强制版本 1.0)。

静态和易失性标志

以下标志是根签名的一部分,允许驱动程序选择策略,以便最好地在设置根参数时处理各个根参数,并在最初编译时将相同的假设嵌入管道状态对象(PSO),因为根签名是 PSO 的一部分。

以下标志由应用设置,并应用于描述符或数据。

typedef enum D3D12_DESCRIPTOR_RANGE_FLAGS
{
    D3D12_DESCRIPTOR_RANGE_FLAG_NONE    = 0,
    D3D12_DESCRIPTOR_RANGE_FLAG_DESCRIPTORS_VOLATILE    = 0x1,
    D3D12_DESCRIPTOR_RANGE_FLAG_DATA_VOLATILE   = 0x2,
    D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC_WHILE_SET_AT_EXECUTE    = 0x4,
    D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC = 0x8
} D3D12_DESCRIPTOR_RANGE_FLAGS;

typedef enum D3D12_ROOT_DESCRIPTOR_FLAGS
{
    D3D12_ROOT_DESCRIPTOR_FLAG_NONE = 0,
    D3D12_ROOT_DESCRIPTOR_FLAG_DATA_VOLATILE    = 0x2,
    D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC_WHILE_SET_AT_EXECUTE = 0x4,
    D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC  = 0x8
} D3D12_ROOT_DESCRIPTOR_FLAGS;

DESCRIPTORS_VOLATILE

设置此标志后,除绑定描述符表的命令列表/捆绑包的命令列表/捆绑包已提交且尚未完成执行外,应用程序随时都可以更改由根描述符表指向的描述符堆中的描述符。 例如,录制命令列表,并在描述符堆中更改描述符,该描述符在提交命令列表以供执行 之前 有效。 这是根签名版本 1.0 的唯一受支持的行为。

如果未 设置DESCRIPTORS_VOLATILE标志,则描述符是静态的。 此模式没有标志。 静态描述符表示描述符堆中由根描述符表指向的描述符堆中的描述符在命令列表/捆绑包(录制期间)设置描述符表时已初始化,在命令列表/捆绑包完成上次执行之前,无法更改描述符。 根签名版本 1.1,静态描述符是默认假设,应用程序必须根据需要指定DESCRIPTORS_VOLATILE标志。

对于将描述符表与静态描述符配合使用的捆绑包,描述符必须在记录捆绑包时(而不是调用捆绑包的时间)开始准备就绪,直到捆绑包最后一次完成执行才会更改。 指向静态描述符的描述符表必须在捆绑记录期间设置,并且不能继承到捆绑包中。 命令列表对具有已设置在捆绑包中并返回到命令列表的静态描述符表使用描述符表是有效的。

当描述符是静态的时,需要设置DESCRIPTORS_VOLATILE标志的行为发生了另一个更改。 对任何缓冲区视图(与 Texture1D/2D/3D/Cube 视图相反)的边界外访问无效,并生成未定义的结果,包括可能的设备重置,而不是返回读取或删除写入的默认值。 删除应用程序依赖于硬件超出边界访问检查的能力的目的是允许驱动程序选择在认为更高效的情况下,将静态描述符访问提升到根描述符访问。 根描述符不支持任何边界外检查。

如果应用程序在访问描述符时依赖于安全的超出边界内存访问行为,则需要将访问这些描述符的描述符范围标记为DESCRIPTORS_VOLATILE。

DATA_VOLATILE

设置此标志后,描述符指向的数据可以随时由 CPU 更改,但绑定描述符表的命令列表/捆绑包已提交且尚未完成执行。 这是根签名版本 1.0 的唯一受支持的行为。

该标志在描述符范围标志和根描述符标志中都可用。

DATA_STATIC_WHILE_SET_AT_EXECUTE

设置此标志后,描述符指向的数据无法从在 GPU 时间线执行期间在命令列表/捆绑上设置基础根描述符或描述符表时开始更改,并且当后续的绘图/调度不再引用数据时结束。

在 GPU 上设置根描述符或描述符表之前,此数据 甚至可以由相同的命令列表/捆绑包更改。 在指向数据的根描述符或描述符表仍然在命令列表/捆绑包上设置时,也可以更改数据,前提是引用它已完成的绘制/调度。 但是,这样做要求在下次取消引用根描述符或描述符表之前再次将描述符表重新绑定到命令列表。 这允许驱动程序知道根描述符或描述符表指向的数据已更改。

DATA_STATIC_WHILE_SET_AT_EXECUTE和DATA_VOLATILE之间的基本区别在于,DATA_VOLATILE驱动程序无法判断命令列表中的数据副本是否已更改描述符指向的数据,而无需执行额外的状态跟踪。 因此,例如,如果驱动程序可以将任何类型的数据预提取命令插入其命令列表中(例如,使着色器能够更高效地访问已知数据),DATA_STATIC_WHILE_SET_AT_EXECUTE让驱动程序知道它只需要在通过 setGraphicsRootDescriptorTable设置数据时执行数据预提取。 SetComputeRootDescriptorTable 或用于设置常量缓冲区视图、着色器资源视图或无序访问视图的方法之一。

对于捆绑包,承诺在执行时设置数据是静态的,将唯一应用于捆绑包的每个执行。

该标志在描述符范围标志和根描述符标志中都可用。

DATA_STATIC

如果设置了此标志,则描述符指向的数据在记录期间在命令列表/捆绑包上设置内存的根描述符或描述符表已初始化,并且只有在命令列表/捆绑包最后一次执行完之后,才能更改数据。

对于捆绑包,静态持续时间从捆绑包的录制过程中的根描述符或描述符表设置开始,而不是录制调用命令列表。 此外,必须在捆绑包中设置指向静态数据的描述符表,而不是继承。 命令列表使用指向捆绑包中设置并返回到命令列表的静态数据的描述符表有效。

该标志在描述符范围标志和根描述符标志中都可用。

组合标志

一次最多可以指定一个 DATA 标志,但采样器描述符范围根本不支持 DATA 标志,因为采样器不指向数据。

缺少 SRV 和 CBV 描述符范围的任何 DATA 标志意味着假定默认DATA_STATIC_WHILE_SET_AT_EXECUTE行为。 选择此默认值而不是DATA_STATIC的原因是,DATA_STATIC_WHILE_SET_AT_EXECUTE更有可能成为大多数情况下的安全默认值,同时仍产生一些优化机会,而不是默认DATA_VOLATILE。

如果没有 UAV 描述符范围的 DATA 标志意味着假定默认DATA_VOLATILE行为,因为通常写入 UAV。

DESCRIPTORS_VOLATILE 不能 与DATA_STATIC组合,但 可以与其他 DATA 标志结合使用。 DESCRIPTORS_VOLATILE可以与DATA_STATIC_WHILE_SET_AT_EXECUTE相结合的原因是,可变描述符仍需要在命令列表/捆绑包执行期间准备好描述符,DATA_STATIC_WHILE_SET_AT_EXECUTE只是在命令列表/捆绑执行子集内就静态性做出承诺。

标志摘要

下表汇总了可能使用的标志组合。

有效的D3D12_DESCRIPTOR_RANGE_FLAGS设置 描述
未设置标志 描述符是静态的(默认值)。 数据的默认假设:对于 SRV/CBV:DATA_STATIC_WHILE_SET_AT_EXECUTE,对于 UAV:DATA_VOLATILE。 SRV/CBV 的这些默认值可以安全地适应大多数根签名的使用模式。
DATA_STATIC 描述符和数据都是静态的。 这最大化了驱动程序优化的潜力。
DATA_VOLATILE 描述符是静态的,数据是可变的。
DATA_STATIC_WHILE_SET_AT_EXECUTE 描述符是静态的,在执行时设置数据是静态的。
DESCRIPTORS_VOLATILE 描述符是可变的,对数据进行默认假设:对于 SRV/CBV:DATA_STATIC_WHILE_SET_AT_EXECUTE,对于 UAV:DATA_VOLATILE。
DESCRIPTORS_VOLATILE |DATA_VOLATILE 描述符和数据都是可变的,等效于根签名 1.0。
DESCRIPTORS_VOLATILE |DATA_STATIC_WHILE_SET_AT_EXECUTE 描述符是可变的,但请注意,在命令列表执行期间仍不允许更改它们。 因此,在执行期间通过根描述符表设置数据时,合并数据是静态的附加声明是有效的, 基础描述符实际上是静态的,比承诺数据是静态的。

 

有效的D3D12_ROOT_DESCRIPTOR_FLAGS设置 描述
未设置标志 数据的默认假设:对于 SRV/CBV:DATA_STATIC_WHILE_SET_AT_EXECUTE,对于 UAV:DATA_VOLATILE。 SRV/CBV 的这些默认值可以安全地适应大多数根签名的使用模式。
DATA_STATIC 数据是静态的,是驱动程序优化的最佳潜力。
DATA_STATIC_WHILE_SET_AT_EXECUTE 在执行时设置数据是静态的。
DATA_VOLATILE 等效于根签名 1.0。

 

版本 1.1 API 摘要

以下 API 调用启用版本 1.1。

枚举

这些枚举包含用于指定描述符和数据波动性的关键标志。

结构

更新的结构(从版本 1.0)包含对波动性/静态标志的引用。

功能

此处列出的方法取代了原始 D3D12SerializeRootSignatureD3D12CreateRootSignatureDeserializer 函数,因为它们旨在处理任何根签名版本。 序列化形式是传递到 CreateRootSignature API 中的内容。 如果已使用其中根签名创作着色器,则已编译的着色器将包含已序列化的根签名。

方法

ID3D12VersionedRootSignatureDeserializer 接口创建,以反序列化根签名数据结构。

帮助程序结构

已添加帮助程序结构,以帮助初始化某些版本 1.1 结构。

  • CD3DX12_DESCRIPTOR_RANGE1
  • CD3DX12_ROOT_PARAMETER1
  • CD3DX12_STATIC_SAMPLER1
  • CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC

请参阅 D3D12 帮助程序结构和函数。

违反静态性标志的后果

上述描述符和数据标志(以及缺少特定标志所暗示的默认值)定义应用程序向驱动程序承诺的行为方式。 如果应用程序违反承诺,则这是无效的行为:结果未定义,并且可能在不同的驱动程序和硬件上有所不同。

调试层提供了用于验证应用程序履行承诺的选项,包括使用根签名版本 1.1 附带的默认承诺,而无需设置任何标志。

版本管理

编译附加到着色器的根签名时,较新的 HLSL 编译器默认在版本 1.1 编译根签名,而旧的 HLSL 编译器仅支持 1.0。 请注意,1.1 根签名不适用于不支持根签名 1.1 的 OS。

使用着色器编译的根签名版本可以使用 /force_rootsig_ver <version>强制使用特定版本。 如果编译器可以保留在强制版本中编译的根签名的行为,则强制版本会成功,例如,在根签名中删除不受支持的标志,这些标志仅用于优化目的,但不会影响行为。

例如,应用程序可以在生成应用程序时将 1.1 根签名编译为 1.0 和 1.1,并根据作系统支持级别在运行时选择适当的版本。 但是,应用程序单独编译根签名(特别是如果需要多个版本),与着色器单独编译根签名是最具空间效率的。 即使最初未使用附加根签名编译着色器,也可以使用 /verifyrootsignature 编译器选项来保留与着色器的根签名兼容性的编译器验证的好处。 稍后在运行时,可以使用没有根签名的着色器创建 PSO,同时传递所需的根签名(可能是 OS 支持的适当版本)作为单独的参数。

创建根签名

根签名

在 HLSL 中指定根签名