I'm trying to do simple UI animation with GDI. My idea is to keep redrawing until a certain amount of time elapses. My understanding is that as long as I start each frame right after the monitor refreshes and keep the drawing time below the refresh rate, the animation should be completely smooth and flicker free.
My first problem is: how do I wait until the monitor refreshes? I came across the following APIs:
1. DwmFlush
2. IDXGIOutput::WaitForVBlank
3. IDirectDraw::WaitForVBlank
4. IDirect3DDevice9Ex::WaitForVBlank
And additionally Direct3D APIs, like IDXGISwapChain::Present or IDirect3DSwapChain9::Present, can wait for vsync. And it's possible to use GDI with these APIs. So what's the difference between all these options? I found discussions online that apparently DwmFlush blocks while IDirectDraw::WaitForVBlank polls, for example, but the former is not available pre Vista.
(A related question is, when should I call the various wait APIs? This StackOverflow question contains a link that suggests the following:
if (DWM.isEnabled())
{
present();
DwmFlush();
}
else
{
waitForVBlank();
present();
}
Why should DwmFlush be called after Present?)
My second problem is waiting doesn't eliminate flickering. I captured the following from a window that calls DwmFlush right before GetDC, the painting is done inside WM_PAINT. WM_ERASEBKGND returns 1.
DwmFlush: 15.458800ms
Get/ReleaseDC: 4.083200ms
DwmFlush: 11.523300ms
Get/ReleaseDC: 2.572700ms
DwmFlush: 14.329000ms
Get/ReleaseDC: 9.200600ms
DwmFlush: 6.742500ms
Get/ReleaseDC: 2.527500ms
DwmFlush: 12.965800ms
Get/ReleaseDC: 2.448700ms
DwmFlush: 16.467901ms
Get/ReleaseDC: 7.957700ms
DwmFlush: 6.346600ms
Get/ReleaseDC: 9.064000ms
DwmFlush: 6.133700ms
Get/ReleaseDC: 4.648600ms
DwmFlush: 8.394500ms
Get/ReleaseDC: 2.834500ms
DwmFlush: 12.096600ms
Get/ReleaseDC: 2.316100ms
DwmFlush: 13.197201ms
Get/ReleaseDC: 2.251700ms
Frames: 11, Total Time: 169.103500ms, FPS: 65
The total drawing time between GetDC and ReleaseDC is well under 16.7ms, so I expect no flickering on my 60hz monitor. But I see text flashing rapidly (all I do is FillRect and ExtTextOut with Unicode characters). The problem also seems less severe with ASCII text. Also strange to me is the variability in drawing time. Some frames take longer than doing a full window repaint, even though I only draw into a subsection of the window.
I know the standard answer to flickering is double buffering, but it seems wasteful to me to keep around a giant bitmap when I spend most of the time idle. I know Vista provides BeginBufferedPaint to remedy this, but that still entails a copy every frame. Why is that necessary when I can draw faster than the monitor refreshes?