Skip to main content
GameDev.net gamedev.net
🔒 Locked

C++ game loop waiting vs threading

Started by Storyyeller Feb 25, 2011 at 4:12 PM 8 replies 6.6k views
Original Post
Storyyeller
Storyyeller
So I have a game that I wrote in C++ with SDL. I originally wrote the main game loop with a form of busy waiting, since that was the easiest thing to do. However, now I'm wondering whether I should switch to some other form.

Basically, the way it goes is that it loops repeatedly, checking for input and processing it, until it detects that enough time has elapsed for the next frame. If it's behind schedule, it then attempts to catch up with a second frame, before going back to the input processing part. The game is intended to run at 62.5FPS (16ms per frame)

The problem is that this uses a lot of computer resources, even when the game is paused. The only alternative I can think of is to add threading, and make the thread sleep periodically. However, multithreading is relatively difficult in C++. Also, another worry I have is that the sleep function has large inaccuracies. So if I rely on it to time my game loop, then there might be large variations in timing between frames.

What should I do? What do you recommend?


void GameLoop()
{
Debug("Game loop begun");
Uint32 lastphysicstime = SDL_GetTicks();
Uint32 newtime = 0;

while(!hasquit)
{
ProcessInput();
newtime = SDL_GetTicks();

//The physics happens at a set framerate
if ((newtime - lastphysicstime)>=MSPERPHYSICFRAME)
{
PhysicsTick(Paused);
RenderEverything(screen);
lastphysicstime += MSPERPHYSICFRAME;
}
if ((newtime - lastphysicstime)>=MSPERPHYSICFRAME)
{
PhysicsTick(Paused);
RenderEverything(screen);
lastphysicstime = SDL_GetTicks();
}
}

Debug("Gameloop returned");
}
I trust exceptions about as far as I can throw them.
Faelenor
Faelenor
Hmm, usually game loops are similar to this:


do
{
AIUpdate();
Physics();
Rendering();
WaitForVBlank();
}


The WaitForVBlank() function will wait for a vertical redraw of the screen, preventing tearing and leaving CPU to the system. As I don't know SDL, you should look in the SDK which function is doing this.

It's not a good idea to sync everything on an arbitrary fixed value like 62.5 FPS.

-Francis
frob
frob
You are correct that multithreading is not easy in C++, and is a topic best avoided by beginners. It is very easy to accidentally deadlock, livelock, or otherwise resource-starve your application.

Your game loop is basically the right way for non-threaded applications to run. There are many ways to implement it, but they basically boil down to this:


RunBeforeEveryIteration(); // In-game profiler and other useful stuff.

if(shouldRunUpdate)
{
RunPreUpdate( elapsed );
RunUpdate( elapsed );
RunPostUpdate( elapsed );
}
if(shouldRunDraw)
{
RunPreDraw( elapsed );
RunDraw( elapsed );
RunPostDraw( elapsed );
}

RunAfterEveryIteration(); // In-game profiler and other useful stuff.

// If you want...
WaitForSomething();



That will let you control the frame rate and update rate independently. You don't need to wait for vblank or other system event or timer, although you can do it if you want.

Inside the main functions you can have code like this:

RunUpdate( float elapsed )
{
// Always run these updates
UpdateUI( elapsed );
UpdateNetwork( elapsed );
...

// Don't run these when we are paused.
if( false == gameSimulator.IsPaused() )
{
UpdateAI( elapsed );
UpdatePathFinding (elapsed );
UpdatePhysics( elapsed );
UpdateSpawnPoints( elapsed );
...
}
}


Of course you can put your pause detection logic anywhere you want, but
rip-off
rip-off
If your game is paused you might consider moving to SDL_WaitEvent() rather than using SDL_PollEvent(). SDL_WaitEvent() will block until something interesting happens, which might include the user pressing the "unpause" key/button.
Storyyeller
Storyyeller
The problem is that the game isn't completely paused, as the menus are still running. And some of the menus are animated, so I can't pause it completely.

Here's my update function. Only the actual game (encapsulated in a class called Entity Manager) is paused.


void PhysicsTick(bool PauseEM)
{
menusystem->Update();
#ifdef RE_LOGGING
mytimer.reset();
#endif

if (!PauseEM && myEntityManager)
{
myEntityManager->Update();
}
}
I trust exceptions about as far as I can throw them.
rip-off
rip-off
In that cause, when the game is paused consider using SDL_Delay(). It should ease the processor load.
Storyyeller
Storyyeller
How long does it actually wait for? It sounds like there's no way to ensure that it actually waits for the amount of time you want it to. Is this a problem in practice?
I trust exceptions about as far as I can throw them.
boogyman19946
boogyman19946
Looks like SDL_Delay() takes an argument of type Uint32 that specifies the amount, in milliseconds, of time that it should wait.
Yo dawg, don't even trip.
rip-off
rip-off
If the game is paused you probably have no real time guarantees you want to hit. It will suffice for keeping the animation running while the user checks their email or gets a cup of coffee or otherwise deals with real life.

SDL_Delay() doesn't make the situation any worse. At any time your program could be interrupted by the OS for some other process - for an arbitrary amount of time. All SDL_Delay() does is say "hey OS, I have no work to do for at least X milliseconds, feel free to use the time for something else". The only real complication is that SDL_Delay() is limited by the scheduler frequency, that is if the scheduler lets tasks run for 10 milliseconds (a common value) before interrupting them, then passing values of less than 10 will usually result in longer delays than anticipated.

But try it out, I expect you won't have any problems with it. Essentially though you cannot have it both ways. If you think that at any moment you need to react to the user (or whatever) then you'll need to busy wait. If not, you shouldn't care about any variations in timing caused by SDL_Delay().

Another potential solution is to use SDL_WaitEvent() and have an SDL_Timer() set to deliver user events (or possibly hijack SDL_VIDEOEXPOSE) to trigger the animation at a reasonable frequency. I haven't looked into detail at the implementation of SDL_Timers, they might incur additional background processing on some platforms which might be counter productive to the goal of reducing processor load.
Prune
Prune
Even input events generally don't need to be processed at anything over 100 fps. In your processing thread, check the elapsed time during the run through the loop at its end and insert a delay that's equal to the difference between the elapsed and target time; this avoids wasting more CPU than is necessary. Note that you will need to use a high precision timer (easy enough to do in Windows or Linux).
"But who prays for Satan? Who, in eighteen centuries, has had the common humanity to pray for the one sinner that needed it most?" --Mark Twain

~~~~~~~~~~~~~~~Looking for a high-performance, easy to use, and lightweight math library? http://www.cmldev.net/ (note: I'm not associated with that project; just a user)

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.