等待VBLANK在窗口模式下呈现在Windows 10上的正确方法是什么?目前我正在做以下事情:
D3DKMTWaitForVerticalBlankEvent(&waitData);
swapchain->Present(0, 0);
然而,这会导致偶尔的口吃。我目前关于口吃的理论如下:
DWM在VBLANK事件之前的某个时间完成合成,然后在VBLANK间隔期间,DWM尝试尽可能快地写入前端缓冲区。因此,如果在VBLANK间隔中调用Present
,则可能在DWM写入前缓冲区后发生。这是我头脑中的画面:
因此,更正确的方法是在VBLANK之前调用Present(1, 0)
以使用DWM对窗口的后缓冲区进行排队。然后调用D3DKMTWaitForVerticalBlankEvent
等待下一个VBLANK。那就是:
swapchain->Present(1, 0);
D3DKMTWaitForVerticalBlankEvent(&waitData);
我的问题是: 在VBLANK间隔和中显示的正确方法是什么 我的理论是正确的还是有更复杂/更简单的事情发生? 还有什么资源可以了解这种互动的更多信息吗?
潜在有用的链接:
MSDN Present
DXGI Best Practices
DXGI Waitable Swap Chain
D3DKMTWaitForVerticalBlankEvent
修改 不确定这应该是答案还是编辑。 经过一些谷歌搜索后,我发现this explanation by Nicholas Steel似乎与我的理论一致
没有确定的火灾方法可以准确地检测VBlank何时发生 因为Windows没有暴露VBlank中断,并显示/ GPU 无论如何都不一定需要生成一个(此外, '当前扫描线'例如由...给出的信息 IDirect3DDevice9 :: GetRasterStatus可能不准确)。结果是, 程序通常会轮询VBlank或依赖Direct3D / OpenGL来执行此操作 对他们来说。
程序在VBlank期间显示视频帧以避免撕裂,因为 显示器将愉快地切换到新画面中画。随着 这些是Windows Vista和更高版本Windows中的compositor 程序仍会检测到VBlank并且只在其中显示帧, 因为他们认为他们正在直接呈现视频帧 现实中,视频帧首先进入合成器。框架 发送到compositor(来自PC上任何正在运行的程序)都将 由合成器排队,并合并在一起进行交换/复制 在VBlank期间到位。
此系统可能出现的问题:
1)VBlank的程序轮询可能会错过合成。这将导致 要排队等待下一个组合的帧,意思是 前一帧将显示两倍的长度。
2)更糟糕的是,下一帧可能不会错过构图,并最终结束 覆盖以前排队的帧 - 所以你最终得到了一个 重复帧后跟跳帧。
3)程序的VSync实现自然无法检测到 VBlank(持续时间很短),导致它等到 下一个VBlank和风险问题1和/或2.
4)这些问题甚至可能结合在一起产生一场完美的风暴'的 重复和/或遗漏的框架。正如你所看到的,这种轮询设置并不理想,而且当a 合成器存在。有许多问题可能会导致问题 新视频帧无法显示,导致前一帧 显示时间超过预期并可能跳过新的 完全是框架!
解决方案是使用合成器而不是合成器。 立即和之后呈现新的视频帧,调用命令 这将阻止直到合成器完成它的任务 (DwmFlush)。这将确保最多1个新视频帧 在每个VBlank时期之间呈现给合成器。只要了 合成器是活跃的,你也不必担心轮询 VBlank自己了。
所以最好的办法是:
if (DWM.isEnabled())
{
present();
DwmFlush();
}
else
{
waitForVBlank();
present();
}
另一个编辑:
对于未来的读者,还有另外两种等待VBLANK的方法,我不知道他们是IDXGIOutput::WaitForVBlank
和IDirectDraw7::WaitForVerticalBlank
后者被弃用了。
答案 0 :(得分:1)
为什么要在窗口模式下等待VBLANK? DWM将确保合成曲面将在VBLANK区域中翻转。
当你说'#34; DWM在VBLANK事件之前的某个时间完成合成"时,什么会使DWM启动合成?在任何应用程序呈现之后,或者在关注的应用程序出现之后,它不会发生。组合实际上在VBLANK发生时开始。它可能会完成并翻转VBLANK中的合成曲面,或者最终可能会在下一个VBLANK中翻转(因此您可能会在延迟时间内延迟额外的帧持续时间)。但是,这并不在你的控制范围内 - 这取决于合成的持续时间,而不取决于你的应用程序的存在。
在一个带窗口的应用中,当你打电话给礼物时,它只是翻转你应用的交换链 - 它不会翻转显示的表面。 DWM将处理这个问题。所以"只要合成器处于活动状态,你就不必担心自己的VBlank轮询了。"
然而,使用DwmFlush()来实现"在每个VBlank周期之间向合成器呈现最多1个新视频帧。不一定是可取的。除非你肯定只想在每个监视周期显示1帧,否则你可以这样做(虽然我不确定DwmFlush()是否是最优雅的方法)。但在大多数情况下,我并不认为强制执行该限制是可取的。