RE mixing GDI with OpenGL

The question “can I mix GL with GDI?” occasionally still pops up, and every time I and others have replied: “avoid!”. The oft mentioned reason is that it causes flicker.

I recently ran into this issue by coincidence myself, and although I knew better, gave it a shot anyway…and found it is actually not a problem at all!

The trivial solution: call glFinish after SwapBuffers. Not before. It fixes the flicker. And it makes sense.

SwapBuffers is not a blocking call. So when subsequent GDI calls are executed, the Device Context (DC) framebuffer (window framebuffer) contents may not yet be updated (overwritten) with the GL backbuffer. Due to varying hardware/software/driver/OS performance, Swapbuffers may or may not complete before the application invokes the GDI calls which start writing to the DC buffer. One frame it manages to complete, the next frame it fails. It randomly alternates, resulting in flicker.

In short, my findings:

  • Simply call glFinish after SwapBuffers. I should note that, typically, glFinish is also called before SwapBuffers, supposedly to allow GL to complete all of it’s framebuffer operations. Today it seems a bit vague whether one should still call glFinish before SwapBuffers, due to API/hardware architecture advances. Most drivers seem to invoke glFinish implicitly or do some voodoo for best performance. Either way, calling glFinish after SwapBuffers does stall the application. It gives SwapBuffers the time to ‘complete’ (copying the GL front buffer to the HDC buffer). In effect, glFinish delays the programs’ GDI calls, so that they are no longer writing to the not-yet-updated DC framebuffer.

  • If you want to draw GDI elements before (‘under’) GL elements (this is rarely useful), things are a bit more complicated. It does not ‘just work’ with standard double-buffering. This is because the GL backbuffer will not know about the contents of the current DC buffer, after you’re done with GDI and start drawing GL elements. The GL backbuffer is a completely different buffer than the GDI buffer. So after GDI drawing, one needs to extract the DC framebuffer, and then copy that to the GL backbuffer (e.g. glDrawPixels). Skip glClear(GL_COLOR_BUFFER_BIT), then do your GL drawing and call SwapBuffers as usual.
    If you then switch back to GDI rendering again, that’s just fine, you must call SwapBuffers followed by glFinish (per the above). Do keep in mind that switching frequently between GL and GDI per frame will incur a lot of pipeline stalls, so this is generally a bad idea. Better draw everything with GL.
    Alternatively, you can use a single-buffered GL context, the GL front-buffer will be the same as the DC framebuffer. You don’t need glDrawPixels or SwapBuffers (just glFinish is enough) since there is only one buffer. However, single-buffering will cause the GL elements to suffer from tearing, so I would not recommend this unless the image is rarely redrawn (non-realtime applications).

To get back to the question and the answer…yes GDI can be safely mixed with GL, provided you draw GDI only on top of GL elements, and call glFinish after SwapBuffers. glFinish may negatively affect performance slightly, but no more than what is needed to enable GDI rendering.

Should one mix GL with GDI? I no longer see any reason against it. For one, it is really nice to draw viewport labels with native OS rendering of installed system fonts with GDI TextOut(); no GL font rendering library required.

Of course, the old advice to never use GDI for high performance realtime applications still stands. E.g. don’t use it to render UI/HUD (or frame rate counter :wink: ) in your game.

Cheers.

1 Like

Nice find. I wonder how it works in the context of the WDDM driver model advice here: http://www.opengl.org/pipeline/article/vol003_7/

1 Like

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.