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 ChainD3DKMTWaitForVerticalBlankEvent
编辑:
不确定这应该是答案还是编辑.
经过一些谷歌搜索后,我发现this explanation by Nicholas Steel似乎与我的理论一致
There is no sure fire way to detect exactly when VBlank will occur
because Windows doesn’t expose a VBlank interrupt,and displays/GPU
aren’t necessarily required to generate one anyway (in addition,
‘current scanline’ information as given by e.g.
IDirect3DDevice9::GetRasterStatus may not be accurate). As a result,
programs generally poll for VBlank or rely on Direct3D/OpenGL to do it
for them.Programs present video frames during VBlank to avoid tearing,since
the monitor will happily switch to the new frame mid-draw. With the
compositor in Windows Vista and later versions of Windows,these
programs will still detect VBlank and only present frames during it,
as they think they are presenting video frames directly,when in
reality the video frames are Feeding into the compositor first. Frames
sent to the compositor (from any running programs on the PC) will be
queued up by the compositor,and merged together to be swapped/copied
into place during VBlank.Problems that can occur with this system:
1) A program polling for VBlank may miss composition. This will cause
the frame to be queued up for the next composition,meaning the
prevIoUs frame will be shown twice as long.2) Worse,the next frame may not miss composition,and end up
overwriting the prevIoUsly queued up frame – so you end up with a
duplicate frame followed by a skipped frame.3) The program’s VSync implementation may naturally fail to detect
VBlank (which has only a short duration),causing it to wait until the
next VBlank and risk problems 1 and/or 2.4) These problems may even combine to generate a ‘perfect storm’ of
duplicate and/or missed frames.As you can see,this polling setup is not ideal,and far worse when a
compositor is present. There are multiple problems that can cause a
new video frame to fail to be displayed,causing a prevIoUs frame to
be displayed for longer than intended and potentially skipping the new
frame altogether!The solution is to work with the compositor instead of against it.
Present a new video frame immediately and afterward,call a command
that will block until the compositor has completed it’s task
(DwmFlush). This will ensure that at most 1 new video frame is
presented to the compositor between each VBlank period. As long as the
compositor is active,you also won’t have to worry about polling for
VBlank yourself anymore.
所以最好的办法是:
if (DWM.isEnabled()) { present(); DwmFlush(); } else { waitForVBlank(); present(); }
另一个编辑:
对于未来的读者,还有另外两种等待VBLANK的方法我不知道它们是IDXGIoUtput :: WaitForVBlank和IDirectDraw7 :: WaitForVerticalBlank后者被弃用了.
当您说“DWM在VBLANK事件之前的某个时间完成合成”时,什么会使DWM启动合成?在任何应用程序呈现之后,或在关注的应用程序呈现之后,它不会发生.组合实际上在VBLANK发生时开始.它可能会完成并翻转VBLANK中的合成曲面,或者最终可能会在下一个VBLANK中翻转(因此您可能会在延迟时间内获得额外的帧持续时间).这不是你的控制 – 它取决于合成的持续时间而不是你的应用程序的存在.
在一个带窗口的应用程序中,当你调用present时,它只是翻转你的应用程序的交换链 – 它不会翻转显示的表面. DWM将处理这个问题.所以“只要合成器处于活动状态,你也不必担心自己的VBlank轮询了.”
然而,使用DwmFlush()来实现“在每个VBlank周期之间向合成器呈现最多1个新视频帧”不一定是期望的.除非你肯定只想在每个监视周期显示1帧,否则你可以这样做(虽然我不确定DwmFlush()是否是最优雅的方法).但在大多数情况下,我不认为强制执行该限制是可取的.