DXGI 呈现路径

DXGI 为应用程序提供了一种“工作正常”的演示方法。例如,应用程序不需要执行任何特殊操作即可在窗口模式和全屏模式之间转换。 这种演示方法之所以可行,是因为 DXGI 和用户模式显示驱动程序协同工作,以跨多个示例抗锯齿 (MSAA) 、监视器旋转、大小和格式的后端和前端缓冲区差异以及全屏模式与窗口模式的组合保留演示文稿。 DXGI 的另一个优点是,它允许显示适配器具有有限的扫描 MSAA 和旋转表面的能力,因为 DXGI 提供“无状态”DDI。 在无状态 DDI 中,适配器的驱动程序不需要记录跨 DDI 调用的数据。

演示的基本任务是将数据从呈现的后台缓冲区移动到主图面以供查看。 此任务在以下各节中所述的不同情况下执行。

打开 DWM 的窗口模式

在桌面 Windows 管理器 (DWM) 的窗口模式中,DXGI 与 DWM 通信,并打开共享资源的视图,该共享资源是 DXGI 生成者的呈现目标和 DWM 的纹理。 除了应用程序创建的任何后台缓冲区之外,此共享资源也存在。 DXGI 调用驱动程序的 BltDXGI 函数,将数据从任何后台缓冲区移动到共享图面。 此操作可能需要拉伸、颜色转换和 MSAA 解析。 但是,此操作从不需要源和目标子矩形。 事实上,这些子矩形不能在调用 BltDXGI 中表示。 此位块传输 (bitblt) 始终在 pBltData 参数指向的 DXGI_DDI_ARG_BLT 结构的 Flags 成员中设置 Present 标志。 设置 Present 标志指示驱动程序应以原子方式执行操作。 驱动程序以原子方式执行 bitblt 操作,以最大程度地减少在 DWM 读取共享资源进行组合时撕裂的可能性。

关闭 DWM 的窗口模式

在采用 DWM 关闭的窗口模式中,DXGI 调用驱动程序的 PresentDXGI 函数,该函数在 pPresentData 参数指向的 DXGI_DDI_ARG_PRESENT 结构的 Flags 成员中设置了 Blt 标志。 在此 PresentDXGI 调用中,DXGI 可以在 DXGI_DDI_ARG_PRESENT的 hSurfaceToPresentSrcSubResourceIndex 成员中指定应用程序创建的任何后台缓冲区。 没有额外的共享图面。

全屏模式

与打开或关闭 DWM 的窗口模式相比,全屏情况更为复杂。

当 DXGI 转换为全屏模式时,它会尝试利用翻转操作来减少带宽并获得垂直同步。 以下条件可能会阻止使用翻转操作:

  • 应用程序没有以与主图面匹配的方式重新分配其后台缓冲区。

  • 驱动程序指定它不会扫描后台缓冲区 (例如,因为后台缓冲区已旋转或 MSAA) 。

  • 应用程序指定它不能接受 Direct3D 运行时放弃后台缓冲区的内容,并且只请求一个缓冲区 (链中的总) 。 (在本例中,DXGI 分配一个背面和一个主图面;但是,DXGI 使用驱动程序的 PresentDXGI 函数和 Blt 标志 set.)

如果出现上述条件之一,从而阻止翻转操作,并且调用具有 Blt 标志的驱动程序的 PresentDXGI 函数也不适合 (因为后台缓冲区与前缓冲区完全) 不匹配,DXGI 将分配代理图面。 此代理图面与前端缓冲区匹配。 因此,可以在代理图面和前缓冲区之间翻转。 如果代理图面存在,DXGI 将使用驱动程序的 BltDXGI 函数, (0) 清除了 Present 标志,将应用程序的后台缓冲区复制到代理图面。 在此 BltDXGI 调用中,DXGI 可能会请求转换、拉伸和解析。 然后,DXGI 调用驱动程序的 PresentDXGI 函数,并在 DXGI_DDI_ARG_PRESENT 结构的 Flags 成员中设置 Flip 标志,以移动代理图面位进行扫描。

若要通知用户模式显示驱动程序驱动程序可以选择退出扫描,驱动程序将接收针对可选和非可选扫描输出表面类的资源创建调用。 可选的扫描输出图面由DXGI_DDI_PRIMARY_OPTIONAL标志指定。 非可选扫描输出图面未设置DXGI_DDI_PRIMARY_OPTIONAL标志。 有关这些类型的资源创建调用的详细信息,请参阅 在资源创建时传递 DXGI 信息

DXGI 设置DXGI_DDI_PRIMARY_OPTIONAL标志以创建所有后台缓冲区图面, (即) 可选图面,并且不会为任何前缓冲区或代理图面 ((即非可选 surface) )设置标志。

如果为后台缓冲区设置了DXGI_DDI_PRIMARY_OPTIONAL,驱动程序可以设置DXGI_DDI_PRIMARY_DRIVER_FLAG_NO_SCANOUT标志。 有关设置此标志的详细信息,请参阅 在资源创建时传递 DXGI 信息。 如果驱动程序为可选缓冲区设置DXGI_DDI_PRIMARY_DRIVER_FLAG_NO_SCANOUT,则除了导致 DXGI 调用驱动程序的 PresentDXGI 函数外,没有其他效果,该函数设置了 Blt 标志,而不是 Flip 标志集

如果未为前端缓冲区或代理图面设置DXGI_DDI_PRIMARY_OPTIONAL,驱动程序仍可以通过失败资源创建调用来选择退出扫描,并DXGI_DDI_ERR_UNSUPPORTED错误代码并设置DXGI_DDI_PRIMARY_DRIVER_FLAG_NO_SCANOUT。

注意 未设置DXGI_DDI_PRIMARY_DRIVER_FLAG_NO_SCANOUT创建调用失败会保留给实际故障情况,例如内存不足。

DXGI 在尝试为 MSAA 或旋转后退缓冲区创建全屏演示链时,会利用这种选择退出方法。 如果驱动程序不会扫描其中任一类型或两种类型,驱动程序将选择退出。然后,DXGI 将尝试创建非旋转图面和/或非 MSAA 图面,直到驱动程序接受资源创建。 因此,DXGI 将逐渐回退,直到非可选图面与前端缓冲区格式、样本计数、旋转和大小完全匹配。

如果驱动程序选择退出任何非可选图面,DXGI 仍必须有一种方法将位从后台缓冲区移动到主图面。 因此,如果驱动程序选择退出 MSAA 和旋转扫描,则当 DXGI 调用驱动程序的 BltDXGI 函数时,驱动程序会选择加入解析和/或旋转。 当驱动程序选择退出时,DXGI 将创建一个代理图面,并调用 BltDXGI 将数据从后台缓冲区移动到该代理图面。 驱动程序应该没有理由选择退出此代理图面,因为代理与前缓冲区完全匹配。

当应用程序在进入或退出全屏模式后不重新创建其图面时,会出现以下异常情况:

  • 如果应用程序在进入全屏模式时不重新创建其图面,DXGI 会确定后台缓冲区与前缓冲区不匹配,即使它们确实与格式、大小、旋转和样本计数匹配。 此决定的原因是操作系统要求在创建这些缓冲区时标记后台缓冲区以便扫描到特定监视器。 窗口式后台缓冲区尚不能明确分配给特定监视器,因为当进入全屏时,监视器是动态选择的。 因此,DXGI 不得通过翻转操作) 将这些后台缓冲区发送到驱动程序进行扫描 (。 此类型的应用程序通常强制 DXGI 创建代理图面。

  • 如果应用程序在返回到窗口模式时不重新创建其后台缓冲区,DXGI 可能会调用驱动程序的 BltDXGIPresentDXGI (并设置 Blt) ,以在先前为翻转操作创建的图面上执行 bitblt。 这种情况不应是一个问题,但为了完整性,此处会提及。 请注意,当应用程序转换为窗口模式时,DXGI 始终会销毁代理图面。

另请注意,当应用程序处于全屏模式时,应用程序可以动态调整其后台缓冲区的大小。 此操作会导致上述情况中描述的逻辑再次发生。 因此,代理图面可能会创建和销毁,即使应用程序保持全屏模式,也可能需要随着时间的推移选择退出。 应用程序还可以将其输出动态传输到另一台监视器,而无需退出全屏模式。 因此,应用程序会导致切换回 bitblt 模式,因为应用程序的后台缓冲区已针对不同的监视器进行了标记。

最后,如果驱动程序不选择退出 MSAA 扫描,则应注意 MSAA 后台缓冲区出现的情况。在这种情况下,驱动程序选择退出 MSAA 扫描。 因此,DXGI 通过翻转操作交换 MSAA 后台缓冲区和 MSAA 前缓冲区,并通过等效于数模转换器 (DAC) 执行解析操作。 在这种情况下,应用程序可以在全屏模式下动态调整其后台缓冲区的大小,这会强制 DXGI 切换到调用驱动程序的 BltDXGI 函数。 由于后端缓冲区和前缓冲区的 MSAA 特征仍然匹配,因此 DXGI 将指定驱动程序执行非解析(可能为颜色转换)拉伸 bitblt。 然后,驱动程序应在不解析的情况下将多重采样复制到前端缓冲区,如果驱动程序选择扫描 MSAA,则这是必需的。