从 Direct3D 11 到 Direct3D 12 的重要更改

Direct3D 12 表示与 Direct3D 11 编程模型的重大区别。 Direct3D 12 使应用比以往更接近硬件。 通过更接近硬件,Direct3D 12 更快、更高效。 但是,通过 Direct3D 12 提高速度和效率的应用的权衡在于,你负责的任务比 Direct3D 11 多。

Direct3D 12 是低级别编程的返回;它通过引入以下新功能来更好地控制游戏和应用的图形元素:用于表示管道的总体状态的对象、用于工作提交的命令列表和捆绑包,以及用于资源访问的描述符堆和表。

你的应用使用 Direct3D 12 提高了速度和效率,但你负责的任务比 Direct3D 11 任务要多。

显式同步

  • 在 Direct3D 12 中,CPU-GPU 同步现在是应用的明确责任,并且不再由运行时隐式执行,因为它位于 Direct3D 11 中。 这一事实也意味着 Direct3D 12 不会自动检查管道危害,因此,这又是应用的责任。
  • 在 Direct3D 12 中,应用负责管道数据更新。 也就是说,必须在 Direct3D 12 中手动执行 Direct3D 11 中的“Map/Lock-DISCARD”模式。 在 Direct3D 11 中,如果在调用 ID3D11DeviceContext::Map 时仍使用缓冲区 D3D11_MAP_WRITE_DISCARD,则运行时将返回指向新内存区域的指针,而不是旧缓冲区数据。 这样,GPU 就可以在应用在新缓冲区中放置数据时继续使用旧数据。 应用中不需要额外的内存管理;旧缓冲区在 GPU 完成后自动重复使用或销毁。
  • 在 Direct3D 12 中,应用显式控制所有动态更新(包括常量缓冲区、动态顶点缓冲区、动态纹理等)。 这些动态更新包括任何必需的 GPU 围栏或缓冲。 该应用负责将内存保留为可用状态,直到不再需要内存。
  • Direct3D 12 仅在接口的生存期内使用 COM 样式引用计数(通过使用与设备生存期绑定的 Direct3D 的弱引用模型)。 所有资源和说明内存生存期都是由应用负责维护的适当持续时间,并且不会对引用进行计数。 Direct3D 11 使用引用计数来管理接口依赖项的生存期。

物理内存驻留管理

Direct3D 12 应用程序必须防止多个队列、多个适配器和 CPU 线程之间的争用条件。 D3D12 不再同步 CPU 和 GPU,也不支持资源重命名或多缓冲的便捷机制。 围栏必须用于避免多个处理单元在另一个处理单元使用完内存之前过度写入内存。

Direct3D 12 应用程序必须确保数据在 GPU 读取数据时驻留在内存中。 在创建对象期间,每个对象使用的内存都驻留在一起。 调用这些方法的应用程序必须使用围栏来确保 GPU 无法访问已逐出的对象。

资源屏障是另一种类型的同步,用于在非常精细的级别同步资源和子资源转换。

请参阅 Direct3D 12 中的内存管理。

管道状态对象

Direct3D 11 允许通过一组大型独立对象执行管道状态作。 例如,输入汇编程序状态、像素着色器状态、光栅器状态和输出合并状态都可以独立修改。 此设计提供了图形管道的方便且相对高级别的表示形式,但它不利用新式硬件的功能,主要是因为各种状态通常相互依赖。 例如,许多 GPU 将像素着色器和输出合并状态合并为单个硬件表示形式。 但是,由于 Direct3D 11 API 允许单独设置这些管道阶段,因此显示驱动程序无法解决管道状态问题,直到完成状态,直到绘制时间为止。 此方案延迟硬件状态设置,这意味着每个帧的额外开销和更少的最大绘制调用。

Direct3D 12 通过将大部分管道状态统一到不可变的管道状态对象(PSO)来解决此方案,这些对象在创建时最终确定。 然后,硬件和驱动程序可以立即将 PSO 转换为执行 GPU 工作所需的任何硬件本机指令和状态。 你仍然可以动态更改正在使用的 PSO,但为此,硬件只需将最少的预计算状态直接复制到硬件寄存器,而不是实时计算硬件状态。 通过使用 PSO,绘图调用开销会显著减少,并且每个帧可以进行更多的绘图调用。 有关 PSO 的详细信息,请参阅 管理 Direct3D 12中的图形管道状态。

命令列表和捆绑包

在 Direct3D 11 中,所有工作提交都通过 即时上下文完成,该上下文表示转到 GPU 的单个命令流。 为了实现多线程缩放,游戏还 延迟上下文, 可供它们使用。 Direct3D 11 中的延迟上下文不会完全映射到硬件,因此可以在其中完成相对较少的工作。

Direct3D 12 基于命令列表引入了一个新的工作提交模型,其中包含在 GPU 上执行特定工作负荷所需的全部信息。 每个新命令列表都包含信息,例如要使用的 PSO、需要哪些纹理和缓冲区资源,以及所有绘图调用的参数。 由于每个命令列表都是自包含的,并且不继承任何状态,因此驱动程序可以预先预计算所有必需的 GPU 命令,并以自由线程的方式进行预计算。 唯一必要的串行过程是通过命令队列将命令列表最终提交到 GPU。

除了命令列表,Direct3D 12 还引入了第二级工作预计算:捆绑包。 与完全自包含且通常构造、提交一次和放弃的命令列表不同,捆绑包提供了一种允许重复使用的状态继承形式。 例如,如果游戏想要绘制具有不同纹理的两个字符模型,一种方法是记录一个命令列表,其中包含两组相同的绘图调用。 但另一种方法是“记录”一个捆绑包,用于绘制单个字符模型,然后使用不同的资源在命令列表中“播放”捆绑包两次。 在后一种情况下,显示驱动程序只需计算一次适当的指令,创建命令列表实质上相当于两个低成本的函数调用。

有关命令列表和捆绑包的详细信息,请参阅 Direct3D 12 中的工作提交。

描述符堆和表

Direct3D 11 中的资源绑定高度抽象且方便,但使许多新式硬件功能未充分利用。 在 Direct3D 11 中,游戏创建 视图 资源对象,然后将这些视图绑定到管道中各种着色器阶段 多个 槽。 着色器反过来从绘制时固定的显式绑定槽读取数据。 此模型意味着,每当游戏使用不同的资源进行绘制时,它都必须将不同的视图重新绑定到不同的槽位,然后再次调用绘图。 此情况还表示完全利用新式硬件功能可以消除的开销。

Direct3D 12 更改绑定模型以匹配新式硬件,并显著提高性能。 Direct3D 12 不要求独立资源视图和显式映射到槽,而是提供描述符堆,游戏在其中创建其各种资源视图。 此方案为 GPU 提供一种机制,用于将硬件本机资源说明(描述符)直接写入内存的前端。 若要声明管道要用于特定绘图调用的资源,游戏指定一个或多个描述符表,这些描述符表表示完整描述符堆的子范围。 由于描述符堆已填充了适当的特定于硬件的描述符数据,因此更改描述符表是一项极其低成本的作。

除了描述符堆和表提供的改进性能外,Direct3D 12 还允许在着色器中动态编制索引资源,从而提供前所未有的灵活性并解锁新的呈现技术。 例如,现代延迟呈现引擎通常将某种材料或对象标识符编码为中间 g 缓冲区。 在 Direct3D 11 中,这些引擎必须小心避免使用太多材料,因为包含一个 g 缓冲区中的过多材料可能会显著减慢最终呈现传递的速度。 使用动态可索引资源时,一个包含一千个材料场景的场景可以像只有十个材料一样快完成。

有关描述符堆和表的详细信息,请参阅 资源绑定,以及 Direct3D 11 绑定模型中的差异。

从 Direct3D 11 移植

从 Direct3D 11 移植是一个涉及的过程,如 从 Direct3D 11 移植到 Direct3D 12中所述。 另请参阅 使用 Direct3D 11、Direct3D 10 和 Direct2D中的选项范围。

了解 Direct3D 12