10
\$\begingroup\$

Talking in context of a game based on openGL renderer :

Let's assume there are two threads :

  1. Updates the game logic and physics etc. for the in game objects

  2. Makes openGL draw calls for each game object based on data in the game objects (that thread 1 keeps updating)

Unless you have two copies of each game object in the current state of the game you'll have to pause Thread 1 while Thread 2 makes the draw calls otherwise the game objects will get updated in the middle of a draw call for that object, which is undesirable!

But stopping thread 1 to safely make draw calls from thread 2 kills the whole purpose of multithreading/concurrency

Is there a better approach for this other than using hundreds or thousands or sync objects/fences so that the multicore architecture can be exploited for performance?

I know I can still use multithreading for loading texture and compiling shaders for the objects which are yet to be the part of the current game state but how do I do it for the active/visible objects without causing conflict with draw and update?

What if I use separate sync lock in each of the game object? This way any thread would only block on one object (ideal case) rather than for the whole update/draw cycle! But how costly is taking locks on each object (game may have a thousand objects)?

\$\endgroup\$
  • \$\begingroup\$ 1. Assuming there will be just two threads doesn't scale well, 4 cores is very common and will only increase. 2. Best practice strategy answer: apply Command Query Responsibility Segregation and Actor Model / Component Entity System. 3. Realistic answer: just hack it the traditional C++ way with locks and stuff. \$\endgroup\$ – Den Jan 5 '15 at 16:58
  • \$\begingroup\$ One common datastore, one lock. \$\endgroup\$ – Justin Jan 5 '15 at 17:43
  • \$\begingroup\$ @justin just as I said with a sync lock The only advantage of the multithreading will be brutally murdered in daylight, the update thread would have to wait until draw thread make calls for all objects then the draw thread will wait until update loop completes updating stuff! which is worse than single threaded approach \$\endgroup\$ – Allahjane Jan 5 '15 at 18:10
  • 1
    \$\begingroup\$ It seems like multithreaded approach in games with async graphics api (e.g openGL) are still active topic and there is no standard or near perfect solution this \$\endgroup\$ – Allahjane Jan 5 '15 at 18:11
  • 1
    \$\begingroup\$ The datastore that is common to your engine and your renderer should not be your game objects. It should be some representation that the renderer can process as quickly as possible. \$\endgroup\$ – Justin Jan 6 '15 at 10:48
12
\$\begingroup\$

The approach you've described, using locks, would be very inefficient and most likely slower than using a single thread. The other approach of keeping copies of data in each thread would probably work well "speed-wise", but with a prohibitive memory cost and code complexity to keep the copies in sync.

There are several alternative approaches to this, one popular solution for multi-threaded rendering is by using a double-buffer of commands. This consists of running the renderer back-end in a separate thread, where all draw calls and communication with the rendering API are performed. The front-end thread that runs the game logic communicates with the back-end renderer via a command buffer (double-buffered). With this setup, you only have one synchronazation point at the completion of a frame. While the front-end is filling one buffer with render commands, the back-end is consuming the other. If both threads are well balanced, none should starve. This approach is suboptimal however, since it introduces latency in the frames rendered, plus, the OpenGL driver is likely to be already doing this in its own process, so performance gains would have to be carefully measured. It also only uses two cores, at best. This approach has been used in several successful games though, such as Doom 3 and Quake 3

More scalable approaches that make better use of multi-core CPUs are the ones based on independent tasks, where you fire an asynchronous request that gets serviced in a secondary thread, while the thread that fired the request continues with some other work. The task should ideally have no dependencies with the other threads, to avoid locks (also avoid shared/global data like the plague!). A task-based architectures is more usable in localized parts of a game, such as computing animations, AI pathfinding, procedural generation, dynamic loading of scene props, etc. Games are naturally event-full, most kinds of events are async, so it is easy to make them run in separate threads.

Finally, I recommend reading:

\$\endgroup\$
  • \$\begingroup\$ just curious! how do you know Doom 3 used this approach ? I thought developers never let out there techniques! \$\endgroup\$ – Allahjane Jan 5 '15 at 18:12
  • 4
    \$\begingroup\$ @Allahjane, Doom 3 is Open Source. In the link I've provided, you'll find a review of the overall architecture of the game. And you're mistaken, yes it is rare to find fully Open Source games, but developers usually expose their techniques and tricks in blogs, papers and events such as GDC. \$\endgroup\$ – glampert Jan 5 '15 at 18:16
  • \$\begingroup\$ Hmm.This was an awesome read! thanks for making me aware of this! You get the tick :) \$\endgroup\$ – Allahjane Jan 5 '15 at 18:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.