Game Design & Game Programming

Those who have used OpenGL are probably aware that you can only invoke OpenGL procedures for a given context on the thread that currently owns it – usually, the main thread. This leads many programmers to assume that OpenGL is strictly single-threaded, and that no loading can be done on the background, inside some loader thread. This is not the actual case, and it’s actually fairly simple to work around this.

The key to multi-threaded OpenGL is in creating multiple contexts that share data between them. On Windows, it is done with the wglShareLists() procedure. On X and Apple APIs, it can be done during context creation (glXCreateContext() and aglCreateContext(), respectively). It boils down to creating a second context for the loader thread, and setting it to share data with the main context. This way, all data loaded on the second context will be available on the main one – you can just create a texture normally on the loader thread, and then glBindTexture() it on the main thread, for example.

Most APIs (such as Allegro, SDL, wxWidgets, etc) will provide you with a simple method of retrieving the window handle, which is all that you require to call the above procedure.

Note that you could create the context and share the lists inside the loader thread, but wglShareLists() must be called BEFORE anything is done on the main context, so the safest way is to do it on the main thread (otherwise, the new thread could take a while to run and be too late to do it).

IMPORTANT! I have observed that, on some cases (Windows 7 64, NVIDIA 320M), attempting to use a texture after it has been created (via glGenTextures()) but before its data finished uploaded (in this case, via glTexSubImage2D()) resulted in the texture being corrupted, and remaining corrupted even after it was uploaded. This will happen even if you wait for glTexSubImage2D() to return before using it on the main thread, since OpenGL is asynchronous. To avoid this problem, make sure that you glFinish() the loader thread before you attempt to use any textures initialized there.

Paulo V. W. Radtke

Yaro

I’m a little confused. You talk about there being a way to do this in an X11/OS X environment, then you proceed to show us just another way to do it in Windows, which is not OS X nor uses X11? This makes no sense to me. I thought the purpose of this article was showing us how to do this on platforms like Linux or Mac OS X.

Drifter

gman

Don’t do it! Or at least that’s my suggestion. Most GL drivers are not well tested with multiple threads and you’ll likely have support nightmares if you ever ship something.

I’d suggest just doing graphics on 1 thread. If you want to upload something preempt whatever you were doing and upload. If you’re loading a texture load from disk and/or decompress on a separate thread and when done pass that info to the one thread talkng to GL.

Note: You can still have multiple GL contexts so you don’t have to track GL state but I’ve found trying to actually use GL directly with multiple threads is just adding your pain.

You’re not gaining anything anyway by using multiple threads as ultimately the driver itself is not multi-threaded.

Also note that even multiple context often don’t work. Especially on mobile. There are no tests by driver makes for multiple contexts threaded or not. Seriously.

AqD

I’m attempting to do it with JOGL on latest nVIDIA cards but multiple threads on shared contexts along create weird problems such as unusually high CPU usage (sluggish mouse, PC nearly freezes). I never had such problem when shared contexts are only accessed by a single thread.

About

I'm Rodrigo Monteiro, and I have been programming video games since 1997, ranging from Klik & Play to C++ and Java. I'm the creator of the Aegisub subtitling software (together with Niels Hansen), and I like joining game programming challenges such as the Allegro SpeedHack and the Global Game Jam.

I currently work as a professional video game programmer at Bossa Studios, in London, United Kingdom.