Higher-Order Fun

Game Design & Game Programming

Tag: opengl

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);

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.