OpenGL framebuffer objects can’t be shared between rendering contexts

(Title is sort of a spoiler for the content of this post)

So I spent about an hour today debugging why a particular user interface element was generating OpenGL errors when I tried to draw it.  It was this:

soldier_croc

This is an early revision of a character editor for MMORPG Tycoon 2.  The interesting bit of it is the 3D character being drawn in the right side of the window.  This is done by drawing the model into an offscreen texture, and then drawing that texture into the window.

Works great here in my UI element testing mode, but broke terribly when I ran it in the real game;  it complained that trying to set that offscreen texture as the place to draw into was completely illegal.  But it worked when I ran the same code from the UI testing mode.

So after an hour or so of investigation, the problem turned out to be simple.

When I load everything for the real game, I do it from a background thread which has its own OpenGL context, which is technically a “shared” context, sharing its data with the main context.  This is so that I can be rendering in the main context, while the background context is creating textures, models, and other OpenGL data, without worrying about trying to maintain the loading screen’s frame rate. 

The gotcha is this:  framebuffer objects (FBOs) cannot be shared between OpenGL contexts the way that vertex buffer objects (VBOs), textures, and other objects can be.  And since I was loading the window from that background thread’s OpenGL context, and the window created the framebuffer that it was going to render, when it came time to actually draw the window on the main thread, the main thread’s OpenGL context had never heard of the FBO, and threw errors like mad. 

I hadn’t seen this problem from the UI testing mode simply because that mode loads everything from the main thread’s normal OpenGL context which is used for rendering, and so that FBO which was created by the window was completely fine to render to.

So there you go.  Turns out that if you’re writing OpenGL code, shared contexts aren’t a complete panacea for multithreading issues after all; you still have to be sure to create your FBOs only from the main context, if that’s where you’re going to be rendering into them. Lesson learned.

(The screenshot here is a bit old, and the models are extremely early and placeholder.  But this is still a whole lot nicer than the old models!  Being able to customise the models used by your players, the monsters, and the NPCs is one of the big new features that will be present in Milestone 10.  I’m pretty pleased at how it’s starting to shape up.)