How do I make and use a timer correctly?

Started by
3 comments, last by Zapeth 12 years, 8 months ago
Hello, I am currently working on a Pacman remake (purely 2D, my first game btw so its more a try to learn some program skills than to create something fun).
So now I am having the problem that the game runs with different speed on different computers and since I have read that there are different timers out there I would like to know what are the best ones (I am programming under Windows but I would like to have the opportunity to easily port the game to Linux or even portable devices).

But I also need help with the principle of a timer. I know it measures (or returns) the time that has passed since the last call (or do only certain timers do that?) but what do I have to do with that information now?


For my game I would like to have a constant game speed of 60fps so I think I know what I have to do with a PC that is too fast - after he is done with the update, drawing etc functions of the game, he has to wait a certain time (what would that be if I want 60fps?) until he repeats them so I simply put him on wait until that time has passed.

But what do I have to do with a PC that is not so fast? For example a really slow PC that makes only 10 fps (or more drastically 0.1fps), what is that PC supposed to do to keep up with the game speed?

Thanks in advance :)

edit: oh sry somehow he did post this topic twice, please delete one of them
Advertisement
Under Windows, you probably want [font="Courier New"]timeGetTime [/font](must link with winmm), or if you don't mind bad resolution, [font="Courier New"]GetTickCount[/font].

[font="Courier New"]timeGetTime [/font]is precise and has a 1ms resolution if you use [font="Courier New"]timeBeginPeriod[/font]/[font="Courier New"]timeEndPeriod[/font], around 15ms otherwise. [font="Courier New"]GetTickCount [/font]is precise and ultra-lightweight, but not terribly high-res (often sufficient, though).

There is [font="Courier New"]QueryPerformanceCounter [/font]too, but this is dangerous, since it relies on the TSC (HPET on new processors and new versions of Windows), which counts clock ticks, not time. With frequency scaling and some broken BIOSes that don't start all cores with the same counter, this can be a disaster. So while this has nanosecond resolution, it is not necessarily accurate.

About what to do with time, there's Glenn Fiedler's fix your timestep series which, although talking about "physics" is generally a good read about "anything, like game, animated".
Your point of view is very interesting about [font="Courier New"][font="Arial"]QueryPerformanceCounter
I'm actually using it massively, so I'm a bit (really) confused[/font]

[/font]Please let me know why my following statements are not correct :


[font="Courier New"]QueryPerformanceCounter [font="Arial"][...][/font] [/font]counts clock ticks, not time.
With frequency scaling.....
[/quote]
That's why we divide this count with the current frequency (QueryPerformanceFrequency() ), isn't it?



... and some broken BIOSes that don't start all cores with the same counter .....
[/quote]
What could we do if the target system/machine is corrupted ? Unless this problem is very recurrent ?

In PhysX 2.8 SDK (at least), they suggest (not to say recommend) to use it in the very first lesson (rigid bodies, lesson 101)

Thank you in advance !
:)
Dividing the count by the frequency is the correct thing to do.... if the frequency is constant. On the most recent lines of CPUs that is the case even if the CPU runs at a lower clock speed, on earlier ones, it is not (CPUID can query that, too). Which means that time "accelerates" and "decelerates" as the CPU scales up and down if you take TSC ticks for time. On Windows 7 (and I believe SP1 of Vista), HPET is used if supported, so there is no problem at all. Sadly, however, unless you make minimum requirements like "at least Core i3/5/7 and Windows 7", you cannot really know for sure where your program will run. It might be Windows XP and a mobile CPU on a notebook.

Unluckily there is no other high resolution timer that you could use, if you need better than 1ms.

The workaround for broken BIOSes is to do all TSC sampling from a thread that is affinity-bound to a single core. That assures that there are no "time travels". Obviously neither the need to shift the data from one thread to another nor the tampering with affinity is precisely pretty :(
QueryPerformanceCounter usually works perfectly fine.

I haven't researched this very extensively at all, so please correct me if I'm mistaken, but the following is my understanding of the problem.
In some systems where there is no built-in multimedia timer (before 2005), the TSC must be used for high precision timing, and if there are also multiple CPUs or cores within a CPU that can get out of sync, from power saving functions that change the clock rate, different values can be returned depending on which core the rdtsc instruction is executed on. If you run on such a system and your process runs on different cores on subsequent QueryPerformanceCounter calls, then you can get a faulty delta time.
As far as I know the timespan during which such systems were built was not very long and it was several years ago now. You can eliminate the potential problem by doing all your timing on a thread that uses SetThreadAffinity to always use the same core.

EDIT: Missed that XP doesn't use the HPET like samoth stated, so I guess anything supporting XP needs it, if the TSC can get out of sync. The Wiki article talks about that too.
However, I don't think QueryPerformanceCounter reads the TSC directly, it corrects the frequency to something like 1Mhz on older systems if I remember correctly, and should fix any issues with slowdown from power-saving as long as it's run on a single core.

wiki/Time_Stamp_Counter
wiki/High_Precision_Event_Timer

If you use QueryPerformanceFrequency and it says 14 Mhz you (kinda) know you have the multimedia timer.
Since the actual requirements are Windows Vista/DX10.0, I think I shouldn't have troubles.
Anyway, if I get problems with timers, I know now a way more safe to do this, thank you
:wink:

EDIT : yes this is a great idea to use a special thread to get these data, the problem is avoided
@samoth: thx for the link but unfortunately my mobile internet blocks the website so I cant read the text now. maybe you could explain me anyway how it works? Is it correct if I force the game to wait until the required time has passed? And what should I do with a PC that cant keep up with the speed I want to have?

@Adaline: thx for your timer code Adaline, I already had a QueryPerformance Timer working but it wasnt as compact as yours :)

As for the question if it is good to use this timer I dont really care as long as it works with the game. And how much Milliseconds would 60fps be?

edit:
ok I did some more research and found this nice site where different methods of using a timer are shown and explained with their pros and cons for both fast and slow computers - http://www.koonsolo.com/news/dewitters-gameloop/

so now I have implemented the first method there with the timeGetTime() function and I watched the fps (that I display with the QueryPerformance Timer) and recognized that they are not exactly around 60.0fps. Its more around 57 - 64 fps, I assume this is because the timeGetTime() function is not as precise as the QueryPerformanceCounter?
another strange thing is that he is not always at ~60fps, sometimes there are suddenly huge fps drop for a short time and sometimes he goes over the 60fps limit I set for some seconds.
also the whole game doesnt run very fluid but that might be because of the just mentioned fps irregularities

This topic is closed to new replies.

Advertisement