Direct3D 12 互操作

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

互操作概述

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

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

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

使用互操作的原因

应用程序需要 D3D12 与其他 API 互操作的原因有多种。 下面是一些示例:

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

D3D12 中的互操作有四种main技术:

  • 应用可以选择向组件提供打开的命令列表,该列表将一些其他呈现命令记录到已绑定的呈现目标。 这相当于向 D3D11 中的另一个组件提供准备好的设备上下文,并且非常适合将 UI/文本添加到已绑定的缓冲区等操作。
  • 应用可以选择向组件提供命令队列以及所需的目标资源。 这相当于在 D3D11 中使用 ClearStateDeviceContextState API 向另一个组件提供干净的设备上下文。 这就是 D2D 等组件的工作方式。
  • 组件可以选择生成命令列表(可能并行)的模型,应用负责稍后提交该列表。 必须跨组件边界提供至少一个资源。 使用延迟上下文的 D3D11 中提供了相同的技术,但 D3D12 中的性能更理想。
  • 每个组件都有自己的队列 () 和/或设备 () ,应用和组件需要跨组件边界共享资源和同步信息。 这类似于旧版 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 之间共享图面。