Higher-Order Fun

Game Design & Game Programming

Multi-thread OpenGL texture loading

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.

On Windows/C++0x, the code looks like this:

void startLoader(HWND hwnd)
{
	// Assuming that this is the main thread
	HDC hdc = GetDC(hwnd);
	HGLRC mainContext = wglGetCurrentContext();
	HGLRC loaderContext = wglCreateContext(hdc);
	wglShareLists(loaderContext, mainContext); // Order matters

	boost::thread([=] () {
		wglMakeCurrent(hdc, loaderContext);
		runLoader();	// Your method for loading textures
		wglMakeCurrent(nullptr, nullptr);
		wglDeleteContext(loaderContext);
	});
}

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.

« Previous post

11 ResponsesLeave one →

  1. Paulo V. W. Radtke

     /  2012-08-24

    Which are the Linux equivalent functions? I think it the only thing missing :).

  2. Thanks for solving my texture corruption problem!

  3. Yaro

     /  2014-02-05

    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.

  4. Drifter

     /  2014-08-13

    I couldn’t make this work on glx or wxwidgets. Can somebody provide a working example? Thanks!

  5. gman

     /  2014-10-10

    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.

  6. AqD

     /  2015-02-11

    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.

    And nothing from loader thread could be rendered, no error :*

  7. RedRofeStard

     /  2017-01-08

    Regproj test

  8. Qianaqueele

     /  2017-11-15

    some small security problems with my latest weforum and Id like to locate something a lot more safeguarded. Do you have any recommendations? http://9786hsje.tumblr.com/ – Brandie

  9. canada viagra viagra online canada https://trustedstoremjd.com/# – best place to buy viagra online viagra without a prescription goodrx viagra

  1. c++ - Multiproceso Renderizado de OpenGL
  2. c# - Multithreading Rendering in OpenGL

Leave a Reply to RedRofeStard

*