DXGI 翻转模型

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

比较 DXGI 翻转模型和 BitBlt 模型

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

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

当应用处于窗口模式时,DXGI 翻转模型的性能改进非常重要。 此表中的序列和插图比较了选择翻转模型与 bitblt 模型的窗口化应用的内存带宽使用情况和系统读取和写入。

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

 

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

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

如何使用 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 结构的 计数 成员,质量 成员 DXGI_SAMPLE_DESC 为零,因为不支持多个样本抗锯齿(MSAA)

如果在 Windows 7 或更早版本的作系统上使用 DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,则设备创建会失败。 使用翻转模型时,可以在窗口模式下使用全屏显示统计信息。 全屏行为不受影响。 如果将 NULL 传递给 IDXGIFactory2::CreateSwapChainForHwndpFullscreenDesc 参数,并将 SwapEffect 设置为 DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,运行时将创建一个额外的后退缓冲区,并旋转任何句柄属于呈现时成为前缓冲区的缓冲区。

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

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

DXGI 翻转模型应用的帧同步

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

对于翻转模型存在的统计信息,IDXGISwapChain::GetFrameStatistics 在这些情况下返回 DXGI_ERROR_FRAME_STATISTICS_DISJOINT

  • 首次调用 GetFrameStatistics,指示序列的开头
  • 模式更改:窗口模式或从全屏模式切换为全屏或全屏切换

PresentRefreshCountSyncRefreshCountSyncQPCTime 成员 DXGI_FRAME_STATISTICS 的值具有以下特征:

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

IDXGISwapChain::GetLastPresentCount 方法返回最后一个当前计数,即与交换链关联的显示设备发出的上次成功 IDXGISwapChain 1::P resent1 调用的当前 ID。 此当前 ID 是 DXGI_FRAME_STATISTICS 结构的 PresentCount 成员的值。 对于 bitblt 模型交换链,而在开窗模式下,所有 DXGI_FRAME_STATISTICS 值均为零。

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

执行以下步骤以避免、检测和从帧演示文稿中的故障中恢复:

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

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

  3. 检测故障:

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

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

    • 在此对 idXGISwapChain1::P resent1的调用数中,将 0 传递给SyncInterval 参数,以放弃并跳过此帧数。

      注意

      如果故障由大量帧组成,则调用 idXGISwapChain1::P resent1,并将 标志 参数设置为 DXGI_PRESENT_RESTART 放弃并跳过所有未完成的排队演示。

       

下面是从帧演示文稿中的故障中恢复的示例方案:

图示了从帧演示文稿中的故障恢复的示例方案

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

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

演示事件的时间线插图

此图说明了此序列:

  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 显示两个蓝色帧,然后显示绿色框架。 可以看到此故障发生在阅读当前统计信息时。

使用翻转模型、脏矩形和滚动区域增强演示文稿