Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call glewInit once for each rendering context? or exactly once for the whole app?

Tags:

opengl

glew

I have a question about how to (correctly) use glewInit().

Assume I have an multiple-window application, should I call glewInit() exactly once at application (i.e., global) level? or call glewInit() for each window (i.e., each OpenGL rendering context)?

like image 415
Changgong Zhang Avatar asked Feb 28 '16 13:02

Changgong Zhang


1 Answers

Depending on the GLEW build being used the watertight method is to call glewInit after each and every context change!

With X11/GLX functions pointers are invariant.

But in Windows OpenGL function pointers are specific to each context. Some builds of GLEW are multi context aware, while others are not. So to cover that case, technically you have to call it, everytime the context did change.

(EDIT: due to request for clarification)

for each window (i.e., each OpenGL rendering context)?

First things first: OpenGL contexts are not tied to windows. It is perfectly fine to have a single window but multiple rendering contexts. In Microsoft Windows what matters to OpenGL is the device context (DC) associated with a window. But it also works the other way round: You can have a single OpenGL context, but multiple windows using it (as long as the window's pixelformat is compatible with the OpenGL context).

So this is legitimate:

HWND wnd = create_a window()
HDC  dc  = GetDC(wnd)
PIXELFORMATDESCRIPTOR pf = select_pixelformat();
SetPixelFormat(dc, pf);

HGLRC rc0 = create_opengl_context(dc);
HGLRC rc1 = create_opengl_context(dc);

wglMakeCurrent(dc, rc0);
draw_stuff(); // uses rc0

wglMakeCurrent(dc, rc1);
draw_stuff(); // uses rc1

And so is this

HWND wnd0 = create_a window()
HDC  dc0  = GetDC(wnd)
HWND wnd1 = create_a window()
HDC  dc1  = GetDC(wnd)

PIXELFORMATDESCRIPTOR pf = select_pixelformat();
SetPixelFormat(dc0, pf);
SetPixelFormat(dc1, pf);

HGLRC rc = create_opengl_context(dc0); // works also with dc1

wglMakeCurrent(dc0, rc);
draw_stuff();
wglMakeCurrent(dc1, rc);
draw_stuff();

Here's where extensions enter the picture. A function like glActiveTexture is not part of the OpenGL specification that has been pinned down into the Windows Application Binary Interface (ABI). Hence you have to get a function pointer to it at runtime. That's what GLEW does. Internally it looks like this:

First it defines types for the function pointers, declares them as extern variables and uses a little bit of preprocessor magic to avoid namespace collisions.

typedef void (*PFNGLACTIVETEXTURE)(GLenum);
extern PFNGLACTIVETEXTURE glew_ActiveTexture;
#define glActiveTexture glew_ActiveTexture;

In glewInit the function pointer variables are set to the values obtained using wglGetProcAddress (for the sake of readability I omit the type castings).

int glewInit(void)
{
    /* ... */

    if( openglsupport >= gl1_2 ) {
    /* ... */
        glew_ActiveTexture = wglGetProcAddress("glActiveTexture");
    /* ... */
    }

    /* ... */
}

Now the important part: wglGetProcAddress works with the OpenGL rendering context that is current at the time of calling. So whatever was to the very last wglMakeCurrent call made before it. As already explained, extension function pointers are tied to their OpenGL context and different OpenGL contexts may give different function pointers for the same function.

So if you do this

wglMakeCurrent(…, rc0);
glewInit();
wglMakeCurrent(…, rc1);
glActiveTexture(…);

it may fail. So in general, with GLEW, every call to wglMakeCurrent must immediately be followed by a glewInit. Some builds of GLEW are multi context aware and do this internally. Others are not. However it is perfectly safe to call glewInit multiple times, so the safe way is to call it, just to be sure.

like image 169
datenwolf Avatar answered Oct 12 '22 23:10

datenwolf