在 Direct3D 10 基础上的更改

本部分仅适用于 Windows 7 及更高版本以及 Windows Server 2008 R2 及更高版本的 Windows 操作系统。

以下部分介绍了 Direct3D 11 与 Direct3D 10 的更改。

Kernel-Mode服务的驱动程序回调函数

当运行时调用用户模式显示驱动程序的 CreateDevice (D3D10) 函数时,Direct3D 版本 11 运行时在 D3DDDI_DEVICECALLBACKS 结构中提供的特定于设备的回调函数将驱动程序与内核句柄和内核函数签名隔离开来。 Direct3D 版本 11 运行时更改了回调语义,因此,回调函数的实现支持自由线程操作模式,而以前的 Direct3D 版本运行时不支持自由线程操作模式。 在驱动程序指示它支持自由线程模式 (D3D11DDICAPS_FREETHREADED) 后,适用于自由线程模式操作的规则;否则,将应用以前严格限制的规则。 有关驱动程序如何指示对自由线程模式的支持的信息,请参阅 线程和命令列表。 Direct3D 版本 11 仍存在以下限制:

  • 一次只能有一个线程对 HCONTEXT 工作。 当前使用 HCONTEXT 的现有回调函数是 pfnPresentCbpfnRenderCbpfnEscapeCbpfnDestroyContextCbpfnWaitForSynchronizationObjectCbpfnSignalSynchronizationObjectCb。 因此,如果多个线程调用这些回调函数并使用相同的 HCONTEXT,则驱动程序必须同步对回调函数的调用。 满足此要求是很自然的,因为这些回调函数可能仅从操作即时上下文的线程调用。

  • 驱动程序必须仅在调用以下驱动程序函数期间使用调用这些驱动程序函数的相同线程来调用以下回调函数:

  • pfnDeallocateCb 回调函数应具有特殊提及,因为驱动程序不需要在驱动程序从其 DestroyResource (D3D10) 函数返回之前调用 pfnDeallocateCb,适用于大多数资源类型。 由于 DestroyResource (D3D10) 是一个自由线程函数,因此驱动程序必须推迟对对象的销毁,直到驱动程序可以有效地确保没有现有即时上下文引用保留 (也就是说,驱动程序必须在 pfnDeallocateCb) 之前调用 pfnRenderCb 此限制甚至适用于共享资源或使用 HRESOURCE 来补充 pfnAllocateCb 的 HRESOURCE 用法的任何其他回调函数。 但是,此限制不适用于主数据库。 有关主要异常的详细信息,请参阅 主要异常。 由于某些应用程序可能需要同步销毁的外观,因此驱动程序必须确保在调用其 Flush (D3D10) 函数期间,针对以前销毁的任何共享资源调用 pfnDeallocateCb。 驱动程序还必须清除以前销毁的任何对象, (那些在调用其 Flush (D3D10) 函数期间不会停止管道) 的对象;驱动程序必须这样做,以确保运行时调用 Flush (D3D10) 作为官方机制来清理可能需要此类机制的少数应用程序的延迟销毁对象。 有关此机制的详细信息,请参阅 延迟销毁和刷新 (D3D10) 。 驱动程序还必须确保在驱动程序的 DestroyDevice (D3D10) 函数在清理期间返回之前完全销毁延迟销毁的任何对象。

弃用允许修改Free-Threaded DDI 的功能

对于 Direct3D 版本 11,显示设备的 API 级概念和即时上下文仍由显示设备的旧概念在 DDI 级别捆绑在一起。 这种将显示设备和即时上下文捆绑在一起,可最大程度地提高与以前版本 DDI ((例如 Direct3D 版本 10 DDI) )的兼容性,并在通过多个版本的 DDI 支持多个版本的 API 时减少驱动程序改动。 但是,这种显示设备和即时上下文的捆绑会导致更混乱的 DDI,因为线程域不是非常明确的。 相反,若要了解多个接口的线程要求以及这些接口中的函数,驱动程序开发人员必须参考文档。

Direct3D 版本 11 API 的主要功能是允许多个线程同时进入创建和销毁函数。 此功能与允许驱动程序交换用于创建和销毁的函数表指针不兼容,因为允许在 D3D10DDI_DEVICEFUNCSD3D10_1DDI_DEVICEFUNCS 中指定的函数使用 Direct3D 版本 10 DDI 语义。 因此,在驱动程序传回 creates (CreateDevice (D3D10) ) 的函数指针后,当驱动程序在 Direct3D 版本 11 DDI 下运行时以及驱动程序支持 DDI 线程时,驱动程序不应尝试通过修改这些特定函数指针来更改行为。 此限制适用于以 pfnCreate、pfnOpenpfnDestroypfnCalcPrivatepfnCheck 开头的所有设备函数。 设备的所有其余功能都与即时上下文紧密关联。 由于单个线程一次操作即时上下文,因此明确定义为继续允许驱动程序热交换即时上下文函数表条目。

pfnRenderCb Vss pfnPerformAmortizedProcessingCb

Direct3D 版本 10 API 函数挂钩 Direct3D 运行时的 pfnRenderCb 内核回调函数以执行摊销处理 (也就是说,驱动程序不会对每个 API 函数调用执行某些操作,而是对) 的每个 API 函数调用执行摊销操作。 API 通常利用此机会来剪裁高水印和刷新其延迟的对象销毁队列等。

为了使内核回调函数尽可能为驱动程序提供自由线程,当驱动程序支持 Direct3D 版本 11 DDI 时,Direct3D API 不再使用 pfnRenderCb 。 因此,支持 Direct3D 版本 11 DDI 的驱动程序必须从进入驱动程序 DDI 函数的同一线程手动调用 pfnPerformAmortizedProcessingCb 内核回调函数,该线程在驱动程序提交即时上下文 (或类似频率) 的命令缓冲区。 由于操作应剪裁高水印,因此在利用 状态刷新 DDI 回调函数时,在驱动程序生成命令缓冲区前序之前执行此操作是有利的。

此外,驱动程序应注意 API 摊销问题,并尝试平衡其使用 pfnPerformAmortizedProcessingCb 内核回调函数的频率。 在极端方面,驱动程序可能会导致过度处理。 例如,如果驱动程序始终 (背靠背) 调用 pfnPerformAmortizedProcessingCb 两次(可能是由于使用多引擎),则驱动程序只调用 pfnPerformAmortizedProcessingCb 一次会更高效。 另一个极端是,如果驱动程序从未调用 pfnPerformAmortizedProcessingCb,驱动程序可能不允许 Direct3D API 对整个帧执行任何工作,这可能是由于交替的帧呈现设计造成的。 驱动程序不需要调用 pfnPerformAmortizedProcessingCb 的频率比它自然更频繁,因为这 (是过度杀人,例如,如果驱动程序在 1 毫秒的时间范围内没有调用 pfnPerformAmortizedProcessingCb ,则必须需要抽水 API) 。 驱动程序需要仅确定哪些现有 pfnRenderCb 调用应附带 pfnPerformAmortizedProcessingCb ,并且自然符合操作的线程语义。

对于支持命令列表的驱动程序,这些驱动程序还必须从延迟的上下文中调用 pfnPerformAmortizedProcessingCb ,只要这些驱动程序用完空间 (与每个即时上下文刷新) 的频率相似。 Direct3D 版本 11 运行时至少希望在此类操作期间剪裁其高水印。 由于与 pfnRenderCb 相关的线程语义在 Direct3D 版本 11 中已放宽,因此必须解决并发问题,以便 Direct3D 版本 11 可以不受限制地继续挂钩 pfnRenderCb

新的 DDI 错误代码

创建D3DDDIERR_APPLICATIONERROR错误代码以允许驱动程序参与验证,而 Direct3D 版本 11 API 未参与验证。 以前,如果驱动程序返回E_INVALIDARG错误代码,则会导致 API 引发异常。 调试层的存在将导致调试输出,并指示驱动程序已返回内部错误。 调试输出会向开发人员建议驱动程序存在 bug。 如果驱动程序返回D3DDDIERR_APPLICATIONERROR,调试层将确定应用程序出现故障。

以追溯方式要求自由线程 CalcPrivate DDI

以追溯方式,Direct3D 版本 11 要求在 Direct3D 版本 10 DDI 函数上以 pfnCalcPrivate 开头的驱动程序函数为自由线程。 此追溯要求与 Direct3D 版本 11 DDI 的行为匹配,以始终要求 pfnCalcPrivate*pfnCalcDeferredContextHandleSize 函数成为自由线程,即使驱动程序指示它不支持 DDI 线程处理也是如此。 有关此追溯要求的详细信息,请参阅 追溯性要求Free-Threaded CalcPrivate DDI

延迟销毁和刷新 D3D10

由于现在所有销毁函数都是自由线程的,因此 Direct3D 运行时无法在销毁期间刷新命令缓冲区。 因此,destroy 函数必须延迟对象的实际销毁,直到驱动程序可以确保操作即时上下文的线程不再依赖于该对象才能生存。 每个离散的即时上下文方法都不能有效地使用同步来解决此销毁问题:因此,驱动程序应仅在刷新命令缓冲区时使用同步。 Direct3D 运行时在必须处理类似问题时也使用此相同设计。

由于批准延迟销毁,Direct3D 运行时主张那些不能容忍延迟销毁解决方法的应用程序改用显式机制。 因此,即使命令缓冲区 (为空) ,驱动程序也必须在调用其 Flush (D3D10) 函数期间处理延迟销毁队列,以确保这些机制实际起作用。

需要某种形式的同步销毁的应用程序必须使用以下模式之一,具体取决于它们需要的销毁的重量级:

  • 在应用程序确保释放该对象的所有依赖项 (即命令列表、视图、中间软件等) 后,应用程序使用以下模式:

    Object::Release(); // Final release
    ImmediateContext::ClearState(); // Remove all ImmediateContext references as well.
    ImmediateContext::Flush(); // Destroy all objects as quickly as possible.
    
  • 以下模式是更严重的销毁:

    Object::Release(); // Final release
    ImmediateContext::ClearState(); // Remove all ImmediateContext references as well.
    ImmediateContext::Flush();
    ImmediateContext::End( EventQuery );
    while( S_FALSE == ImmediateContext::GetData( EventQuery ) ) ;
    ImmediateContext::Flush(); // Destroy all objects, completely.
    

主要异常

主要资源是运行时在调用驱动程序的 CreateResource (D3D11) 函数时创建的资源。 运行时通过将 D3D11DDIARG_CREATERESOURCE 结构的 pPrimaryDesc 成员设置为指向 DXGI_DDI_PRIMARY_DESC 结构的有效指针来创建主数据库。 对于上述从 Direct3D 10 到 Direct3D 11 的更改,主数据库具有以下值得注意的例外:

  • 驱动程序的 CreateResource (D3D11) DestroyResource (D3D10) 函数都不是自由线程的,它们共享直接上下文线程域。 以 pfnCreatepfnDestroy 开头的函数仍可存在并发性,其中包括 CreateResource (D3D11) 和 DestroyResource (D3D10) 。 但是, createResource (D3D11) DestroyResource (D3D10) 不能存在并发。 例如,驱动程序可以检测对其 CreateResource (D3D11) 或 DestroyResource (D3D10) 函数的调用是否适用于主函数,从而确定它可以在函数调用期间安全地使用或触摸即时上下文内存。

  • 主销毁不能由 Direct3D 运行时延迟,并且驱动程序必须在调用驱动程序的 DestroyResource (D3D10) 函数内适当调用 pfnDeallocateCb 函数。