cse 380 – computer game programming render threading portal, by valve,...
TRANSCRIPT
CSE 380 – Computer Game ProgrammingRender Threading
Portal, by Valve, http://orange.half-life2.com/portal.html
What is multi-tasking?• Doing many things simultaneously
• On a computer, Operating Systems run multiple programs simultaneously
• When you’re working, how many different applications do you have open at one time?
• Games use threads to do multiple things simultaneously– Why?
• better utilization• better framerates/performance
Threads• Individual programs appear to do multitasking
• Each task is called a thread
• Programs that can run more than one thread at once are called multithreaded– a program is broken up into a number of threads of
control– all threads share the same address space (data).– threads communicate with each other via the shared
objects– operations in threads are scheduled by a thread
scheduler (typically OS)
Why do we get a performance advantage?
• Imagine you wanted to make a CAR
• Approach 1:– Step 1: make 4 tires– Step2: when done with Step 1, Make a windshield– …– Step 1,000,000,000: Assemble Car
1.Make 4 Tires
2.Make a
windshield
1,000,000,000.Assemble Car…
Why do we get a performance advantage?
• Imagine you wanted to make a CAR
• Approach 2:– Step 1: Simultaneously have different
workers/suppliers make the windshield, engine, tires, etc.
– Step 2: Assemble car as parts are available
1a.Make 4 Tires
1b.Make a
windshield
2.Assemble Car
…
How do you check for broken eggs?• You buy a carton of eggs
• You don’t want any broken
• Option 1:
– Take out and check them one at a time
– Poor utilization. Why?
• because 11 eggs are idle while we check one of them
• Option 2:
– Open carton and look at top
– Flip carton upside down
– Open carton and look at bottom
– High utilization. Why?
• eggs are never waiting for other eggs that have been removed and are being examined
Utilization
• You don’t want your program to be idle when there’s work to be done. Like:– while our game loop is sleeping– while we’re retrieving user input– while expensive rendering is going on
• For multi-core:– while one core is doing something the others shouldn’t
be idle
Typical Thread Tasks
• Rendering– separate thread only performs rendering– expensive operation
• Networking and I/O– threads may wait for messages– they check buffer continuously for immediate response
• Physics– do pathfinding on one CPU and physics on another
Using a Render Thread
• One approach:– Thread 1: all game updates (AI, Collisions, etc.)– Thread 2: all rendering, and only rendering
• Strategy:– Each frame:
• Thread 1 builds a RenderList
• Simultaneously Thread 2 renders the RenderList Thread 1 had built the previous frame
– Note: this means we’ll need 2 RenderLists
2 RenderLists
• RenderList *renderList1, *renderList2;
• Each frame:
Thread 1
• If time to process data:
• Set time to process data to false
• Process data and build render list 1
• Swap render lists 1 and 2
• Set time to render to true
Thread 2
• If time to render:
• Set time to render to false
• Render render list 2
• Set time to process data to true
Race Conditions
• Threads are independent pieces of control operating on shared data.
• If we’re not careful:– thread 2 could overwrite thread 1’s changes
• data corruption
– thread 2 could try to use thread 1’s changes prematurely
– or vice versa
• One thread corrupting shared data for the other threads is called a race condition
• Common to Consumer – Producer relationships– one thread is the producer (thread 1)
– one thread is the consumer (thread 2)
How do we prevent race conditions?• Mutexes. What’s that?
– A mutex puts a lock on a piece of data
– 1st thread reserves a piece of data
– 2nd thread cannot use it until the 1st thread is done with it
• Windows Mutexes:– WaitForSingleObject – requests a reservation
• http://msdn2.microsoft.com/en-us/library/ms687032(VS.85).aspx– ReleaseMutex – releases a reservation
• http://msdn2.microsoft.com/en-us/library/ms685066(VS.85).aspx
• What should we be locking?
– anything used by both threads
• RenderLists, TextureManagers, text to render, etc.
Using Mutexes (type HANDLE)RenderList *renderList;
HANDLE renderListMutex;
…
// WHEN WE WANT TO USE renderList
WaitForSingleObject(renderListMutex, INFINITE);
... // USE renderList
ReleaseMutex(renderListMutex);
// NOW THE OTHER THREAD CAN USE IT
Complication: Deadlock
• What’s that?– Thread 1 has a lock on object A– Thread 1 is waiting for object B– Thread 2 has a lock on object B– Thread 2 is waiting for object A
• Can bring a program to a halt
• How do we resolve it?– prevent it from happening through a well thought-out
mutex/lock strategy
Platform dependency & threads
• One drawback of threads
• Thread libraries are typically system dependent
• For example:– C++’s CreateThread function
• creates threads
• C/C++ runtime (CRT) will not get properly initialized on Windows
Windows Thread Management• _beginthreadex
– starts a thread
– you provide the method to run in that thread• unsigned __stdcall MyMethod ( void* pArguments )• typically would have a loop tied to game loop timing
– http://msdn2.microsoft.com/en-us/library/kdzttdcb(VS.80).aspx
• _endthreadex– stops a thread
– http://msdn2.microsoft.com/en-us/library/hw264s73(VS.80).aspx
So we’ll need a Windows Thread class• GameRenderThread – could be a virtual class with
just method headers
• WindowsGameRenderThread – could be a Windows implementation of GameRenderThread
GameRenderThread.hclass GameRenderThread
{
public:
virtual void kill()=0;
virtual void runFromMainThread(Game *game)=0;
virtual void start(Game *game)=0;
};
WindowsGameRenderThread.hclass WindowsGameRenderThread : public GameRenderThread
{
private:
HANDLE renderThreadHandle;
DWORD renderThreadID;
Game *game;
bool timeToProcessData;
bool timeToRender;
HANDLE timeToProcessDataMutex;
HANDLE timeToRenderMutex;
HANDLE renderingRenderListMutex;
…
WindowsGameRenderThread.cppWindowsGameRenderThread::WindowsGameRenderThread()
{
renderThreadHandle = NULL;
timeToProcessData = true;
timeToRender = false;
timeToProcessDataMutex = CreateMutex(0, NULL,
L"timeToProcessDataMutex");
timeToRenderMutex = CreateMutex(0, NULL,
L"timeToRenderMutex");
renderingRenderListMutex = CreateMutex(0, NULL,
L"renderingRenderListMutex");
}
WindowsGameRenderThread.cppvoid WindowsGameRenderThread::start(Game *game)
{
// START THE RENDER THREAD
HANDLE renderThreadHandle;
unsigned int renderThreadID;
renderThreadHandle = (HANDLE)_beginthreadex( NULL, 0, &RenderThread, game, 0, &renderThreadID );
setRenderThreadHandle(renderThreadHandle);
setRenderThreadID(renderThreadID);
}
void WindowsGameRenderThread::runFromMainThread(Game *game)
{
WaitForSingleObject(timeToProcessDataMutex, INFINITE);
if (timeToProcessData)
{
timeToProcessData = false;
game->processGameData();
ReleaseMutex(timeToProcessDataMutex);
// NOW LET'S SWAP THE RENDER LISTS
WaitForSingleObject(renderingRenderListMutex, INFINITE);
GameGraphics *graphics = game->getGraphics();
graphics->swapRenderLists();
ReleaseMutex(renderingRenderListMutex);
WaitForSingleObject(timeToRenderMutex, INFINITE);
timeToRender = true;
ReleaseMutex(timeToRenderMutex);
}
else
ReleaseMutex(timeToProcessDataMutex);
}
Called each frame by
main thread
unsigned __stdcall RenderThread( void* pArguments )
{
Game *game = (Game*)pArguments;
GameGraphics *graphics = game->getGraphics();
WindowsGameRenderThread *thread = (WindowsGameRenderThread*)game->getRenderThread();
while (game->isGameActive())
{
WaitForSingleObject(thread->getTimeToRenderMutex(), INFINITE);
if (thread->isTimeToRender())
{
thread->setTimeToRender(false);
ReleaseMutex(thread->getTimeToRenderMutex());
WaitForSingleObject(thread->getRenderingRenderListMutex(), INFINITE);
int gameMode = game->getGameMode();
if ((gameMode != LEVEL_LOADING_GAME_MODE)
&& (gameMode != LEVEL_UNLOADING_GAME_MODE))
graphics->renderGameInRenderThread(game);
Render Thread
ReleaseMutex(thread->getRenderingRenderListMutex());
WaitForSingleObject(thread->getTimeToProcessDataMutex(), INFINITE);
thread->setTimeToProcessData(true);
ReleaseMutex(thread->getTimeToProcessDataMutex());
}
else
{
ReleaseMutex(thread->getTimeToRenderMutex());
}
}
_endthreadex( 0 );
return (26);
}
Render Thread
References• Coding For Multiple Cores on Xbox 360 and Microsoft Windows
– http://msdn2.microsoft.com/en-us/library/bb204834(VS.85).aspx