Direct3D 12 互作

D3D12 可用于编写组件化应用程序。

互作概述

D3D12 非常强大,并允许应用程序以类似控制台的效率编写图形代码,但并不是每个应用程序都需要从头开始重塑方向盘并编写整个呈现引擎。 在某些情况下,另一个组件或库已经做得更好,或者在其他情况下,部分代码的性能与其正确性和可读性不一样重要。

本部分介绍以下互作技术:

  • 同一设备上的 D3D12 和 D3D12
  • 不同设备上的 D3D12 和 D3D12
  • 在同一设备上,D3D12 和 D3D11、D3D10 或 D2D 的任何组合
  • 不同设备上的 D3D12 和 D3D11、D3D10 或 D2D 的任意组合
  • D3D12 和 GDI、D3D12 和 D3D11 和 GDI

使用互作的原因

应用程序希望与其他 API 进行 D3D12 互作的原因有多种。 一些示例:

  • 增量移植:想要将整个应用程序从 D3D10 或 D3D11 移植到 D3D12,同时在移植过程的中间阶段运行(以启用测试和调试)。
  • 黑盒代码:在移植其余代码时,希望保留应用程序的特定部分 as-is。 例如,可能无需移植游戏的 UI 元素。
  • 不可更改的组件:需要使用应用程序不拥有的组件,这些组件不会写入目标 D3D12。
  • 新组件:不想移植整个应用程序,但想要使用使用 D3D12 编写的新组件。

D3D12 中的互作有四种主要技术:

  • 应用可以选择向组件提供打开的命令列表,该列表将一些附加的呈现命令记录到已绑定的呈现目标。 这相当于向 D3D11 中的另一个组件提供准备好的设备上下文,并且非常适合将 UI/文本添加到已绑定的缓冲区等内容。
  • 应用可以选择向组件提供命令队列以及所需的目标资源。 这相当于在 D3D11 中使用 ClearStateDeviceContextState API 向另一个组件提供干净的设备上下文。 这就是 D2D 等组件的工作原理。
  • 组件可以选择生成命令列表的模型,该列表可能并行生成,应用将在以后负责提交。 必须跨组件边界至少提供一个资源。 尽管 D3D12 中的性能更理想,但 D3D11 中的这种技术在 D3D11 中使用。
  • 每个组件都有自己的队列和/或设备,应用和组件需要跨组件边界共享资源和同步信息。 这类似于旧 ISurfaceQueue,更现代 IDXGIKeyedMutex

这些方案之间的差异在于组件边界之间完全共享的内容。 假设设备是共享的,但由于设备基本上是无状态的,因此它并不真正相关。 关键对象是命令列表、命令队列、同步对象和资源。 其中每一个在共享它们时都有自己的复杂性。

共享命令列表

最简单的互作方法只需要与引擎的一部分共享命令列表。 呈现作完成后,命令列表所有权将返回到调用方。 可以通过堆栈跟踪命令列表的所有权。 由于命令列表是单线程的,因此应用无法使用此技术执行唯一或创新的作。

共享命令队列

可能是同一进程中共享设备的多个组件的最常见技术。

当命令队列是共享单元时,需要调用组件,使其知道所有未完成的命令列表都需要立即提交到命令队列(并且需要同步任何内部命令队列)。 这相当于 D3D11 Flush API,是应用程序提交自己的命令列表或同步基元的唯一方法。

共享同步基元

在其自己的设备和/或命令队列上运行的组件的预期模式是接受 ID3D12Fence 或共享句柄,并在开始工作时接受 UINT64 对,它将等待,然后等待第二个 ID3D12Fence 或共享句柄,以及 UINT64 对,当所有工作完成时,它将发出信号。 此模式与 IDXGIKeyedMutex和 DWM/DXGI 翻转模型同步设计的当前实现相匹配。

共享资源

到目前为止,编写利用多个组件的 D3D12 应用的最复杂部分是如何处理跨组件边界共享的资源。 这主要是由于资源状态的概念。 虽然资源状态设计的某些方面旨在处理命令内列表同步,但其他方面确实会影响命令列表、影响资源布局以及访问资源数据的有效作集或性能特征。

有两种处理这种复杂性的模式,这两种模式基本上都涉及组件之间的协定。

  • 合同可由组件开发人员定义并记录。 这可能很简单,就像“启动工作时资源必须处于默认状态,在完成工作时将重新置于默认状态”,或者可能具有更复杂的规则来允许共享深度缓冲区等内容,而无需强制中间深度解析。
  • 当资源跨组件边界共享时,应用程序可以在运行时定义协定。 它包含相同的两个信息片段 : 资源在组件开始使用时的状态,组件在完成时应将其保留在其中的状态。

选择互作模型

对于大多数 D3D12 应用程序,共享命令队列可能是理想的模型。 它允许创建和提交工作的完整所有权,无需额外的内存开销即可创建冗余队列,并且不会对处理 GPU 同步基元产生性能影响。

一旦组件需要处理不同的队列属性(例如类型或优先级),或者共享需要跨越进程边界,则需要共享同步基元。

共享或生成命令列表并非由第三方组件在外部广泛使用,但在游戏引擎内部的组件中可能广泛使用。

互作 API

Direct3D 11 on 12 主题将指导你使用与本主题中所述的互作类型相关的大部分 API 图面。

另请参阅 ID3D12Device::CreateSharedHandle 方法,可用于在 Windows 图形 API 之间共享图面。