Using multithreading with openGL

Speaking in the context of a game based on the openGL renderer:

Let's assume there are two streams:

  • which updates gameLogic and physics, etc. for game objects

  • which makes drawGL calls for each game object based on the data in game objects (this thread 1 keeps updating)

If you don't have two copies of each game object in the current game state, you will have to pause Thread 1 while Thread 2 will callbacks, otherwise the game objects will update in the middle of the call for that object! which is undesirable!

but stopping thread 1 to safely make draw calls from thread 2 kills the whole multithreading / collaboration target

Is there a better approach for this other than using hundreds or thousands or synchronization objects / fences so that multi-core architecture can be used for performance?

I know I can still use multiThreading to load textures and compile shaders for objects that are not yet part of the current game state , but how do I do this for active / visible objects without causing a draw and update conflict?

+3


source to share


2 answers


The usual approach is for the modeling thread, after the game step completes, to commit the state to an intermediate buffer and then signal the render thread. Because OpenGL runs asynchronously, the render thread needs to execute fairly quickly, thereby freeing up the intermediate buffer for the next state.



In any case, you shouldn't render directly from the game state, since the rendering has to do its job and what the simulation produces is not always the same. Therefore, some matching may be required anyway.

+3


source


This is a fairly common question you are asking. If you ask 10 different people, you will probably get 10 different answers. I've implemented something similar in the past, and here's what I did (after a long series of optimization loops).

Your model update loop, which is running on a background thread, should look something like this:

while(true)
{
  updateAllModels()
}

      

As you said, this will cause a problem when the GL thread starts up as it can render a model based view very well, which is half way through rendering, which can lead to UI crashes at best.

The direct way to solve this problem would be to synchronize the update:

while (true)
{
  synchronized(...)
  {
    updateAllModels();
  }
}

      

If the object you are synchronizing with is the same object that you will use to synchronize the drawing method.

We now have an improved method that won't crash the UI, but general rendering is likely to result in a very severe performance hit, as all rendering has to wait for all model updates to complete, or vice versa. updating models will need to wait for the entire drawing to complete.



Now think, what do we need to sync?

In my application (space game), when updating models, I needed to compute vectors, check for collisions and update all object positions, rotations, scale, etc.

Of all these things, the only gaze concerns are position, rotation, scale and a few other small considerations that a user interface needs to know in order to display the game world correctly. The rendering process doesn't care about the game object vector, AI code, collision tests, etc. With that in mind, I changed the update code to look something like this:

while (true)
{
  synchronized(...)
  {
     updateVisibleChanges(); // sets all visible changes - positions, rotations, etc
  }

  updateInvisibleChanges(); // alters vectors, AI calculations, collision tests, etc  
}

      

As before, we keep the update and drawing methods in sync, but this time the critical section is much smaller than before. Basically, the only things that need to be set in the updateVisibleChanges method are things related to position, rotation, scale, etc. Objects to be rendered. All other calculations (which are usually the most exhaustive) are done afterwards and do not stop rendering.

An added bonus from this method is when you make invisible changes you can be sure that all objects are in the position they should be (which is very useful for accurate collision testing). For example, in a method before the last one, object A is moved, then object A checks for collision with object B, which has not been moved yet. Perhaps if object B had moved before object A tested the collision, there would have been a different result.

Of course the last example I showed is not perfect - you still have to hang up the render method and / or the updateVisible method to avoid collisions, but I'm afraid this will always be a problem, and the key is minimizing the amount of work you do nor in streaming mode.

Hope it helps :)

+1


source







All Articles