Platform specifics: Windows
This article describes Windows-specific behavior of OpenGL programs.
- 1 Compiler errors and gl.h
- 2 What should I do before the window is created?
- 3 When is a good time to create the GL context?
- 4 It's flickering! Why?
- 5 When do I render my scene?
- 6 When do I destroy the GL context?
- 7 How many times can I call SetPixelFormat?
- 8 PFD_DRAW_TO_BITMAP
- 9 Partial Screen Update
- 10 Multiple Windows
- 11 wglShareLists
- 12 SwapInterval aka vsync
- 13 wglGetProcAddress
- 14 The Default Renderer
- 15 The WGL functions
- 16 Installing Mesa3D on Windows
Compiler errors and gl.h
If you get compiler errors pointing to gl.h, this is because there are some things like WINGDI which are used in gl.h but are only defined in some other header files. Include windows.h before including gl.h
If you have link errors, then you need to figure out how to link with your specific compiler/IDE.
Example : with VC++6 (which is very old by now), you go to Project->Settings and click on the links tab.
Add opengl32.lib to the Object/library modules list.
If you use other libs like GLU, add glu32.lib.
Other compiler/IDE mind need to link with opengl32.a
What should I do before the window is created?
Be sure to use the CS_OWNDC flag.
If you are doing plain old WinMain coding, RegisterClassEx() takes the WNDCLASSEX structure.
WNDCLASSEX.style should have the CS_OWNDC
If you are using MFC (SDI or MDI project), override PreCreateWindow and add the CS_OWNDC to cs.
For CDialog based project, PreCreateWindow doesn't get called.
This flag is really needed for Win9x systems.
Since DCs (device contexts) consume resources, the OS does not assign a DC but might share it between multiple windows and GUI elements.
On WinNT and its derivatives, it is said that resources are not a problem and it assigns a unique DC per window.
For good practice, I recommend using CS_OWNDC.
When is a good time to create the GL context?
When the WM_CREATE message is sent.
This is when the window is created (has a valid HWND and DC).
Normally, the window is visible at this point. Even if you create a invisible window, this is the place to create the GL context.
This is also a good place to make the context current, setup some GL states and setup some things for your program.
For MFC, you override the OnCreate function for your "View" class.
It's flickering! Why?
Windows sends a WM_ERASEBKGND message when the background needs to be erased.
Tell Windows that you handled the message by returning a non-zero number (TRUE).
On MFC, override OnEraseBkgnd and just return TRUE.
When do I render my scene?
Answer to this question depends on the situation:
- For application like CAD, 3D model viewer or anything that doesn't require constant updating, render when you receive the WM_PAINT message
- For high performance game do not use WM_PAINT and render directly from your "infinite loop"
Besides this simple division, please consider following advices:
- If you are developing game or dynamic visualisation in window (not fullscreen), consider setting up a timer and render only when receiving WM_TIMER message. You will be still able to get high framerate, but your program will not eat so much CPU resources
- If you are developing fullscreen application, do not render when user ALT-TABs from your program. You can detect this by handling WM_ACTIVATEAPP. This will again prevent CPU "hogging" when it is not necessary
To render image on the screen, you can follow this simple scheme:
glClear( ... ) // Clear the color/depth/stencil buffer // // Draw your geometry using OpenGL commands here // SwapBuffers( ... ) // Swap buffers to make geometry visible on screen
To swap buffers, use SwapBuffers instead of wglSwapBuffers.
For MFC, override OnPaint or OnDraw (depending on the parent class)
When do I destroy the GL context?
Some people do it when the WM_DESTROY message is received. In my experience, this is not correct because at this point, the window resources and DC are destroyed already. Functions like wglMakeCurrent may fail.
Try the following: call wglMakeCurrent(NULL, NULL) followed by wglMakeCurrent(hdc, hglrc).
The second call will(might) fail.
Instead, destroy the GL context when the WM_CLOSE message is received. With MFC, override OnClose.
Don't forget to deallocate your textures, display lists, VBOs, PBOs, FBOs, shaders, etc before destroying the GL context.
It is good programming practice to release resources!
Call wglMakeCurrent(NULL, NULL) to make the GL context non-current.
Call wglDeleteContext(glrc) to destroy the GL context.
If you have allocated a DC for your window, release it and/or destroy it (ReleaseDC and/or DeleteDC)
How many times can I call SetPixelFormat?
For each window, once. According to MSDN, it would lead to significant complications if they allowed for more flexibility.
Never call GetDC(NULL) and then call SetPixelFormat. This gives the DC for the entire desktop. Instead, create a fullscreen window.
This flag is for making an offscreen render buffer onto with you render with your GL commands. Typically, people do this so that they can BitBlt the result to a window or a BitBlt to a printer device context or so that they can use some Win32 commands on that same surface.
A few source code examples from 1995 show how to use PFD_DRAW_TO_BITMAP.
You should avoid using this flag during context creation, as it may cause you to not get a hardware-accelerated rendering context. It will give you the Microsoft OpenGL version 1.1 software renderer.
Partial Screen Update
Certain types of software maintain multiple viewports on a single window. They render a "right view", "left view", "top view" and "preview" of the same model. The programmer might decide that "preview" needs updating but the other views do not.
In this case, you would need to select a pixelformat that supports PFD_SWAP_COPY. PFD_SWAP_COPY means that the backbuffer will be copied to the front buffer when you call SwapBuffers. Without that flag, it would be just swap pointers between front and back buffer. Switching pointers is much faster than a buffer copy. You need setup a scissor area with glScissor and enable scissoring. Then call glClear to clear only the scissor area. Now you can render and call SwapBuffers.
There might be some performance loss because these days graphics cards expect that you clear the entire screen. You should benchmark and see if there are any benefits. If there aren't, select another render path in your engine where you update the entire window.
If your project opens other windows and you want GL on the other window as well, in each function that you have, call wglMakeCurrent(hdc, glrc) and when you are done call wglMakeCurrent(NULL, NULL)
wglMakeCurrent(hdc, glrc); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); RenderThis(); RenderThat(); SwapBuffers(hdc); wglMakeCurrent(NULL, NULL);
This is because a GL context can be current to 1 thread. Since window 1 has a GL context and window 2 has a GL context, both can't be current at the same time. You can either create a window in another thread, this way each context is in its own thread, or if both windows use the same pixelformat, use 1 GL context for both windows. In the 2 window 1 context case, you still have to call wglMakeCurrent(hdc, glrc) wglMakeCurrent(NULL, NULL) because the hdc would be different for each window.
According to MSDN, wglShareLists shares display list space between 2 GL contexts. The MSDN documentation is incomplete. This function allows the sharing of other objects. In addition to display lists, this function allows sharing of textures, renderbuffers, and buffer objects. Only these object types are shared among GL contexts.
The best time to call wglShareLists is after creating the GL contexts you want to share, but before you create any objects in either of the contexts. If you create objects, then there is a chance that wglShareLists will fail.
Sharing can also be done as part of context creation, using wglCreateContextAttribsARB. This is analogous to how *nix systems do it, and you don't have to worry about not creating objects in the new context, since creation and sharing are done simultaneously.
HGLRC wglCreateContextAttribsARB(HDC hDC, HGLRC hShareContext, const int *attribList)
The 2nd parameter to wglCreateContextAttribsARB is a previously created GL context. wglCreateContextAttribsARB is part of the WGL_ARB_create_context extension which was introduced in parallel to when GL 3.0 was introduced. WGL_ARB_create_context can be found in the string returned by wglGetExtensionsStringARB(hdc)
Programs written for GL 3.0 and above should make use of WGL_ARB_create_context. Even programs that try to create for example an old context such as 2.1 with wglCreateContextAttribsARB can also use it to share resources.
SwapInterval aka vsync
If you need information on WGL_EXT_swap_control which gives wglSwapIntervalEXT, read
SwapInterval aka vsync
This function is for getting a pointer to OpenGL functions. More details can be found in the article on loading OpenGL function pointers.
wglGetProcAddress requires a GL context to be created and to be made current (wglMakeCurrent) for it to work, otherwise, it will return NULL for every call.
The Default Renderer
In this section, we'll talk about the default GL implementation of Microsoft.
The original Windows 95 did not have OpenGL. You had to download an installer from Microsoft called opengl95a.exe and run it.
Windows 95b came with GL. It included a GL 1.1 software renderer. Windows NT 4 also came with a GL 1.1 software renderer. There were a few screensavers that utilized GL : for example the screensaver with the maze.
The case for Windows 98, 98SE, Me, 2000 was the same : they came with the same GL 1.1 software renderer and the same GL screensavers.
Windows XP changed things a bit. opengl32.dll now utilized Direct3D and since Windows comes with drivers for your hardware, the renderer was fast. The GL screensavers were rewritten to use Direct3D, probably version 8 since Windows XP came with DirectX 8.
For Windows Vista, Microsoft had announced that they will update their renderer to GL 1.4. My own experience shows that it is actually a GL 1.1 software renderer. For some reason, Microsoft decided to remove the Direct3D wrapper from opengl32.dll.
The WGL functions
We call them the "wiggle" functions.
There are many wgl functions and some aren't even documented in MSDN.
Mostly, you will only need :
If you are writing an GL extension loader, you will make plenty use of wglGetProcAddress.
I don't recommend that you use some of these wgl functions :
- Use ChoosePixelFormat instead of wglChoosePixelFormat
- Use DescribePixelFormat instead of wglDescribePixelFormat
- Use SetPixelFormat instead of wglSetPixelFormat
- Use GetPixelFormat instead of wglGetPixelFormat
- Use SwapBuffers instead of wglSwapBuffers
glX vs wgl
This is a list of glX functions and their equivalent wgl functions. There might be some missing entries.
- glXChooseVisual --- ChoosePixelFormat
- glXCopyContext —-- NULL
- glXCreateContext --- wglCreateContext
- glXCreateGLXPixmap --- CreateDIBitmap and CreateDIBSection
- glXDestroyContext --- wglDeleteContext
- glXDestroyGLXPixmap --- DeleteObject
- glXGetConfig --- DescribePixelFormat
- glXGetCurrentContext --- wglGetCurrentContext
- glXGetCurrentDrawable --- wglGetCurrentDC
- glXIsDirect —-- NULL
- glXMakeCurrent --- wglMakeCurrent
- glXQueryExtension ---- GetVersion
- glXQueryVersion --- GetVersion
- glXSwapBuffers --- SwapBuffers
- glXUseXFont wglUseFontBitmaps and wglUseFontOutlines
- glXWaitGL —-- NULL
- glXWaitX --— NULL
- XGetVisualInfo --- GetPixelFormat
- XCreateWindow --- CreateWindow/CreateWindowEx and GetDC/BeginPaint
- XSync GdiFlush
- NULL —-- SetPixelFormat
- glXGetProcAddress --— wglGetProcAddress
- NULL —-- wglShareLists
Installing Mesa3D on Windows
If your system does not contain a GPU, or the GPU vendor delivers graphics drivers providing OpenGL support that's so old as to be useless to you, you might want to consider installing the Mesa3D OpenGL library on your system to provide OpenGL support.
Mesa3D is a graphics library that provides an OpenGL implementation for multiple platforms. GPU hardware acceleration is supported on some GPUs, with a software (CPU) rendering pipeline generally available as a fallback or alternative rendering method (see Mesa3D Support Matrix).
For experienced developers that choose to build Mesa3D libraries from source, here are a few guides on how to download, build, and install Mesa3D:
- Downloading, Compiling, and Installing Mesa3d (Mesa3D)
- Building Mesa3D on Windows (Dossena)
- How to build Mesa for software rendering with llvmpipe on Windows with Visual Studio 2017 (llvmpipe) (Qt)
For beginning developers and others which don't have the time or desire to build Mesa3D libraries from source, here are some pre-built Windows installer (EXE) images. Options are provided to install the Mesa3D OpenGL libraries either system-wide or per-application: