Platform specifics: Windows

From OpenGL Wiki
Revision as of 12:25, 9 July 2011 by V-man (talk | contribs) (wglGetProcAddress: Adding "Note: It has been reported that............")
Jump to: navigation, search

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.

PFD_DRAW_TO_BITMAP

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..

Multiple Windows

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)
Example:

 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.

wglShareLists

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)

http://www.opengl.org/registry/specs/ARB/wgl_create_context.txt

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

wglGetProcAddress

This function is for getting a pointer to a function. Since on Windows, opengl32.dll will never get updated, opengl32.dll only provides the GL 1.1 functionality subset. To get the higher ones, you need wglGetProcAddress. Note that wglGetProcAddress gives a direct pointer to the real opengl driver implemented by your IHV, so you get to bypass opengl32.dll. This doesn't mean your GL code will run faster. In the case of modern games, there are often other bottlenecks. For example, they are often fill rate limited due to the enormous about of pixels they need to render.

An example for wglGetProcAddress would be :

First, I'm going to look at glext.h which defines all the functions in GL 1.2 and above and also extensions. Or, we could look at gl3.h (whatever you prefer).

Let's say, I want to get a pointer for glCompressedTexImage3D. I can see the definition for glCompressedTexImage3D in glext.h.

 PFNGLCOMPRESSEDTEXIMAGE3DPROC glCompressedTexImage3D;

I would put the above line in one of my cpp files (If I'm programming in C++). I would probably name the file allmy_gl_functions.cpp. I would like for all my other cpp files to see that function definition therefore I would create a header file and call it allmy_gl_functions.h. I would declare this in my header file

 extern PFNGLCOMPRESSEDTEXIMAGE3DPROC glCompressedTexImage3D;

and now I can include that header file in all my other cpp files so that both the compiler and linker would know what is to be done. The only thing left to do is to create a GL context and then get the function pointer

 glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)wglGetProcAddress("glCompressedTexImage3D");

So, wglGetProcAddress takes a string as argument and looks into a table and returns an address. The only thing left for you to do is load all the hundreds of functions of GL.

Fortunately, there are already several projects that do all that for us. GLEW is one example. http://www.opengl.org/wiki/Getting_started

We strongly recommend that you read that Getting Started page. Pay attention to the Getting Functions section. We strongly recommend that you don't waste your time and use a already made library such as GLEW.

Note: It has been reported that wglGetProcAddress fails for functions that are already present in opengl32.dll. Do not try to get a function pointer for glVertex3f, glBegin, glBindTexture, glEnable and the many other GL 1.0 and 1.1 functions. It has also been reported that GetProcAddress works on the GL 1.0/1.1 functions although I'm not sure why you would want to get function pointers for these GL 1.0/1.1 functions.

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 :

  • wglCreateContext
  • wglDeleteContext
  • wglMakeCurrent
  • wglShareLists


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