DXGI 翻转模型

Windows 8 DXGI 1.2 中增加了对翻转演示模型及其关联的当前统计信息的支持。 Windows 8的 DXGI 翻转演示模型类似于 Windows 7 的Direct3D 9EX 翻转模式演示。 视频或基于帧速率的演示应用(如游戏)可以通过使用翻转演示模型受益最大。 使用 DXGI 翻转演示模型的应用可减少系统资源负载并提高性能。 应用还可通过翻转演示模型使用当前的统计信息增强功能,通过提供实时反馈和更正机制更好地控制呈现速率。

比较 DXGI 翻转模型和 BitBlt 模型

运行时使用位块传输 (位) 翻转演示模型,以在显示监视器上显示图形内容。 bitblt 和翻转演示模型之间的最大区别是,后缓冲区内容如何Windows 8 DWM 进行组合。 在 bitblt 模型中,每次调用 IDXGISwapChain1::P resent1 时,后端缓冲区的内容将复制到重定向图面上。 在翻转模型中,所有反向缓冲区与 DWM 桌面窗口管理器 (共享) 。 因此,DWM 可以直接从这些后端缓冲区进行组合,而无需执行任何其他复制操作。 一般情况下,翻转模型更高效。 翻转模型还提供更多功能,例如增强的现成统计信息。

如果旧组件使用 Windows 图形设备接口 (GDI) 直接写入 HWND,请使用 bitblt 模型。

当应用在开窗模式下时,DXGI 翻转模型的性能改进十分显著。 此表和插图中的序列比较了内存带宽使用情况,以及选择翻转模型和 bitblt 模型的开窗应用的系统读取和写入。

步骤 存在于 DWM 的 BitBlt 模型 向 DWM 呈现的 DXGI 翻转模型
1. 应用更新其帧 (写入)
应用更新其帧 (写入)
2. Direct3D 运行时将图面内容复制到 DWM 重定向 (读取、写入)
Direct3D 运行时将应用图面传递给 DWM
3. 共享图面复制完成后,DWM 将应用图面呈现在屏幕上, (读取、写入)
DWM 将应用图面呈现在屏幕上, (读取、写入)

blt 模型和翻转模型的比较图

翻转模型通过减少由 DWM 针对窗口框架组合的 Direct3D 运行时的读取和写入次数来减少系统内存使用量。

如何使用 DXGI 翻转模型

面向 Windows 8 的 Direct3D 11.1 应用通过使用 DXGI SWAP _ CHAIN _ _ DESC1结构的 SwapEffect 成员中设置的 DXGI _ SWAP EFFECT FLIP _ _ _ SEQUENTIAL枚举值创建交换链,从而使用翻转模型。 将 SwapEffect 设置为 DXGI _ SWAP EFFECT FLIP _ _ _ SEQUENTIAL 时,还要将 DXGI _ SWAP CHAIN _ _ DESC1 的这些成员设置为指示的值:

  • BufferCount 为介于 2 和 16 之间的值,以防止因等待 DWM 释放以前的演示缓冲区而降低性能。
  • 格式 为 DXGI _ FORMAT _ R16G16B16A16 _ FLOAT、DXGI _ FORMAT _ B8G8R8A8 _ UNORM 或 DXGI _ FORMAT _ R8G8B8A8 _ UNORM
  • SampleDesc 成员指定的 DXGI _ SAMPLE _ DESC结构的 Count 成员,并且 DXGI _ SAMPLE _ DESC 的质量成员为零,因为不支持多个示例抗锯 (MSAA)

如果在版本 7 或更早的操作系统Windows DXGI _ SWAP EFFECT FLIP _ _ _ SEQUENTIAL,设备创建将失败。 使用翻转模型时,可以在开窗模式下使用全屏显示统计信息。 全屏行为不受影响。 如果为开窗交换链将 NULL 传递给 IDXGIFactory2::CreateSwapChainForHwndpFullscreenDesc 参数,并且将 SwapEffect 设置为 DXGI _ SWAP EFFECT FLIP _ _ _ SEQUENTIAL, 则运行时将创建一个额外的反向缓冲区,并轮换任何句柄,该句柄属于呈现时成为前缓冲区的缓冲区。

使用翻转模型时,请考虑以下提示:

  • 每个 HWND使用一个翻转模型交换链。 不要将多个翻转模型交换链定向到同一 HWND
  • 请勿将翻转模型交换链与 GDI 的 ScrollWindowScrollWindowEx 函数一起使用。 某些 Direct3D 应用使用 GDI 的 ScrollWindowScrollWindowEx 函数在用户滚动事件发生后更新窗口内容。 ScrollWindowScrollWindowEx 在用户滚动窗口时执行屏幕上的窗口内容位。 这些函数还需要对 GDI 和 Direct3D 内容进行位模型更新。 使用任一函数的应用在应用进入开窗模式时,不一定显示在屏幕上滚动的可见窗口内容。 建议不使用 GDI 的 ScrollWindowScrollWindowEx 函数,而是在屏幕上重绘 GDI 和 Direct3D 内容以响应滚动。
  • 在其他 API(包括 DXGI 位blt 表示模型、其他版本的 Direct3D 或 GDI)中,使用 HWND 中的翻转模型。 由于 bitblt 模型维护了图面的其他副本,因此可以通过 Direct3D 和 GDI 中的零碎更新将 GDI 和其他 Direct3D 内容添加到同一 个 HWND。 使用翻转模型时,只有运行时传递给 DWM 的翻转模型交换链中的 Direct3D 内容可见。 运行时将忽略所有其他位模型 Direct3D 或 GDI 内容更新。

DXGI 翻转模型应用的帧同步

当前统计信息是媒体应用用于同步视频和音频流以及从视频播放故障中恢复的帧计时信息。 应用可以使用当前统计信息中的帧计时信息来调整其视频帧的呈现速率,以更流畅地呈现。 若要获取当前统计信息,请调用 IDXGISwapChain::GetFrameStatistics 方法以获取 DXGI _ FRAME _ STATISTICS 结构。 DXGI _FRAME _ STATISTICS 包含有关 IDXGISwapChain1::P resent1 调用的 统计信息。 翻转模型交换链以窗口模式和全屏模式提供当前统计信息。 对于开窗模式下的位网模型交换链,所有 DXGI _ FRAME _ STATISTICS 值都是零。

对于翻转模型显示统计信息 ,IDXGISwapChain::GetFrameStatistics在这些情况下返回 DXGI _ ERROR FRAME STATISTICS _ _ _ DISJOINT:

DXGI _ FRAME _ STATISTICS 的 PresentRefreshCount、SyncRefreshCountSyncQPCTime 成员中的值具有以下特征:

  • 当应用存在于每个 vsync 上时 ,PresentRefreshCount 等于 SyncRefreshCount。
  • SyncRefreshCount 是在提交 present 时在 vsync 间隔获取的 ,SyncQPCTime 大约是与 vsync 间隔关联的时间。

IDXGISwapChain::GetLastPresentCount方法返回上一个当前计数,即与交换链关联的显示设备上次成功的 IDXGISwapChain1::P resent1调用的当前 ID。 此当前 ID 是 DXGI _ FRAME _ STATISTICS结构的 PresentCount 成员的值。 对于 bitblt 模型交换链,在窗口模式下,所有 DXGI _ FRAME _ STATISTICS 值都是零。

避免、检测和从故障中恢复

执行以下步骤以避免、检测和从帧呈现中的故障中恢复:

  1. 队列 IDXGISwapChain1::P resent1 调用 (,即多次调用 IDXGISwapChain1::P resent1, 这会导致它们收集到队列) 。

  2. 创建一个 present-queue 结构,用于存储 IDXGISwapChain1::P resent1 由 IDXGISwapChain::GetLastPresentCount) 返回的所有成功提交的 (IDXGISwapChain1::P resent1当前 ID) 以及关联的计算/预期的 PresentRefreshCount 值。

  3. 检测问题:

    • 调用 IDXGISwapChain::GetFrameStatistics
    • 对于此帧,获取当前 ID (PresentCount) 和 vsync 计数,其中操作系统向监视器显示最后一个映像 (PresentRefreshCount) 。
    • 检索与当前 ID 关联且先前存储在 present-queue 结构中的预期 PresentRefreshCount。
    • 如果实际 PresentRefreshCount 晚于预期的 PresentRefreshCount, 则出现了一个故障。
  4. 若要从故障中恢复,

    • 计算从故障中恢复时要跳过的帧数。 例如,如果步骤 3 显示当前 ID (PresentCount) 的预期 vsync 计数 (PresentRefreshCount) 为 5,并且当前 ID 的实际 vsync 计数为 8,则从故障中恢复时要跳过的帧数为 3 帧。

    • 在调用 IDXGISwapChain1::P resent1时,将 0 传递给 SyncInterval 参数,以丢弃和跳过此帧数。

      备注

      如果问题由大量帧组成,请调用 IDXGISwapChain1::P resent1,Flags 参数设置为 DXGI PRESENT _ _ RESTART 以丢弃和跳过所有未完成的排队显示。

下面是从帧呈现中的故障中恢复的示例方案:

从帧演示中的故障中恢复的示例场景的插图

在示例方案中,你期望帧 A 在 vsync 计数为 1 的屏幕上显示。 但实际上,你检测到帧 A 在屏幕上显示为 4 的 vsync 计数。 因此,确定出现了一个故障。 然后可以丢弃 3 帧,也就是说,可以在对 IDXGISwapChain1::P resent1的 3 次调用中将 0 传递给 SyncInterval 参数。 在以上示例方案中,若要从故障中恢复,总共需要 8 个 IDXGISwapChain1::P resent1 调用。 然后,第 9 帧根据预期 vsync 计数可见。

下面是演示事件的时区。 每个垂直线表示一个 vsync。 水平方向是时间,向右增加。 可以使用此图来想象如何发生故障。

演示事件的时区示意图l

下图演示了此序列:

  1. 应用在 vsync 上唤醒,呈现蓝色,调用 IDXGISwapChain1::P resent1,然后返回到睡眠状态。

  2. 图形处理单元 (GPU) 从空闲状态唤醒,执行蓝色渲染,然后返回到睡眠状态。

  3. DWM 将下一个 vsync 唤醒,将蓝色组合到其后端缓冲区中,调用 IDXGISwapChain1::P resent1,然后返回到睡眠状态。

  4. 应用会唤醒、呈现绿色、调用 IDXGISwapChain1::P resent1,然后返回到睡眠状态。

    备注

    应用并发执行,而 GPU 执行蓝色组合。

  5. 接下来,GPU 为应用呈现绿色。

  6. 最后,DAC (模拟转换器) 下一个 vsync 上监视器上 DWM 组合的结果。

在时间线中,可以想象当前统计信息的延迟以及故障的发生情况。 例如,若要显示屏幕上出现绿色颜色的 DWM 问题,请想象一下扩大绿色/红色框,使绿色/红色框的右侧与紫色/红色框右侧匹配。 在此方案中,DAC 显示两个蓝色帧,然后显示绿色帧。 可以看到,读取当前统计信息时出现了这种问题。

使用翻转模型、脏矩形和滚动区域增强呈现