在窗口模式下在Windows 10上等待VBLANK的正确方法

时间:2018-03-12 21:12:55

标签: winapi directx direct3d dwm dxgi

等待VBLANK在窗口模式下呈现在Windows 10上的正确方法是什么?目前我正在做以下事情:

D3DKMTWaitForVerticalBlankEvent(&waitData);
swapchain->Present(0, 0);

然而,这会导致偶尔的口吃。我目前关于口吃的理论如下:
DWM在VBLANK事件之前的某个时间完成合成,然后在VBLANK间隔期间,DWM尝试尽可能快地写入前端缓冲区。因此,如果在VBLANK间隔中调用Present,则可能在DWM写入前缓冲区后发生。这是我头脑中的画面: bad

因此,更正确的方法是在VBLANK之前调用Present(1, 0)以使用DWM对窗口的后缓冲区进行排队。然后调用D3DKMTWaitForVerticalBlankEvent等待下一个VBLANK。那就是:

swapchain->Present(1, 0);
D3DKMTWaitForVerticalBlankEvent(&waitData);

有相应的图片: good 第二种方法似乎完美无缺,不再有任何口吃。

我的问题是: 在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::WaitForVBlankIDirectDraw7::WaitForVerticalBlank后者被弃用了。

1 个答案:

答案 0 :(得分:1)

为什么要在窗口模式下等待VBLANK? DWM将确保合成曲面将在VBLANK区域中翻转。

当你说'#34; DWM在VBLANK事件之前的某个时间完成合成"时,什么会使DWM启动合成?在任何应用程序呈现之后,或者在关注的应用程序出现之后,它不会发生。组合实际上在VBLANK发生时开始。它可能会完成并翻转VBLANK中的合成曲面,或者最终可能会在下一个VBLANK中翻转(因此您可能会在延迟时间内延迟额外的帧持续时间)。但是,这并不在你的控制范围内 - 这取决于合成的持续时间,而不取决于你的应用程序的存在。

在一个带窗口的应用中,当你打电话给礼物时,它只是翻转你应用的交换链 - 它不会翻转显示的表面。 DWM将处理这个问题。所以"只要合成器处于活动状态,你就不必担心自己的VBlank轮询了。"

然而,使用DwmFlush()来实现"在每个VBlank周期之间向合成器呈现最多1个新视频帧。不一定是可取的。除非你肯定只想在每个监视周期显示1帧,否则你可以这样做(虽然我不确定DwmFlush()是否是最优雅的方法)。但在大多数情况下,我并不认为强制执行该限制是可取的。