Best practise for FPS limiting?

SysRq2000

Member
Joined
Aug 10, 2003
Messages
19
Location
Denmark
Hi. Im currently developing a 2d game using Direct3D. But I would like to limit my "game loop" to executing just 30 times per second, or said in an other way, I would like my game to run at 30 FPS... What is the best and/or normal way to do this? Currently my game loop is just a plain "Do" loop. Ive tried to just use the tick event of a Timer but it is VERY inaccurate. Any suggestions?

- SysRq
 
You can limit it by doing something like this;

C#:
const int MaxFPS = 40;

int ticks = 0;
int speed = 0;

while (true) {
   ticks = Environment.TickCount;
   if (ticks > speed) {
      speed = 1000 / MaxFPS + ticks;
      // Perform regular game functionality.
   }
}

Youll want to do about 10 FPS over what you want for max. The loops always tends to go a bit slower then whats calculated.
 
Close but no cigar...

Thx for the suggestion. However, I dont think that the use of Environment.TickCount is very reliable...

Environment.TickCount have a resolution of approx. 16 milliseconds on my machine (may vary on your machine), which is a very unfortunate number because 1000 / 30 is 33,333... and 16*2 is 32. So in your code the time between execution will be 16*3 (48) milliseconds... and that will give you 21 frames per second. Notice that it is 9 frames below the wanted framerate of 30 FPS! (You make it 10 :p)

Originally posted by wyrd
Youll want to do about 10 FPS over what you want for max. The loops always tends to go a bit slower then whats calculated.
Maybe a windows api timer could solve the resolution problem, but Im worried that WinAPI calls from VB.Net are slow...?

- SysRq
 
I dont know of any better timer then GetTickCount() and Environment.TickCount (which I believe are the same thing).

http://msdn.microsoft.com/library/d...frlrfsystemenvironmentclasstickcounttopic.asp
.NET Framework Class Library
Environment.TickCount Property

Property Value
A 32-bit signed integer containing the amount of time in milliseconds that has passed since the last time the computer was started.

Remarks
The value of this property is derived from the system timer and is stored as a 32-bit signed integer. Therefore, the elapsed time will wrap around to zero if the system is run continuously for 24.9 days.

The resolution of the TickCount property cannot be less than 500 milliseconds.

The TickCount property handles an overflow condition by resetting its value to zero. The minimum value returned by TickCount is 0.

TickCount is different from the Ticks property, which is the number of 100-nanosecond intervals that have elapsed since 1/1/0001, 12:00am.

Use the DateTime.Now property to obtain the current local date and time on this computer.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/gettickcount.asp
Return Values
The return value is the number of milliseconds that have elapsed since the system was started.

Remarks
The elapsed time is stored as a DWORD value. Therefore, the time will wrap around to zero if the system is run continuously for 49.7 days.

If you need a higher resolution timer, use a multimedia timer or a high-resolution timer.

To obtain the time elapsed since the computer was started, retrieve the System Up Time counter in the performance data in the registry key HKEY_PERFORMANCE_DATA. The value returned is an 8-byte value. For more information, see Performance Monitoring.

Of note;
If you need a higher resolution timer, use a multimedia timer or a high-resolution timer.

I have no idea what sort of performance (speed wise) those timers give, but you can give them a try.

which is a very unfortunate number because 1000 / 30 is 33,333

Dont you mean 1000 / 40.
 
[api]timeGetTime[/api] is apparantly accurate to the millisecond. [api]QueryPerformanceFrequency[/api] and [api]QueryPerformanceCounter[/api] are very very accurate indeed.

All three are APIs.
 
High-resolution performance timer

Well you can construct a much higher resolution performance timer by calling the windows api functions:
QueryPerformanceCounter(out long) and
QueryPerformanceFrequency(out long)

Read this article from The Code Project, to learn how to construct your own high-resolution performance timer using windows api calls. :D I just dont know if there is an unknown cost by calling windows api functions from a loop in managed code? :confused: Maybe there is a better solution to the problem... Im still open to suggestions! ;)

Dont you mean 1000 / 40.
No, I just used the original intended framerate of 30, and not the error-corrected value of 40, to explain why you have had to add 10 to get the correct framerate. Btw, if your TickCount resolution is 16 milliseconds, the true framerate will be 31.25fps when setting the intended framerate to 40fps. That is because the actual time between execution will be 32ms, and not 25ms as 40fps would suggest... ;)

- SysRq
 
QueryPerformanceFrequency & QueryPerformanceCounter, the most accurate?

Originally posted by VolteFace
[api]QueryPerformanceFrequency[/api] and [api]QueryPerformanceCounter[/api] are very very accurate indeed.
I dont think it is possible to get any more accurate... am I wrong?

- SysRq
 
No, it is as accurate as it can possibly be (potentially at least; system load might have some slight effect on it).
 
I just dont know if there is an unknown cost by calling windows api functions from a loop in managed code?

The only cost is that of calling the function. Calling a Windows API function should never be slower then calling an equivelent managed code function (ie; GetTickCount() or Environment.TickCount). You can always run minor benchmarking tests.

No, I just used the original intended framerate of 30, and not the error-corrected value of 40, to explain why you have had to add 10 to get the correct framerate. Btw, if your TickCount resolution is 16 milliseconds, the true framerate will be 31.25fps when setting the intended framerate to 40fps. That is because the actual time between execution will be 32ms, and not 25ms as 40fps would suggest...

Right, just as I said.. you need to add 10 to the desired FPS. :P The code I gave you would give the desired result, so Im not sure what the explanation was for.

BTW what type of game are you making?
 
According to AllApi.Net QueryPerformanceCounter supposedly has a .Net equivelant, Im not sure if this class actually is a QueryPerformanceCounter though.

With FPS restrictions youll want to spread them otherwise theyll bulk at the start (if the program runs at about 110 FPS) restricting to 40FPS means that for the last 300ms or so the program will not be interactive
 
The game...

BTW what type of game are you making?
Its an unicycle game... But its still very much in the design stage. Im developing the game around various physic concepts to make the unicycle handling very realistic. Early gameplay tests done in Flash MX, seems very promising, and I cant wait to show you guys an alpha release of the game. I got the idea from the old, but very addictive game, "Elasto Mania". Check it out!

- SysRq
 
Thats really an excellent article.

I wonder however how can this
Well, you must find the most accurate timer on you PC. You want a timer that updates no faster than every millisecond. x86 PCs have a nice hardware clock that runs at 1193180hz (1.13198 MHz), microsecond resolution. Find it and use it. Once you have a timer, you need to make it a floating-point precision timer (lets call it "ftime()"); use doubles for this, since most timers use 64-bit integers. For making a floating-point precision timer, just get the timers value and divide by its rate:


double ftime(void)
{
return super_fast_clock()/the_rate_of_the_clock;
}
be done in .NET...
 
To make a call to the hardware clock in vb.net use the following code.
[VB]
<DllImport("Kernel32.dll")> _
Public Shared Function QueryPerformanceCounter(ByRef lpPerformanceCount As Long) As Boolean
End Function

<DllImport("Kernel32.dll")> _
Public Shared Function QueryPerformanceFrequency(ByRef lpFrequency As Long) As Boolean
End Function
[/VB]
So a class could be formed like this...
[VB]
Public Class HighResolutionTimer
Private m_Frequency As Long
Private m_Count As Long

Public Sub New()
QueryPerformanceFrequency(m_Frequency)
End Sub

Public ReadOnly Property FTime() As Double
Get
QueryPerformanceCounter(m_Count)
Return m_Count / m_Frequency
End Get
End Property

<DllImport("Kernel32.dll")> _
Private Function QueryPerformanceCounter(ByRef lpPerformanceCount As Long) As Boolean
End Function

<DllImport("Kernel32.dll")> _
Private Function QueryPerformanceFrequency(ByRef lpFrequency As Long) As Boolean
End Function
End Class
[/VB]
- SysRq
 
Back
Top