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 模型 向 DWM 呈现的 DXGI 翻转模型
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 成员指定为 1 的 DXGI_SAMPLE_DESC 结构的 Count成员,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,指示序列的开头
  • 模式更改:窗口模式更改为全屏或全屏或全屏过渡

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

  • 当应用在每个 vsync 上显示时,PresentRefreshCount 等于 SyncRefreshCount
  • SyncRefreshCount 是在提交存在时在 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 结构,以存储 IDXGISwapChain::GetLastPresentCount 返回的所有成功提交的 IDXGISwapChain1::P resent1 的当前 (ID) 和关联的计算/预期的 PresentRefreshCount 值。

  3. 检测故障:

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

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

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

      注意

      如果故障包含大量帧,请调用 IDXGISwapChain1::P resent1 ,并将 Flags 参数设置为 DXGI_PRESENT_RESTART 放弃并跳过所有未完成的排队礼物。

       

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

从帧演示中的故障恢复的示例方案的插图

在示例方案中,预期帧 A 以 1 的 vsync 计数在屏幕上显示。 但实际上,你检测到帧 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 显示两个蓝色帧,然后显示绿色帧。 可以看到,此故障发生在读取现有统计信息时。

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