支持命令列表

本部分仅适用于 Windows 7 及更高版本和 Windows Server 2008 R2 及更高版本的 Windows。

Direct3D 运行时对命令列表使用以下 Direct3D 11 DDI:

驱动程序的 CommandListExecuteCalcPrivateCommandListSizeCreateCommandListDestroyCommandList 函数的语义主要是根据其他类似的 DDI 函数和相应 DDI 的 API 文档自行解释的。

Direct3D 运行时在 pCreateCommandList 参数指向的 D3D11DDIARG_CREATECOMMANDLIST 结构的 hDeferredContext 成员中指定的延迟上下文上成功调用驱动程序的 CreateCommandListRecycleCreateCommandList 函数后,Direct3D 运行时对延迟的上下文执行以下销毁序列:

  1. Direct3D 运行时会“关闭”所有打开的延迟对象句柄。 请注意,这些句柄可能仍然绑定在延迟上下文中。

  2. 运行时会销毁延迟的上下文。

在调用 CreateCommandListRecycleCreateCommandList 期间,驱动程序对状态刷新 DDI 回调函数的任何调用都会继续泄露延迟上下文的当前状态。 但是,在“关闭”和销毁递延上下文期间,对状态刷新 DDI 的任何调用都会反映出没有任何内容被绑定(也就是说,在调用 CreateCommandListRecycleCreateCommandList 之后,所有内容都隐式地解除了绑定)。

递延上下文也可由应用程序显式放弃,或因 API 或驱动程序出错而放弃。 在这种情况下,Direct3D 运行时会执行以下序列:

  1. Direct3D 运行时会调用驱动程序的 AbandonCommandList 函数。

  2. 运行时会逐个解除延迟上下文中的句柄绑定。

  3. 运行时会“关闭”所有打开的延迟对象句柄。

  4. 运行时会回收或销毁延迟的上下文。

前面的序列类似于直接上下文的销毁序列。 调用驱动程序的 AbandonCommandList 函数为驱动程序提供了一个机会,使其可以将状态应用到驱动程序喜欢的任何状态中。

在调用驱动程序的 CommandListExecute 函数期间,驱动程序必须转换延迟上下文的状态,使其与创建设备时的状态相当。 此操作也被称为“清除状态”操作。 但是,在调用驱动程序的 CommandListExecute 函数期间,驱动程序对状态刷新 DDI 回调函数的任何调用仍会反映上一次 DDI 调用驱动程序函数时绑定的状态。 在对驱动程序函数的下一次 DDI 调用中,驱动程序对状态刷新 DDI 回调函数的任何调用都会将当前状态显示为完全为空,这反映了 CommandListExecute 中隐式的状态转换。 这与状态刷新 DDI 回调函数的典型语义和行为略有不同。 如果驱动程序在调用驱动程序的 SetShader 函数时调用了状态刷新 DDI 回调函数,则状态刷新 DDI 回调函数将显示已绑定正在绑定的新着色器。 在 CommandListExecute 期间,状态刷新 DDI 回调行为的这种差异为驱动程序反映旧状态提供了更大的灵活性。

Direct3D 版本 11 API 可确保任何查询都不会既被命令列表操作(即调用了 QueryBeginQueryEnd),又只被试图执行命令列表的上下文“开始”。 此 API 还确保不会在当前映射了相同资源的上下文中执行记录了动态资源映射的命令列表。 在应用程序调用 FinishCommandList 函数之前,Direct3D 运行时会调用驱动程序的 QueryEndResourceUnmap DDI 函数,该函数用于仍打开已开始查询或映射资源的任何查询或动态资源,因为 FinishCommandList 会隐式终止查询范围并解除映射资源的映射。

小型命令列表的优化

针对小内存量命令列表的内存循环优化对于减少命令列表 DDI 函数调用之间的争用和降低命令列表所需的调用处理开销非常重要。 每个命令列表中固有的处理开销都很大。 此优化适用于命令列表,在这种情况下,命令列表所需的处理开销会占用命令列表所需的 CPU 时间和内存空间。 小内存量命令列表是指单个图形命令,如 CopyResource。 CopyResource 所需的内存量为两个指针。 不过,CopyResource 仍需要与大内存容量命令列表相同的命令列表调用处理量。 当小内存量命令列表频繁生成时,运行时调用驱动程序的 CreateCommandListDestroyCommandListCreateDeferredContextDestroyDevice(D3D10) 函数(用于延迟上下文)所需的处理开销就变得越来越重要。 这里所指的内存是存放驱动程序数据结构的系统内存,其中包括 DDI 句柄内存。

驱动程序的 RecycleCommandList 函数必须在驱动程序句柄停止使用(但尚未删除)以及以前未使用的驱动程序句柄被重新使用时通知驱动程序。 此通知同时适用于命令列表和延迟上下文句柄。 驱动程序必须回收的唯一内存是 DDI 句柄指向的内存。 虽然 RecycleCommandList 的目标是回收与句柄相关联的内存,但为了提高效率,驱动程序可以完全灵活地选择回收哪些内存。 驱动程序无法更改即时上下文命令列表句柄指向的内存区域的大小。 此大小是 CalcPrivateCommandListSize 的返回值。 驱动程序也不能更改上下文命令列表本地句柄指向的内存区域的大小,该大小是 CalcDeferredContextHandleSize 的返回值。

驱动程序的 RecycleCreateCommandListRecycleCreateDeferredContext DDI 函数必须以 E_OUTOFMEMORY HRESULT 值返回内存不足错误代码。 这些函数不会通过调用 pfnSetErrorCb 函数来提供此类错误代码。 通过这一驱动程序要求,运行时就不必使用全设备同步来监视这些创建类型驱动程序函数的即时上下文错误。 对于小内存量命令列表来说,注意这些错误会造成灾难性的争用。

驱动程序的 RecycleDestroyCommandListRecycleCommandListRecycleCreateCommandList 函数之间的区别非常重要。 它们的功能如下。

RecycleDestroyCommandList

运行时会调用驱动程序的 RecycleDestroyCommandList 函数,通知驱动程序需要进行轻量级销毁。 也就是说,驱动程序还不应取消 DDI 命令列表句柄的内存分配。 驱动程序的 RecycleDestroyCommandList 函数与驱动程序的 DestroyCommandList 函数一样是自由线程的。

RecycleCommandList

驱动程序的 RecycleCommandList 函数会通知驱动程序,运行时已将命令列表句柄集成回延迟上下文缓存。 然后,该函数为驱动程序提供了将与命令列表相关的内存整合回延迟上下文缓存的机会。 运行时会从延迟上下文线程调用驱动程序的 RecycleCommandList 函数。 RecycleCommandList DDI 函数减少了驱动程序执行同步的需要。

RecycleCreateCommandList

运行时会调用驱动程序的 RecycleCreateCommandList 函数,使之前未使用的 DDI 句柄重新完全有效。

这些回收 DDI 函数提供了优化机会,有助于回收小内存量命令列表的资源。 以下伪代码展示了通过从 API 到 DDI 的函数调用流程来实现运行时的情况:

::FinishCommandList()
{
  // Empty InterlockedSList, integrating into the cache
  Loop { DC::pfnRecycleCommandList }

  If (Previously Destroyed CommandList Available)
 { IC::pfnRecycleCreateCommandList }
 else
  {
    IC::pfnCalcPrivateCommandListSize
    IC::pfnCreateCommandList
    IC::pfnCalcDeferredContextHandleSize(D3D11DDI_HT_COMMANDLIST)
  }

  Loop { DC::pfnDestroy* (context-local handle destroy) }

  IC::pfnRecycleCreateDeferredContext
}
...
Sporadic: DC::pfnCreate* (context-local open during first-bind per CommandList)

CommandList::Destroy()
{
  // If DC still alive, almost always recycle:
  If (DC still alive)
 { IC::pfnRecycleDestroyCommandList }
  Else
 { IC::pfnDestroyCommandList }
  // Add to InterlockedSList
}

以下状态图显示了即时上下文 DDI 命令列表句柄的有效性。 绿色状态代表一个句柄,可与 CommandListExecute 一起使用。

说明即时上下文 DDI 命令列表句柄有效性状态的示意图。