How does one separate game logic and rendering? I know there seem to already be questions on here asking exactly that but the answers are not satisfactory to me.

From what I understand so far the point of separating them into different threads is so that game logic can start running for the next tick immediately instead of waiting for the next vsync where rendering finally returns from the swapbuffer call its been blocking on.

But specifically what data structures are used to prevent race conditions between the game logic thread and the rendering thread. Presumably the rendering thread needs access to various variables to figure out what to draw, but game logic could be updating these same variables.

Is there a de facto standard technique for handling this problem. Maybe like copy the data needed by the rendering thread after every execution of the game logic. Whatever the solution is will the overhead of synchronization or whatever be less than just running everything single threaded?

Those links give the typical end result that one would like, but don't elaborate on how to do it. Would you copy the entire scene graph each frame or something else? The discussions are too high level and vague.
–
user782220Jul 2 '12 at 6:37

I thought the links were fairly explicit about how much state is copied in each case. eg. (from 1st link) "A batch contains all the information necessary to draw a frame, but does not contain any other game state." or (from 2nd link) "Data still needs to be shared however, but now instead of each system accessing a common data location to say, get position or orientation data, each system has its own copy" (See especially 3.2.2 - State Manager)
–
DMGregoryDec 4 '13 at 2:32

4 Answers
4

What I have generally seen to handle logic/render thread communication is to triple buffer your data. This way the render thread has say bucket 0 it reads from. The logic thread uses bucket 1 as it's input source for the next frame and writes the frame data to bucket 2.

At sync points, the indices of what each of the three buckets mean are swapped so that the next frame's data is given to the render thread and the logic thread can continue forward.

But there isn't necessarily a reason to split rendering & logic into their respective threads. You can in fact keep the game loop serial and decoupling your render frame rate from the logic step using interpolation. To take advantage of multi-core processors using this kind of setup is where you would have a thread pool that operates on groups of tasks. These tasks can be simply things such as rather than iterate a list of objects from 0 to 100, you iterate the list in 5 buckets of 20 across 5 threads effectively increasing your performance but not over complicating the main loop.

I've been working on the same thing. The additional concern is that OpenGL (and to my knowledge, OpenAL), and a number of other hardware interfaces, are effectively state machines that do not get along well with being called by multiple threads. I don't think their behavior is even defined, and for LWJGL (possibly also JOGL) it often throws an exception.

What I ended up doing was creating a sequence of threads that implemented a specific interface, and loading them onto a control object's stack. When that object got a signal to shut down the game, it would run through each thread, call an implemented ceaseOperations() method, and wait for them to close before closing itself. Universal data that could be relevant to rendering sound, graphics, or any other data is kept in a sequence of objects that are volatile, or universally available to all threads but never kept in thread memory. There's a slight performance penalty there, but used properly, it has allowed me to flexibly assign audio to one thread, graphics to another, physics to yet another, and so forth without tying them into the traditional (and dreaded) "game loop."

So as a rule, all OpenGL calls go through the Graphics thread, all OpenAL through the Audio thread, all input through the Input thread, and all that the organizing control thread needs to worry about is thread management. Game state is held in the GameState class, which they can all take a look at as they need to. If I ever decide that, say, JOAL has gotten dated and I want to use the new edition of JavaSound instead, I just implement a different thread for Audio.

Hopefully you see what I'm saying, I have a few thousand lines on this project already. If you would like me to try and scrape together a sample, I'll see what I can do.

The problem you'll eventually face is this setup doesn't scale particularly well on a multi-core machine. Yes, there are aspects of a game which generally are best served in their own thread such as audio but much of the remainder of the game loop can actually be managed serially in conjunction with thread pool tasks. If your thread pool supports affinity masks, you can easily queue up say render tasks to be executed on the same thread and have your thread scheduler manage thread work queues and do work stealing as needed giving you multi-threading and multi-core support.
–
crancranDec 16 '13 at 5:02

Usually, the logic that deals with graphics rendering passes (and their schedule, and when they're gonna run, etc) is handled by a separate thread. However that thread is already implemented (up and running) by the platform you use to develop your game loop (and game).

So in order to obtain a game loop where the game logic updates independently of the graphics refresh schedule you don't need to make extra threads, you just tap into the already existing thread for said graphics updates.

This depends on what platform you're using. For example:

if you're doing it in most Open GL related platforms (GLUT for C/C++, JOLG for Java, Android's OpenGL ES related Action) they will usually give you a method/function which is periodically called by the rendering thread, and which you can integrate into your game loop (without making the gameloop's iterations dependent on when that method is called). For GLUT using C, you do something like this:

glutDisplayFunc(myFunctionForGraphicsDrawing);

glutIdleFunc(myFunctionForUpdatingState);

in JavaScript, since there's no multi-threading (that you can reach programmaticaly) you can use the "requestAnimationFrame" mechanism to get notified when a new graphics rendering will be scheduled, and do your game state updates accordingly.

Basically what you want is a mixed step game loop: you have some code that updates the game state, and which is called inside the main thread of your game, and you also want to periodically tap into (or be called back by) the already existing graphics rendering thread for heads up as to when it's time to refresh the graphics.

Locking variables makes sure they don't change while running the code following it, so variables don't get changed by your updating thread while you are rendering them (in fact they DO change, but from the standpoint of your rendering thread they don't).
You have to watch out with the synchronized keyword in Java though, since it only makes sure the pointer to the variable/Object doesn't change. The attributes can still change without changing the pointer. To contemplate for this, you could copy the object yourself or call synchronized on all attributes of the object you don't want to change.

Mutexes are not necessarily the answer here because the OP would not only needs to decouple game logic and rendering but they also want to avoid any stalling of one thread's ability to move forward in it's processing regardless of where the other thread may currently be in it's processing loop.
–
crancranDec 5 '13 at 0:26