Sign in to follow this  

Decoupling the rendering thread and the game thread

This topic is 2633 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

From watching the Android game programming Google conference, they mention that it is a good idea to separate the rendering and the game logic, ie via threads. My question is, how does the rendering thread knows that when it is rendering the gaming data, that it is not being altered at the same time?

Has anyone used this type of method before?

For reference here is the video:
http://www.youtube.com/watch?v=U4Bk5rmIpic

Share this post


Link to post
Share on other sites
You'll have to make a real good plan when splitting threads, otherwise get ready for a world of pain! As you mentioned, the trick is to prevent reading/writing at the same time. This can be done with "mutexes" / "critical sections". Basically you look a section in one thread so that another has to wait until the lock is gone.

var X : integer; // my shared global variable
<thread1>
enterCriticalSection( mutex_X_Handle );
x := something;
leaveCriticalSection( mutex_X_Handle ); // Release the lock

<thread2>
enterCriticalSection( mutex_X_Handle ); // If thread 1 locked via mutex_X, then wait
x := something_else;
leaveCriticalSection( mutex_X_Handle );

That gives a second problem, preventing not waiting too much for each other. The problem with sharing game-logic and rendering is that stuff such as matrices/positions are required a lot by both threads. AI routines such as pathfinding on the other hand can be seperated quite easily. One method is working with lists or "thread pools".

----------------------- PUTTING A REQUEST -----------------
<main Thread>
requestObjectt.startPos := position
requestObject.targetPos := position
requestObject.otherParameters := xyz
requestObject.requesterHandle := &my_AIobject;
// Put the request. Don't forget to lock the list while doing so!
list := pathFinding_RequestList.getLockedList();
list.add( requestObject );
pathFinding_RequestList.unlock;
// Go on with whatever you were doing... The result will come later

<Pathfinding thread>
// Check if there is a new request
list := pathFinding_RequestList.getLockedList();
if list.count > 0 then
newRequestObject := pathFinding_RequestList.items[0]; // FIFO
pathFinding_RequestList.unlock; // Unlock list again so that the other thread can

resultObject := pathfinder.solve( newRequestObject ); // Do the pathfinding
// When we are done, put it in a "result list"
list := pathFinding_ResultList.getLockedList();
list.add( resultObject );
pathFinding_ResultList.unlock;


----------------------- GETTING A RESULT -----------------
< main Thread >
// Poll for results
list := pathFinding_ResultList.getLockedList();
if list.count > 0 then begin
result := pathFinding_ResultList.items[0]; // FIFO
handleResult( result ); // Pass result to the requester
end;
pathFinding_ResultList.unlock;


The overlap time that both threads are working with on the same thing, the 2 lists in this case, is minimal. The real work is within the pathfinding itself, and this is nicely executed in the background without stalling other threads.


I think currently more and more graphical API's also offer routines to render stuff in another thread, but that requires knowledge about the specific API you'll be using.

Rick

Share this post


Link to post
Share on other sites
Separating these things does not require threads, nor is it usual to use threads to separate them. Just set up a main loop that interweaves calls to the top-level rendering and physics functions, and don't alter the data in the rendering functions.

Share this post


Link to post
Share on other sites
The best way to deal with this is to split your models into two(or three) parts.

What I have is:

1) A model class, eg static mesh. This contains data which does not changes, such as vertex buffers, material mappings etc. It can safely be used from any thread.

2) A model instance, this references the model and contains per instance data which often changes. For example the model transform, drawing mode etc. This class is used by the game/entity thread.

3) A model proxy class, this contains a copy of the rendering specific instance data. For example the transform for the current frame.

Then you have a pair of game loops something like this, one loop runs on the entity thread and the other on the game thread.

Render thread:

while(true)
{
Wait for entity thread to finish.

>> Update proxies from entity model instances by copying relevant data. eg the instance transform.

Signal entity thread that it can start processing.

Pre render setup, eg updating light and material bindings.
Draw Render proxies
}


Entity thread:

while(true)
{
Wait for signal from render thread to start

Perform entity update, physics etc using model instances.

Signal render thread that we are complete
}

Of course this can be extended to more threads, but typically this is more complicated and individual parts of each thread may be multi threaded.

For instance the render thread can start multiple threads to render shadow maps(easy since the proxies only change when they are updated after the entity thread finishes).

Or the entity thread can run the physics on multiple threads(probably with additional copies of the relevant state).

David

Share this post


Link to post
Share on other sites
Here is another thread on this topic I've had bookmarked for a while.

http://www.gamedev.net/community/forums/topic.asp?topic_id=554016

It is also worth checking out the sources for Replica Island. I don't recall offhand the exact approach it uses, but there is a separate rendering thread. The developer's blog and talks have also indicated this topic is one which he plans to integrate more thoroughly into the game.

http://replicaisland.googlecode.com/svn/trunk
http://replicaisland.blogspot.com/

Share this post


Link to post
Share on other sites

This topic is 2633 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this