本部分仅适用于 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 的现有回调函数是 pfnPresentCb、 pfnRenderCb、 pfnEscapeCb、 pfnDestroyContextCb、 pfnWaitForSynchronizationObjectCb 和 pfnSignalSynchronizationObjectCb。 因此,如果多个线程调用这些回调函数并使用相同的 HCONTEXT,驱动程序必须将调用同步到回调函数。 满足此要求十分自然,因为这些回调函数可能只从操作即时上下文的线程调用。
驱动程序必须使用调用这些驱动程序函数的相同线程,仅在调用以下驱动程序函数期间调用以下回调函数:
pfnAllocateCb:创建共享资源时,驱动程序必须在调用驱动程序的 CreateResource(D3D11) 函数的线程上调用 pfnAllocateCb。 与设备进行常规的非共享分配是完全自由线程的。
pfnPresentCb:驱动程序必须在调用驱动程序的 PresentDXGI 函数期间调用 pfnPresentCb。
驱动程序必须仅在调用SetDisplayModeDXGI函数期间调用pfnSetDisplayModeCb。
pfnRenderCb:驱动程序必须在调用驱动程序 Flush(D3D10)函数的线程上调用 pfnRenderCb。 由于 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 的主要功能是,它允许多个线程同时输入创建和销毁函数。 这种功能与允许驱动程序替换用于创建和销毁的函数表指针不兼容,这与 Direct3D 版本 10 DDI 中在 D3D10DDI_DEVICEFUNCS 和 D3D10_1DDI_DEVICEFUNCS中指定的函数的语义相矛盾。 因此,在驱动程序传回用于创建(CreateDevice(D3D10)的函数指针之后,驱动程序不应尝试通过修改这些特定函数指针来更改行为,该驱动程序在 Direct3D 版本 11 DDI 下运行,而驱动程序则支持 DDI 线程。 此限制适用于从 pfnCreate、pfnOpen、pfnDestroy、pfnCalcPrivate 和 pfnCheck 开始的所有设备函数。 其余所有设备功能都与即时上下文紧密关联。 由于单个线程一次操控即时上下文,因此可以继续允许驱动程序热插拔即时上下文函数表项。
pfnRenderCb 对比 pfnPerformAmortizedProcessingCb
Direct3D 版本 10 API 函数通过挂钩 Direct3D 运行时的 pfnRenderCb 内核回调函数来执行分摊处理,也就是说,驱动程序不是为每次 API 函数调用执行某些操作,而是为若干次 API 函数调用分摊执行操作。 API 通常使用此机会调整高水印并清理其延迟对象销毁队列等。
为了让内核回调函数在驱动程序支持 Direct3D 版本 11 DDI 时尽可能实现自由线程,Direct3D API 不再使用 pfnRenderCb。 因此,支持 Direct3D 版本 11 DDI 的驱动程序必须从同一线程手动调用 pfnPerformAmortizedProcessingCb 内核回调函数,该线程在驱动程序在立即上下文(或类似频率)上提交命令缓冲区后输入驱动程序 DDI 函数。 由于该操作应当剪除高水位标记在驱动程序生成命令缓冲区前,为了更好地利用状态刷新 DDI 回调函数的优势,应提前进行此操作。
此外,驱动程序应注意 API 摊销问题,并尝试平衡它使用 pfnPerformAmortizedProcessingCb 内核回调函数的频率。 在一个极端时,驱动程序可能会导致过度处理。 例如,如果驱动程序总是连续调用两次pfnPerformAmortizedProcessingCb,可能是因为多引擎的使用,那么驱动程序只调用一次pfnPerformAmortizedProcessingCb 会更高效。 在另一种极端情况下,驱动程序可能不允许 Direct3D API 执行任何帧内工作,如果驱动程序未曾调用 pfnPerformAmortizedProcessingCb,这可能是由于交替帧渲染设计所致。 驱动程序不需要比自然情况下更频繁地调用 pfnPerformAmortizedProcessingCb,因为这样会过于繁琐(例如,如果驱动程序在 1 毫秒时间范围内没有调用 pfnPerformAmortizedProcessingCb,那么可能需要激活 API)。 驱动程序只需要确定哪些现有的 pfnRenderCb 调用应附带 pfnPerformAmortizedProcessingCb 调用,并自然地符合该操作的线程语义。
对于支持命令列表的驱动程序,每当这些驱动程序耗尽空间时,这些驱动程序还必须从延迟上下文调用 pfnPerformAmortizedProcessingCb (与每次即时上下文刷新类似的频率)。 Direct3D 版本 11 运行时预期至少在此类操作期间降低其峰值。 由于与 pfnRenderCb 相关的线程语义在 Direct3D 版本 11 中已经放宽,因此必须解决并发问题,以便 Direct3D 版本 11 能够继续无限制地挂钩 pfnRenderCb。
新的 DDI 错误代码
创建D3DDDIERR_APPLICATIONERROR错误代码是为了在Direct3D版本11的API无法进行验证时,让驱动程序能够进行验证。 以前,如果驱动程序返回了E_INVALIDARG错误代码,则会导致 API 引发异常。 调试层的存在会产生调试输出,并表明驱动程序已返回一个内部错误。 调试输出会表明给开发人员,驱动程序存在问题。 如果驱动程序返回D3DDDIERR_APPLICATIONERROR,则调试层会确定应用程序出错,而不是其他原因。
以追溯方式要求自由线程 CalcPrivate DDI
Direct3D 版本 11 追溯要求从 Direct3D 版本 10 DDI 函数上的 pfnCalcPrivate 开始的驱动程序函数进行自由线程处理。 此追溯要求与 Direct3D 版本 11 DDI 的行为一致,始终要求 pfnCalcPrivate* 和 pfnCalcDeferredContextHandleSize 函数支持自由线程,即便驱动程序表明不支持 DDI 线程。 有关此追溯要求的详细信息,请参阅 溯及要求 Free-Threaded CalcPrivate DDI。
延迟销毁和刷新 D3D10
由于所有销毁函数现在都是自由线程的,因此 Direct3D 运行时在销毁期间无法刷新命令缓冲区。 因此,销毁函数必须延迟对象的实际销毁,直到驱动程序可以确保作即时上下文的线程不再依赖于该对象来生存。 每个离散的即时上下文方法都无法使用同步来解决此销毁问题;因此,驱动程序应仅在刷新命令缓冲区时使用同步。 当 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) 函数都不是自由线程的,它们共享即时上下文线程域。 并发仍可以存在以 pfnCreate 和 pfnDestroy 开头的函数,其中包括 CreateResource(D3D11) 和 DestroyResource(D3D10)。 但是,主要资源的 CreateResource(D3D11) 和 DestroyResource(D3D10) 无法存在并发性。 例如,驱动程序可以检测到对其 CreateResource(D3D11) 或 DestroyResource(D3D10) 函数的调用适用于主数据库,从而确定它在函数调用期间可以安全地使用或触摸即时上下文内存。
主销毁无法被 Direct3D 运行时延迟,因此驱动程序在调用其 DestroyResource(D3D10) 函数时,必须正确调用 pfnDeallocateCb 函数。