Sign in to follow this  

Win32 Game Loop For Fixed Frame Rate

This topic is 3717 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

I'm trying my hand at Win32 OpenGL programming and can't seem to get my game loop to work properly. It seems to only draw the first frame and then never draw again unless I move the window. Any ideas?

	DWORD target = (DWORD)(1000 / targetFrameRate);

	while (!done)
	{
		DWORD starting_point = GetTickCount();

		if (PeekMessage(&msg, windowHandle, 0, 0, PM_REMOVE))
		{
			if (msg.message == WM_QUIT || 
				msg.message == WM_DESTROY || 
				msg.message == WM_CLOSE)
			{
				Exit();
				break;
			}

			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		Update((float)(GetTickCount() - starting_point) / 1000.0f);

		Draw();
		SwapBuffers(deviceContext);

		while ((GetTickCount() - starting_point) < target);
	}

Share this post


Link to post
Share on other sites
There are a few things wrong:
  1. This line:
        Update((float)(GetTickCount() - starting_point) / 1000.0f); 
    passes in only the time spent processing up to 1 message and does not include the time spent in the spin lock.
  2. You should not process only 1 message per frame. That can lead to problems. What happens if two messages are generated every frame? You need to process all the messages every frame.
  3. You should only exit when WM_QUIT is received. WM_DESTROY and WM_CLOSE are part of cleaning up and you need to let them be processed normally.

Share this post


Link to post
Share on other sites
Thanks for those tips. I'm still getting used to not only Win32 programming, but making game loops and things. I've been using XNA for a long time, but I've always wanted to make a full game engine in C++, so I've started dabbling.

Any ideas why my loop only redraws things when I move the window? Here's what I've changed my loop to (I realized I didn't even need to check the message there because my WndProc function handles all of those anyway):

DWORD target = (DWORD)(1000 / targetFrameRate);

while (!done)
{
DWORD starting_point = GetTickCount();

while (PeekMessage(&msg, windowHandle, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

Update((float)(GetTickCount() - starting_point) / 1000.0f);

Draw();
SwapBuffers(deviceContext);

while ((GetTickCount() - starting_point) < target);
}


My default value for targetFrameRate (which is an int) is 60. 'target' then computes to 16. So it's not getting stuck in that second loop. I'm running out of ideas.

Share this post


Link to post
Share on other sites
Hm. Ok. Can anyone show me what a good fixed-rate game loop would look like? This is what mine currently has evolved to, but it's pretty glitchy so I'm sure this isn't what it should be:

DWORD target = (DWORD)(1000 / targetFrameRate);

while (!done)
{
DWORD starting_point = GetTickCount();

do
{
if (PeekMessage(&msg, windowHandle, 0, 0, PM_NOREMOVE))
{
GetMessage(&msg, windowHandle, 0, 0);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} while ((GetTickCount() - starting_point) <= target);

Update(1.0f / (float)targetFrameRate);

Draw();
SwapBuffers(deviceContext);
}

Share this post


Link to post
Share on other sites
I don't believe that PeekMessage blocks. From MSDN:
"Unlike GetMessage, the PeekMessage function does not wait for a message to be posted before returning."

Here's my windows game loop:


if (GAME.OneTimeInit(hWnd, hDC))
{
do // Restart game
{
GAME.Reset();
GAME.EnterMenuState();

do // Main loop
{
// Do Windows housekeeping: process any waiting windows messages.
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
GAME.ExitApp();
exitCode = msg.wParam;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else if (GAME.IsAppActive()) // No windows messages waiting: do our own stuff.
{
GAME.Run();

if (gKeys[VK_ESCAPE]) // ESC signalled a quit
{
// GAME.HandleEscapeKey();
}
}
else
{
WaitMessage(); // Don't eat all the bloody CPU when minimised!
}

} while (GAME.IsGameRunning());

} while (GAME.IsAppRunning());
}



I.e. I leave it to the game object to sort out timing stuff.

Then the Game main loop:


mElapsedGameTimeMs = MeasureElapsedTime();

// Execute a number of fixed time step physics iterations for the real elapsed time.
int loopCount = 0;
while (mElapsedGameTimeMs >= KTickTimeMs && loopCount < KMaxPhysicsIterationCount)
{
WORLD().Update(KTickTimeMs);
mCollisionManager.CheckCollisions();
mElapsedGameTimeMs -= KTickTimeMs;
loopCount++;
}

if (KTickTimeMs*loopCount > 0)
{
// Update even if particleLevel is 0, to flush existing particles.
PARTICLES().Update(KTickTimeMs*loopCount);
}

mSceneManager.Update(KTickTimeMs*loopCount, mCamera.WorldPosn(), InterfaceMode());

SOUNDSYS().Update();

// Handle any accumulated time debt:
if (mElapsedGameTimeMs >= KTickTimeMs)
{
// Physics running slower than real-time:
if (!mNetworkGame)
{
// Discard time debt, resulting in game 'falling behind' real-time.
mElapsedGameTimeMs = 0;
}
else
{
// Network synch problems!! Kick/slow others down/what?!
}
}

// Render if a physics update occurred, else no point (until we do interpolation that is)
// if (loopCount > 0 || mTimeCompression==0)
{
Render();
SwapBuffers(mHDC); // Swap screen buffers
}


Share this post


Link to post
Share on other sites
Here's me:
#define MAX_FPS 85
#define MIN_DT (1000 / (MAX_FPS))

do {
static unsigned lastTime;
unsigned time, dt;

if (!active) {
Sleep(5);
else {
Sleep(0);
}

do {
while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
time = timeGetTime();
if (!lastTime) {
lastTime = time;
}
dt = time - lastTime;
} while (dt < MIN_DT);
lastTime = time;

DoStuff(dt);
} while (1);

Share this post


Link to post
Share on other sites
Instead of looping doing nothing and wasting cycles other processes could be using, figure out how many ms you have to kill and Sleep() them. Otherwise you're just spinning your wheels for nothing.

Also, if you're going to use Sleep() you need to make it more accurate with timeBeginPeriod(2). Also I would drop the GetTickCount() for timeGetTime() as it will now have 2ms resolution which is, if I recall right a lot better then GetTickCount() which is like 10-15ms? The two was just an example btw, I currently use 1 but someone said this was bad. But even 5ms should be good for you.

Even a Sleep(0) is something you should do if you have no time, it gives a tiny bit back to the OS, the rest of your current timeslice if I do recall correctly.

Share this post


Link to post
Share on other sites
That's definitely working a lot nicer. Very smooth movement of my objects as opposed to the glitchy movement before. Unfortunately now when I hit the little X close button, it freezes. I guess I can't win. :)

Edit: And it freezes when I move the window. And if I minimize it. Is my game loop ruining my window messages?

done = false;
MSG msg;

timeBeginPeriod(2);

DWORD target = (DWORD)(1000 / targetFrameRate);

while (!done)
{
DWORD startingPoint = timeGetTime();

while (PeekMessage(&msg, windowHandle, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
done = true;
break;
}

TranslateMessage(&msg);
DispatchMessage(&msg);
}

DWORD sleepLength = target - (timeGetTime() - startingPoint);
Sleep((sleepLength > 0) ? sleepLength : 0);

Update(1.0f / (float)targetFrameRate);

Draw();
SwapBuffers(deviceContext);
}



[Edited by - NickGravelyn on September 17, 2007 1:17:23 PM]

Share this post


Link to post
Share on other sites
Here's my message handler:

LRESULT CALLBACK Game::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(WM_QUIT);
break;

case WM_CLOSE:
case WM_QUIT:
Exit();
return 0;

case WM_KEYDOWN:
case WM_KEYUP:
setKeyboardKeyState((int)wParam, (message == WM_KEYDOWN));
return 0;

default:
return DefWindowProc(hWnd, message, wParam, lParam);
}

return 0;
}


and my current game loop:

done = false;
MSG msg;

timeBeginPeriod(2);

DWORD target = (DWORD)(1000 / targetFrameRate);

while (!done)
{
DWORD startingPoint = timeGetTime();

while (PeekMessage(&msg, windowHandle, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

if (done)
break;

DWORD sleepLength = target - (timeGetTime() - startingPoint);
Sleep((sleepLength > 0) ? sleepLength : 0);

Update(1.0f / (float)targetFrameRate);

Draw();
SwapBuffers(deviceContext);
}

Share this post


Link to post
Share on other sites
Another one fixed frame rate example (that I'm use in my programs):


int fix_fps = 30;
float fFPS;
int sleep = 0;
static LARGE_INTEGER base;
static LARGE_INTEGER freq;
QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
QueryPerformanceCounter((LARGE_INTEGER*)&base);
while (!done)
{


if (PeekMessage(&msg, windowHandle, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT ||
msg.message == WM_DESTROY ||
msg.message == WM_CLOSE)
{
Exit();
break;
}

TranslateMessage(&msg);
DispatchMessage(&msg);
}
static LARGE_INTEGER cur;
QueryPerformanceCounter(&cur);

// calculate fps
if(cur.QuadPart>=base.QuadPart)
{

fFPS = (float)((double)freq.QuadPart/(double)(cur.QuadPart-base.QuadPart));
}
else
{

fFPS = (float)((double)freq.QuadPart/(double)(base.QuadPart-cur.QuadPart));
}

if(fix_fps > 0.0f)
{
if((int)fFPS < fix_fps )
sleep--;
else if((int)fFPS > fix_fps )
sleep++;

// sleep cant be negative
if(sleep < 0) sleep = 0;

// sleep
Sleep(sleep);
}
Draw();
SwapBuffers(deviceContext);


}


Share this post


Link to post
Share on other sites

This topic is 3717 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