Wednesday, August 26, 2009

Creating OpenGL Textures or VBOs On A Second Thread

It's always scary when I Google for a question and find my own blog post in the top few search terms...it makes me think I've programmed my way out into the wilderness. Given how many forum questions involve people trying to figure out how to put OpenGL resource loading in a worker thread (and how many times they are told to just use one thread because it's a PITA) I figured I would blog what I have discovered through painful trial and error.

The previous post discusses setup of "loader threads" - that is, a worker thread with a shared GL context that is meant to be used to load textures and VBOs without interrupting the main thread from doing rendering work. The loader thread has a current GL context but usually no framebuffer, which is fine since we don't really want to draw.

What do we need to do to create a texture or VBO on this worker thread? The steps go something like this:
  1. Issue the GL commands to create the texture or VBO. Typically you'd have someting like glBindTexture, glTexImage2D, glTexParamBlaBlaBla.

    Remember: GL state is separate for each thread - if you have OpenGL utility code that "caches" state in global variables, you'll need to make it thread safe or something. I use one set of utilities on the main thread (with caching) to render, and a separate set on worker threads; a debug-build-only check catches incorrect use of utilities on the wrong thread.

  2. Call glFlush...more details here. But basically if you don't force Open to swallow what it's chewed, your texture-create commands might just be sitting around for a while.

    (Side note: as of August 2009, the bugs you get on Windows from not flushing a VBO built in a worker thread are truly spectacular visually...the crazy runways in the sky and other creepy effects are all due to a missing glFlush on a worker.)


  3. Finally, send a signal to the rendering thread to allow it to use the entity.

I use message queues for step 3 because I love message queues in a way that might not be healthy. To go with the "resource ownership" idea, once your worker has loaded and flushed the texture or VBO, you are "passing ownership" to the renderer via the message queue. The main thread can do something like this:
while(1) {
scene_graph.draw();
obj * new_goo = msg_queue.read(do_not_block);
if(new_goo)
scene_graph.insert(new_goo);
}

The cost to the main thread of this design is about as low as it can get - basically you only pay for bucketing your new geometry.

1 comment:

  1. Thanks for the articles, very helpful stuff
    I'm having a problem with the thread object creation (especially VBO) creation, and it reduces the performance of all proceeding frames drastically.
    I'm on AMD 5750 with latest drivers + windows
    Have you seen something like this ?
    https://www.opengl.org/discussion_boards/showthread.php/183544-Creating-objects-in-another-thread?p=1257403#post1257403

    ReplyDelete