2011-10-29

c++: multithreaded opengl

An example of doing multithreaded OpenGL calls in C++.


Recently I've been using openFrameworks, a creative coding platform for doing graphics and audio projects. A critical piece for software like this is the ability to perform OpenGL calls on worker threads, for doing things like loading and rendering in the background. There was a class included, ofxGlThreadDispatcher I believe, but it wasn't working quite how I wanted so I replaced it. If you're familiar with that class, mine bears essentially no relation; I retained the sole API call to perform on a background thread (performOnWorkerThread()) but otherwise everything has been replaced.

Mine is designed in two basic pieces: The GlThread manages the thread where operations are performed, including implementing a message queue. The GlThreadClient provides access to the thread and also provides a container for operations that need to be performed.

One of the reason the thread class is separated is so that it can be memory managed by the main application. The idea is that, in your main app class, you simply have a reference to the thread, i.e.

...
private:
GlThread mLoadingThread;

This way there is never any need to allocate or deallocate. Then, to utilize the thread, generally you subclass the thread client like this:

ClassThatHasOperations : public GlThread<ClassThatHasOperations> {
public:
ClassThatHasOperations(GlThread&);

void mainThreadOperation();

private:
void _workerThreadOperation();
};

And then the class can send member functions to the worker thread like this:

void ClassThatHasOperations::mainThreadOperation() {
performOnWorkerThread(&ClassThatHasOperations::_workerThreadOperation);
}

That's all it takes to use the class. One thing to be aware of: In most designs you generally want to hide the destructor, and then have a special destruction function that implements the actual destructor on the worker thread. Alternatively, you can block in the destructor (using the waitForNoInput() call), but assuming you're running a realtime application, you probably don't want to do that, and instead want to send the object off to the worker thread to be deleted.

Another thing to note: The GlThread class should always be created on your main thread. This is necessary to synchronize the call to wglMakeCurrent() with the current DC. If you don't block on the main thread until after this call is made, it is possible for the OpenGL thread context to not initialize properly, preventing any GL calls in the worker thread.

I'll post the code for anyone curious about the details. I pulled this from active code so there are probably some includes missing, but otherwise you should be able to drop it into any Windows project.


gl_thread.h

gl_thread.cpp


As a bonus, here's a variant that uses lambdas instead of class and function pointers. This eliminates the mess of templates and is vastly preferable, but note that I just wrote this in a text file for fun, this is not something I've actually used in a project, so there are undoubtably bugs.

gl_thread_lambda.h

gl_thread_lambda.cpp

No comments:

Post a Comment