Rendering on a separate thread

realh wrote on Sunday, September 29, 2013:

I want to keep my rendering, input and game threads all separate, but I can’t get it to work on Windows, only in Linux. I’ve written a minimal test program to demonstrate. It should plot a blue triangle on a white background, which it does in Linux, but not in Windows, unless I make a single-threaded version. I just get a black screen. I also tried calling glewInit from the rendering thread, but that made it complain “Missing GL version”. Linux is fine either way.

I’m using MinGW32 with precompiled binaries of GLFW 3.0.3 (but still using 3.0.1 on Linux), self-compiled GLEW and pre-compiled pthreads-win32 (GCE2 version). I tried switching my main programs to use the Windows threading API, but that didn’t make any difference. They’re too complex to post here because there are lots of abstraction wrapper classes etc for portability to Android as well as GLFW+Windows/Linux. I’ll try to attach the simpler example here though.

Any ideas? The book that got me into Android programming said it’s very desirable to separate input and rendering for responsiveness, and I agree. I thought it would be common practice elsewhere too, but then I discovered it isn’t possible with SDL. Then I thought the GLFW docs were saying it is possible with GLFW3.

realh wrote on Sunday, September 29, 2013:

My example of threading working in Linux and not in Windows.

dougbinks wrote on Monday, September 30, 2013:

It looks like your primary thread isn’t setting it’s context to NULL. See http://www.opengl.org/wiki/OpenGL_and_multithreading

I would advise against trying to put your OpenGL rendering thread on a different thread from the one you use to create the window. You’ll almost certainly simply transfer the rate determining thread from the first to the second whilst making your program harder to work with, and probably at the same time block both threads due to synchronization.

If you want to separate out the work on to multiple threads, do so by doing any heavy processing on other threads (preferably using a task model). For example you can compile a list of visible objects, sort object by state etc. Input should be gathered on the main thread but processed by the other threads/tasks. For low latency view control you may also want to re-measure input as late as possible see this post by Carmack on this at AltDevBlogADay.

realh wrote on Tuesday, October 01, 2013:

You were right, making the parent thread set its context to NULL has fixed it. Thanks, that’s great.

Multi-threading might make things a little bit harder to program, but will improve the quality of the game IMO. If you don’t want gameplay to lag on complex scenes you either have to multi-thread or make the game able to move objects by different amounts depending on the period between any pair of “ticks”. I think the latter would be more complicated than multi-threading in most games.

Even if I couldn’t separate input and rendering threads I’d still want the game logic on a separate thread for the above reason. But GLFW doesn’t seem to provide a way I could wait for input but be woken up as soon as the game thread signals it’s time to render a frame, or when there’s a vsync and therefore a good time to swap buffers. So I’d have to either use a busy loop or limit it to ~60Hz and just hope that doesn’t cause rendering to lag too far behind the game thread.

dougbinks wrote on Thursday, October 03, 2013:

Multi-threading in general is definitely a good idea. The issue I’m mentioning is that you’re unlikely to gain anything by putting your OpenGL command thread on a separate thread from the one the context was created on, and you may actually loose by doing so.

FYI GLFW doesn’t provide threading and synchronisation functions as it’s a windowing abstraction API, but there are other libraries for this. I’ve rolled my own which I may open source if I get time. Intel’s Threaded Building Blocks (TBB) provides a robust cross platform tasking system and threading library which has been used in several AAA games, and has plenty of documentation and samples. Using tasks helps to solve some of the synchronization and busy wait issues, but it does take a while to get your head around.