Archived

This topic is now archived and is closed to further replies.

fps limit

This topic is 5046 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''ve been trying to limit my game speed to 20 fps, but some problems occured on the way. The way I want to limit fps is, that if time delta is less than a 0.05 (20 fps) new frame won''t be drawn. Game exections just goes and checks new messages and so on. Here is the code I''ve written so far:
static float lastTime = (float)GetTickCount();
	
while(true)
{
	if(::PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
	{
		if (msg.message == WM_QUIT) {
			break;
		}

		::TranslateMessage(&msg);
		::DispatchMessage(&msg);
	}
	else
    {
		float currTime = (float)GetTickCount();
		float timeDelta = (currTime - lastTime) * 0.001;

		if(timeDelta >= 0.05) // 20 fps = 1/20 = 0.05

		{
			Game->cycle();
		}
		else
		{
			// This is where my problem is. I think.

			currTime = (float)GetTickCount();
			timeDelta += (currTime - lastTime) * 0.001;
		}

		lastTime = currTime;
    }
}
--------------------------------------- People fear what they don''t understand.

Share this post


Link to post
Share on other sites
A few things:

First of all, I know this doesn''t help answer your question, but what is the reason you want to limit the frames to 20? Some reasons may be valid, but others certainly won''t be. So depending on your reasons, we may be able to lead you down a more appropriate path.

Secondly, without looking closely at your code, I do highly recommend using timeGetTime(), instead of GetTickCount(). GetTickCount() can be horrible sometimes. To use timeGetTime(), you''ll need to add winmm.lib to your linker settings, and there are two calls you should make, one before you ever call timeGetTime(), and one after you''re done calling timeGetTime() (probably right before you quit the program): timeBeginPeriod(1), and timeEndPeriod(1). That will inform Windows to increase the accuracy of the call to timeGetTime(). Remember this, whenever you need to get the time with a few milliseconds precision. GetTickCount() would probably be fine for measuring seconds, but it is horrible at measuring things like frame time.

Thirdly, with actual code. What you should be doing is only remembering lastTime whenever you actually complete a game cycle. Try something like this in the second chunk of your loop:


DWORD currTime = timeGetTime();
if (currTime - lastTime > 50) //If the delta is greater than 50 milliseconds

{
lastTime = lastTime + 50;
//You may be tempted to do this:

// lastTime = currTime;

//But this won''t guarantee 20 FPS, it will only

//guarantee at most 20 FPS, but it could be slower.

Game->cycle();
}

Share this post


Link to post
Share on other sites
Thanks a lot Andrew.

I think, you solved my problem. But I would be grateful if someone gives other solution to my problem, just for comparison. And thanks for that timeGetTime() advice. I've used it sometimes before but I tought it's the same thing as GetTickCount().

---------------------------------------
People fear what they don't understand.

[edited by - constans on February 20, 2004 3:06:58 PM]

Share this post


Link to post
Share on other sites
This is the way how I finally realised the FPS limit. Just in case if somebody have the same problem.
static int tickTrigger = 0;	
int tickCount;
timeBeginPeriod(1);

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

::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
else
{
tickCount = timeGetTime();

if(tickCount > tickTrigger)
{
tickTrigger = tickCount + 100; // 1000 ms / 10 FPS = 100 _Game->cycle();

}
}
}

timeEndPeriod(1);


[edited by - constans on February 21, 2004 5:47:32 AM]

Share this post


Link to post
Share on other sites
You could also use the display refresh rate as a clock.

So you would find out (or set) the monitor refresh rate, perhaps to 60Hz, then every third monitor refresh would draw a new frame, or do a back-buffer flip, or whatever to get 20hz.

Share this post


Link to post
Share on other sites
Although the technique you are currently using will work, you'll probably find that it hogs processor time horribly. Why do you want to limit the number of frames in the first place? There are much better methods that aren't much harder (e.g. using an average elapsed time to move objects). You can even try a fixed time step, but that is a little harder.

Look here and here for a little more info on timing.

And if you decide to stick with your current method, you should play nice with the Windows OS and add a call to Sleep(0). That will tell Windows to use any remaining time allocated to your thread on another task while you wait for the time to pass.


pan narrans | My Website | Study + Hard Work + Loud Profanity = Good Code

[edited by - pan narrans on February 20, 2004 4:10:21 PM]

Share this post


Link to post
Share on other sites
Well, actually this is not a game. I''m working on a simple example which draws rectagles at random position on the screen and at the same time I''m testing some new techniques. I just wanted to limit drawing without Sleep(). So I''m actually limiting rectangles per frame not frames per second. That''s all.

Thank you very much for your helpful comments and advices. I should start study those papers on FPS theory you mentioned.

If you have something to add, feel free to write it. I''ll be very grateful.

Share this post


Link to post
Share on other sites
I wouldn't advocate using the Sleep function to regulate the timing, e.g. Sleep(33), that could be pretty awful . But, I would add a call to Sleep with a parameter of 0. From the Windows SDK: "A value of zero causes the thread to relinquish the remainder of its time slice to any other thread of equal priority that is ready to run. If there are no other threads of equal priority ready to run, the function returns immediately, and the thread continues execution".

So your code would simply look like this:

static int tickTrigger = 0;
int tickCount;
timeBeginPeriod(1);

while(true)
{
if(::PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
break;
}
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
else
{
tickCount = timeGetTime();
if(tickCount > tickTrigger)
{
tickTrigger = tickCount + 100;
Game->cycle();
}
}
Sleep(0);
}
timeEndPeriod(1);


Without that call your code will consume 100% of the CPU time. Basically you would just be giving up a tiny fraction of a second on each loop iteration, which the OS could use for other processes, if any exist. It wouldn't slow your code down at all.


pan narrans | My Website | Study + Hard Work + Loud Profanity = Good Code

[edited by - pan narrans on February 21, 2004 5:50:55 AM]

Share this post


Link to post
Share on other sites
float currTime = (float)GetTickCount();
float timeDelta = (currTime - lastTime) * 0.001;
if(timeDelta >= 0.05) // 20 fps = 1/20 = 0.05
{
Game->cycle();
lastTime = currTime; // Deebo - add this line
}
else // Deebo - remove this else statement
{
// This is where my problem is. I think.
currTime = (float)GetTickCount();
timeDelta += (currTime - lastTime) * 0.001;
}
lastTime = currTime; // Deebo - remove this line too

The else statement is just setting currTime and timeDelta.
when the loop starts over, they are being written to again.
That makes the else statement literally a waste of time.
Just simply set lastTime to the last time a frame was rendered and before rendering simply check if currTime has advanced enough

Intro Engine

Share this post


Link to post
Share on other sites
I see that a lot of people here advocate the use of timeGetTime or GetTickCount. Personally, I've been using QueryPerformanceFrequency and QueryPerformanceCounter since I began Windows programming 4 years ago, and it's never failed or given me any problems at all. I use the following time code:

LARGE_INTEGER liStartTime, liTimerFreq;

BOOL InitializeTime(void)
{

if(!QueryPerformanceFrequency(&liTimerFreq)) return(FALSE);

QueryPerformanceCounter(&liStartTime);

return(TRUE);

}

float CalculateTime(void)
{

LARGE_INTEGER liCurrTime;
float fTime = 0;

QueryPerformanceCounter(&liCurrTime);

fTime = ((float)liCurrTime.QuadPart - (float)liStartTime.QuadPart) / (float)liTimerFreq.QuadPart;

m_StartTime = m_CurrTime;

return(fTime);

}



Windows 95 - 32 bit extensions and a graphical shell for a 16 bit patch
to an 8 bit operating system originally coded for a 4 bit microprocessor,
written by a 2 bit company that can't stand 1 bit of competition.


[edited by - iNsAn1tY on February 22, 2004 3:29:28 PM]

Share this post


Link to post
Share on other sites
quote:
Original post by iNsAn1tY
I see that a lot of people here advocate the use of timeGetTime or GetTickCount. Personally, I''ve been using QueryPerformanceFrequency and QueryPerformanceCounter since I began Windows programming 4 years ago, and it''s never failed or given me any problems at all. I use the following time code:

LARGE_INTEGER liStartTime, liTimerFreq;

BOOL InitializeTime(void)
{

if(!QueryPerformanceFrequency(&liTimerFreq)) return(FALSE);

QueryPerformanceCounter(&liStartTime);

return(TRUE);

}

float CalculateTime(void)
{

LARGE_INTEGER liCurrTime;
float fTime = 0;

QueryPerformanceCounter(&liCurrTime);

fTime = ((float)liCurrTime.QuadPart - (float)liStartTime.QuadPart) / (float)liTimerFreq.QuadPart;

m_StartTime = m_CurrTime;

return(fTime);

}



Windows 95 - 32 bit extensions and a graphical shell for a 16 bit patch
to an 8 bit operating system originally coded for a 4 bit microprocessor,
written by a 2 bit company that can''t stand 1 bit of competition.


[edited by - iNsAn1tY on February 22, 2004 3:29:28 PM]


Me too, but I didn''t want to complicate things further


pan narrans | My Website | Study + Hard Work + Loud Profanity = Good Code

Share this post


Link to post
Share on other sites