Some multithreading questions

I had thought that I understood threading and OpenGL, but I seem to be experiencing some multithreaded bugs and its made me question myself. Perhaps they are just driver issues because I’m off the beaten video game path. I wanted to sort of play a true/false game to check if my understanding agrees with everyone. Also I’m more interested in what is the best practice (that will help me avoid driver quirks rather than what the GL specification says which is somewhat incomplete with regards to multithreading shared contexts imo).

  1. If contextA is current to threadA and contextB is current to threadB and the contexts have been previously shared, do we have to worry about thread protection/mutexes? I’d assume that in the driver contextA and contextB share a table of textures and furthermore since OpenGL is not thread safe then we need explicit protection for modification of existing objects and whenever creating/destroying objects:
    (a) to prevent threadA and threadB from modifying the same object at once and
    (b) to handle the case where the texture table could be modified simultaneously by multiple threads at once. eg. suppose contextA creates a new texture ID and the driver decides it needs to grow the shared texture table data structure whilst at the same time contextB is attempting to bind a texture from the very same table

  2. If I have shared contextA and contextB and now wish to delete just contextB but continue to use contextA then its safe to delete it, but both must be not current (or just no OpenGL operations must occur on contextA during wglDeleteContext?).

  3. If I create contextA and contextB on the same thread then they must be shared on that same thread or the driver may become upset.

  4. If I want to share contextA and contextB they must be created on the same thread or the driver may become upset. Said differently: you shouldn’t share contexts that were created on different threads.

  5. It is not required but it is the best practice to share your new context before you create any GL objects on it. ie. always share new empty context with existing context

  6. It is not required but it is the best practice to have one hub context (that you never use except to share contexts with) which all other contexts share themselves with

I am busy right now but there is a current thread all about multithread (http://www.opengl.org/discussion_boards/showthread.php/178995-synchronizing-opengl-with-multiple-threads) that you might find insightful. Will check back here later…

  1. The OpenGL 4.3 specification has an entire section on objects and multiple contexts (and thus threads). In particular, you need to prevent 1a, but 1b should be fine. That is, you can create different object names and object instances in different threads. The creation is, for all intents and purposes, atomic.

  2. If you’re deleting contextB, contextA does not care. You do need to make sure contextB isn’t current, but that’s about it.

If I create contextA and contextB on the same thread then they must be shared on that same thread or the driver may become upset.

Perhaps, but one would hope that you’re using wgl/glXCreateContexAttribARB(), which specifies that the newly created context will be shared with another at creation time.

  1. No idea.

It is not required but it is the best practice to share your new context before you create any GL objects on it. ie. always share new empty context with existing context

It is required. For example, wglShareLists states:

If we extend this to all shared objects, then the new context must not have created any shareable objects. Again, wgl/glXCreateContextAttribsARB creates and shares contexts; use that where possible.

  1. That’s up to you. However you want to manage your contexts, objects, etc is your prerogative. If you want to keep a phantom context around just to keep objects alive, you can do that.

Such a thing might be useful for dealing with window re-creation (such as when resizing the desktop or some-such) without destroying every OpenGL object.

Hi Stephen_H
I have two threads: one loading thread and one render thread.

At the beginning of the program, I create two render contexts in one of the threads (render thread in this case):


///--- OpenGL 2.1
int attributes[] = {
    WGL_CONTEXT_MAJOR_VERSION_ARB, 2,
    WGL_CONTEXT_MINOR_VERSION_ARB, 1,
    WGL_CONTEXT_FLAGS_ARB, 0,
    0
    };
HGLRC rcGL21=wglCreateContextAttribsARB(hDC, NULL, attributes);
if(rcGL21!=NULL){
    g_hRTRC=rcGL3x;
    g_hMTRC=wglCreateContextAttribsARB(hDC, NULL, attributes);
    if(g_hMTRC==NULL){
        ... Error
    }
}
 else{
   ... Error
}

if(!wglShareLists(g_hMTRC, g_hRTRC)){
   ... Error
}
if(!wglMakeCurrent(hDC, g_hRTRC)){
   ... Error
}

And after that in the other thread:


if(!wglMakeCurrent(hDC, g_hMTRC)){
   ... Error
}

And everything works properly. The only thing I have noticed is that you cannot call to glTexImage2D at the same time in the two threads. But if you use one thread to create/load ‘objects’ (textures, framebuffers, …) and the other to render, everything works properly.

Hope this helps.

I think the best practice is to not use multi threading for OpenGL. That is, only one thread is needed to keep the graphics card busy too 100%, and so you can’t gain any FPS by using more than one thread.

Of course, you can still use threads to prepare data that will be used. A good way to design this is to use some kind of thread pool.

If you’re trying to optimize texture transfers, you could check out this presentation by NVidia. Or the slides here(PDF)