DXGI 呈现路径

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

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

DWM 开启时的窗口模式

在桌面窗口管理器(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 可以在 hSurfaceToPresentSrcSubResourceIndex 成员DXGI_DDI_ARG_PRESENT中指定任何应用程序创建的后退缓冲区。 没有额外的共享界面。

全屏模式

全屏模式比 DWM 打开或关闭时的窗口模式更为复杂。

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

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

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

  • 应用程序指定它不能接受 Direct3D 运行时丢弃后台缓冲区的内容,并且请求整条链中只包含一个缓冲区(总计)。 (在这种情况下,DXGI 分配一个背面缓冲区和一个主缓冲区,但是,DXGI 使用驱动程序的 PresentDXGI 函数,并设置 Blt 标志。

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

为了通知用户模式显示驱动程序驱动程序可以选择不进行扫描输出,驱动程序将会收到关于可选和非可选类别的扫描输出表面的资源创建调用。 可选的屏幕输出表面由DXGI_DDI_PRIMARY_OPTIONAL标志指定。 非可选的输出图面没有设置DXGI_DDI_PRIMARY_OPTIONAL标志。 有关这些类型的资源创建调用的详细信息,请参阅 在资源创建时传递 DXGI 信息

DXGI 将 DXGI_DDI_PRIMARY_OPTIONAL 标志设置为创建所有后缓冲表面(即可选的表面),并且不为任何前缓冲表面或代理表面(即非可选的表面)设置该标志。

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

如果未为前缓冲区或代理图面设置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 将指定驱动程序执行非解析(可能为颜色转换)拉伸位。 然后,驱动程序应复制(无需解析)多重采样到前端缓冲区,如果驱动程序选择扫描 MSAA,则这是必要的。