I looked at this thread, and I found a lot of timer routines using QPC but there is no explanation about how to use these routines in the main loop.
My game must run at 60FPS, with options for reducing CPU ussage.
Can someone post a little example program using high resolution timer routines, and how to use them in the main loop?
Thanks in advance.
You should not use high resolution timers in main loops. They should be used for high precision timing - in games, mainly profiling. For game logic, use lower precision time functions, like timeGetTime() (returns milliseconds) on win32.
The reason is because most high resolution timing functions have... issues...
edit: updated RDTSC problems description
RDTSC: reports incorrect results on many computers (mostly dual-core systems), crashes on 486s and earlier, and the units change at run time due to power-management issues
(note: dual-core issue can be fixed for dual-core AMDs with a utility downloadable from their website)
QPC: slow; yields incorrect results on many computers; win32 only
gettimeofday: changes when system time changed; not sure what underlieing method it uses; many yield errors on some computers
And anyway, sub-millisecond precision for regular user interactions is pointless.
1 | int next_tick_ms = get_time_ms(); |
2 | int ticks_since_last_frame = 0; |
3 | int last_frame_ms = 0; |
4 | while (!quitting) { |
5 | bool do_frame = false; |
6 | int current_ms = get_time_ms(); |
7 | if (current_ms >= next_tick_ms) { |
8 | calculate_physics(); |
9 | next_tick_ms += ms_per_tick; |
10 | //we're too far behind |
11 | if (next_tick_ms < current_ms - 20) next_tick_ms = current_ms - 20; |
12 | ticks_since_last_frame++; |
13 | if (ticks_since_last_frame > 5) do_frame = true; |
14 | } |
15 | else { |
16 | if (ticks_since_last_frame || current_ms > last_frame_ms + 100) |
17 | do_frame = true; |
18 | } |
19 | if (do_frame) { |
20 | render_frame(); |
21 | ticks_sicne_last_frame = 0; |
22 | last_frame_ms = current_ms; |
23 | } |
24 | } |
QPC: slow; yields incorrect results on many computers…
gettimeofday: …many yield errors on some computers
I'm going to need you to prove this.
And anyway, sub-millisecond precision for regular user interactions is pointless.
But it leads to more accurate simulation of physics and other game functions.
RDTSC: crashes on 486s and earlier…
gettimeofday: changes when system time changed
These are completely non-issues
Orz: Wrong I'm afraid.
I'm going to need you to prove this.
Gimme a few minutes to come up with references. Only some of the problems are documented on the web though. I have personally seen both QPC and RDTSC fail. I've never seen gettimeofday() fail, but that's because I primarily use windows.
But it leads to more accurate simulation of physics and other game functions.
Timers don't make physics calculations more precise; they just make physics more closely synchronized with real time. However, there's not point in synchronizing them more accurately with real time than the output devices (or the human perceiving them, assuming you're outputting to a human) can track. For instance, CRTs tend to only do a retrace every 8-20 milliseconds, rendering sub-millisecond synchronization for animations displayed on CRTs pointless. LCDs have different issues, which I'm less familiar with the precise numbers for, but they have multiple sources of timing issues; data rates for the digital cables and update rates for the LCs themselves. Either issue alone is enough to render sub-millisecond timing irrelevant for synchronizing LCD output. The human eye also introduces timing limitations, but these are harder to quantify due to variations depending upon which portion of the eye you are using (peripheral vision etc), individual variation between eyes and brains, and the nature of the visual stimuli.
Sound is a different issue of course, but one I know less about. However, it's usually buffered for several milliseconds in advance even in games, and much of the timing there is handled by the sound hardware and drivers.
Orz: Wrong I'm afraid.
About what? Be more specific.
Basically, I have tested high resolution timers using QPC on windows on many different PC's for my games, as has Mike Welch who I have worked with on Neon Wars. The results are consistently better than lower precision timing results in terms of smoothness and accuracy.
So, I am speaking from a practical and pretty experienced point of view here.
Timers don't make physics calculations more precise; they just make physics more closely synchronized with real time.
If you're using a delta-time method, you are basically using a Riemann sum to compute the position based off the velocity and acceleration. By using smaller deltas (smaller rectangles), your result is more accurate. Imagine: how accurate (*not* how smooth would it appear on screen) would the movements be if you used a 1-second timer?
You are totally high jacking the thread.
You are totally high jacking the thread.
Oh, you're right. I sorta forgot about the question when orz posted his example of how to do it. Did you just add the example of how to use KittyCat's code, or was that there? Either way: Paul: read this, especially where he says "How to use KC's routines:"
The "proof" I promised:
(also, of course, the other reason I forgot to list originally: RDTSC is based upon processor frequency, which changes at run time due to power management features)
personal experience: my dual-core AMD had neither QPC nor RDTSC work correctly originally. Using KB896256 and another microsoft patch I can't find at the moment fixed QPC but did not fix RDTSC (contrary to the claims of the docs for the hotfixes IIRC). Using AMDs utility may have fixed RDTSC, but I haven't tested it enough to tell yet (only installed yesterday).
2nd-hand experience: Shizmoo Games (http://www.shizmoo.com/) told me in aproximately 2001 (my memory is a little fuzzy on the exact date) that they had switched from using QPC to using lower resolution timing functions because a significant numbers of their users had some flaw in their motherboards chipset that caused QPC to return bizarre results but left other timing methods functional.
documentation for dual-core &| multi-CPU issues on the web from major companies:
http://support.microsoft.com/kb/896256
Supposedly fixes issues with QPC and/or RDTSC; however, it did not fix RDTSC on my dual-core AMD.
http://support.microsoft.com/kb/909944
http://www.amd.com/us-en/Processors/TechnicalResources/0,,30_182_871_9706,00.html
Supposedly fixes issues with RDTSC; working so far for me, but only had it installed for a few hours now. Reports of crashes if both this and the above microsoft hotfix are installed.
documentation on the web from less well-known sources:
http://www.virtualdub.org/blog/pivot/entry.php?id=106
note the headline: Beware of QueryPerformanceCounter()
http://www.gamedev.net/reference/programming/features/timing/
in particular, note:
QPC has several issues:
1. Hardware bug: race condition when updating parts of the register? I have seen speculation to this effect more than once. ([qpc_race])
2. It jumps forward several hundred ms sometimes under heavy PCI bus load. ([qpc_jump])
3. The PIT is slow to read - it requires sending a latch command, and 2 8-bit reads, for a total of ~3 µs.
4. When using the TSC, it is documented to return different values depending on which processor is running the code, on some (buggy) systems.
http://forums.seriouszone.com/showthread.php?t=44791&page=5
edit: note: that's a Croteam (developers of Serious Sam) developer posting there, talking about RDTSC (he likes QPC though)
Will update this to handle other posts while I was googling for that stuff
edit:
If you're using a delta-time method, you are basically using a Riemann sum to compute the position based off the velocity and acceleration. By using smaller deltas (smaller rectangles), your result is more accurate. Imagine: how accurate (*not* how smooth would it appear on screen) would the movements be if you used a 1-second timer?
You're equating physics synchronization with physics accuracy. If you want more accurate physics on the same time measurement method while using a delta-based time, it's as simple as using two deltas of half the size. Time gets clumped, but physics accuracy is not lost.
I'll be honest. I don't care about all those references.. In my experience when testing on many different setups, it's the best choice.
I don't care about all those references..
I do. I don't know I quite trust the stuff about QPF, but the RDTSC references make sense. Actually, I'm going to do some experimentation when I get home: the original UT used RDTSC in its code, and it runs too fast on many systems I've played it on. I will disable CPU clock scaling and see what I get.
[append]
That's the first comment in the virtual dub blog post
Okay, so the best high-frequency counter is timeGetTime and gettimeofday, it seems. Yes, orz?
CGamesPlay: if you QueryPerformanceFrequency each time you get the timer it is good with frequency scaled processors
Gull: I'm sure that solves the CPU scaling problem, but not the others...
Personally, I like the timeGetTime/gettimeofday method. It's easier, after all
What is the precision of gettimeofday? If it is too low it won't look very good even if it is more reliable.
It's microseconds.
Ok....
I use timeGetTime() for game logic, and RDTSC for performance monitoring.
timeGetTime():
On all systems I've tested, has a resolution of at least 10 milliseconds; on many (all 2k&XP? not sure) it has a resolution of 1 millisecond. It's accuracy seems generally quite good for its resolution. 10 milliseconds is less than I'd like when use delta-timing, but good enough; 1 millisecond is plenty for delta-timing games. This function was reasonably fast on the systems where I tested it.
RDTSC:
Fast. Typically 10-100 cycles, depending upon CPU type. The problems this has render it useless on some platforms, but performance monitoring is non-critical on users systems, so you only have to worry about your own testbeds.
other timing methods:
allegro timer interrupts - I don't like the interface, but they seem to yield enough accuracy for timing game logic. I never set them to run over 200 hertz.
gettimeofday() - unixers seem to like it but docs claim that it's not monotically increasing, so I would avoid it for game logic. I'd like to do more testing on this - I don't know what the real-world speed, resolution, or accuracy is like.
SDL_GetTicks() - haven't checked the implementation, but I would guess it to be a wrapper for timeGetTime() on windows. If you use SDL this looks like a decent choice.
libc time(NULL) - very low resolution, but this is what I use when I can't find anything non-libc
libc clock() - documentation on this varies wildly and is contradictory.
QPC() - too buggy for game logic, too slow for profiling. However, unlike RDTSC its units are easily convertable to seconds, so I still occaisonally use it.
If it is too low it won't look very good even if it is more reliable.
Oh, well, if the user is playing at the moment DST kicks in and he has his RTC set to localtime (for no reason at all), then the game would lag an hour
I tried to use these routines, RIchard helped me a lot, and he answered all my question but I could not make it work properly.
I am using devcpp since a few days ago and perhaps there is something wrong with my configuration, so can someone else try to compile this routines?
timer.hpp
1 | #ifndef __Timer_hpp__ |
2 | #define __Timer_hpp__ |
3 | |
4 | #include <allegro.h> |
5 | #include <winalleg.h> |
6 | |
7 | struct timer |
8 | { |
9 | LARGE_INTEGER tstart, tticks, tnow, tlast; |
10 | int high_freq, logic_frames, logic_fps, gfx_frames, gfx_fps; |
11 | }; |
12 | |
13 | extern struct timer timer; |
14 | |
15 | void start_timer(void); |
16 | void reset_timer(void); |
17 | int check_timer(int frac_sec); |
18 | void reset_timer_alt(void); |
19 | |
20 | #endif |
timer.cpp
1 | #include <Windows.h> a veces hay que sacvar esto |
2 | #include "timer.hpp" |
3 | |
4 | // High resolution timer code for Windows. Call start_timer first and then call |
5 | // check_timer with the required accuracy range. |
6 | // The timer is more accurate than the default Allegro timers.. |
7 | |
8 | struct timer timer; |
9 | |
10 | void start_timer(void) |
11 | { |
12 | timer.high_freq = QueryPerformanceFrequency(&timer.tticks); |
13 | timer.logic_frames = 0; |
14 | |
15 | if (timer.high_freq) |
16 | { |
17 | QueryPerformanceCounter(&timer.tstart); |
18 | timer.tlast = timer.tstart; |
19 | } |
20 | } |
21 | |
22 | void reset_timer(void) |
23 | { |
24 | if (timer.high_freq) |
25 | { |
26 | |
27 | QueryPerformanceCounter(&timer.tnow); |
28 | timer.tstart = timer.tnow; |
29 | } |
30 | } |
31 | |
32 | int check_timer(int frac_sec) |
33 | { |
34 | int t; |
35 | |
36 | if (timer.high_freq) |
37 | { |
38 | QueryPerformanceCounter(&timer.tnow); |
39 | t = (int) (((timer.tnow.QuadPart - timer.tstart.QuadPart) * frac_sec) / timer.tticks.QuadPart); |
40 | |
41 | // Have we done 1 second since the timer was last reset? |
42 | if (t > 240) |
43 | { |
44 | // Yes, so reset again and update details. |
45 | // If we reset the timer after each check we get errors building up causing a lack |
46 | // of precision. We also reset it here to make it as fast as possible. |
47 | |
48 | timer.tstart = timer.tlast; |
49 | t -= timer.logic_frames; |
50 | timer.logic_fps = timer.logic_frames; |
51 | timer.logic_frames = 0; |
52 | |
53 | timer.gfx_fps = timer.gfx_frames; |
54 | timer.gfx_frames = 0; |
55 | } |
56 | timer.tlast = timer.tnow; |
57 | return t; |
58 | } |
59 | |
60 | return 0; |
61 | } |
my loop
1 | start_timer(); |
2 | do |
3 | { |
4 | //Richard's timer runs at 240fps, so I did this to get a 60fps |
5 | // 240/4 = 60, then check_timer(240)%4 = {0,4,8,12,16,20,24,28,32,36...} = 60FPS |
6 | |
7 | if((check_timer(240)%4) == 0){ |
8 | //draw background and show FPS |
9 | draw_sprite(background, bg.get_frame(),0,0); |
10 | textprintf(background, font, 0,0, makecol(205, 215, 255), "FPS %d", timer.gfx_fps); |
11 | //draw sprites |
12 | draw_trans_sprite(background, my_frame.get_frame(), mouse_x+111, mouse_y+101); |
13 | |
14 | //blit to screen |
15 | blit(background, screen, 0, 0, 0, 0, 640, 480); |
16 | timer.gfx_frames++; |
17 | } |
18 | // Wait until we press Escape. |
19 | } while (!key[KEY_ESC]); |
.
.
.
Well, this loops works OK at 60fps, but when some sprites disappear the FPS grows(the game runs faster), with few sprites my game runs faster and with more sprites it runs slower.
What can be wrong?
.
.
.
.
.
.
Paul: read this, especially where he says "How to use KC's routines:"
I need a high resolution timer with QPC
You should not use high resolution timers in main loops
I disagree.
You are totally high jacking the thread.
I agree.
.
.
.
[EDIT]
GullRaDriel I'll try that now, thanks!
Paul, did you try that change the 240 to 60 thing I mentioned?
From Pmask libray you have test.c and test2.c which use Orz's timing routines so all your need are filled .
Paul, did you try that change the 240 to 60 thing I mentioned?
Yes, I tried several times, but it allways run at 240 or more without reset_timer()
With reset_timer() it seems that works, but then timer.gfx_fps shows allways 0
I can't see how that would happen if you had no 240 references anymore.. but anyway, try Orz's code.
How should I use Orz's routines?
I need to see a main loop implemented with those routines to understand how they work.
Here are Orz's routines for timing:
...
All these examples comes from pmask library [sourceforge.net], by Orz.
I think looking at it will definitely solve your problem Paul.
Yeah, that's my timing measurement code. A few caveats:
1. the gettimeofday() implemenation there is bugged... just some easily fixed typos, fixed in my lastest version of that file (so far existing on my computer only...)
2. while that's in the PMASK download for use by the examples, I consider it to not be a part of the examples, not the library itself.
thread's title said:
I need a high resolution timer with QPC
That was the title, but your original post said
I found a lot of timer routines using QPC but there is no explanation about how to use these routines in the main loop
So I posted code that demonstrated how to use a timer function in a games main loop, independant of whether that timer function used QPC. Though, for usage with QPC, its best with the ints changed to doubles. i.e.
1 | double next_tick_ms = get_time_ms(); |
2 | double ticks_since_last_frame = 0; |
3 | double ms_per_tick = 1000.0 / 60;//60 hertz |
4 | double last_frame_ms = 0; |
5 | while (!quitting) { |
6 | bool do_frame = false; |
7 | double current_ms = get_time_ms(); |
8 | if (current_ms >= next_tick_ms) { |
9 | calculate_physics(); |
10 | next_tick_ms += ms_per_tick; |
11 | //we're too far behind |
12 | if (next_tick_ms < current_ms - 20) next_tick_ms = current_ms - 20; |
13 | ticks_since_last_frame++; |
14 | if (ticks_since_last_frame > 5) do_frame = true; |
15 | } |
16 | else { |
17 | if (ticks_since_last_frame || current_ms > last_frame_ms + 100) |
18 | do_frame = true; |
19 | } |
20 | if (do_frame) { |
21 | render_frame(); |
22 | ticks_since_last_frame = 0; |
23 | last_frame_ms = current_ms; |
24 | } |
25 | } |
edit: fixed a typo in the code (mispelled since as sicne in one case), added a value of ms_per_tick
The only thing necessary (with or without changing the ints to doubles) to make that into a functional QPC-timed game loop is a QPC based timer such as:
1 | double ms_per_QPC_tick=0; |
2 | void init_get_time_ms() { |
3 | LARGE_INTEGER fred; |
4 | if (QueryPerformanceFrequency(&fred)) { |
5 | ms_per_QPC_tick = 1000.0 / fred.QuadPart; |
6 | } |
7 | return -1;//do whatever you do on an error |
8 | } |
9 | volatile double get_time_ms() { |
10 | LARGE_INTEGER bob; |
11 | if (QueryPerformanceCounter(&bob)) { |
12 | return bob.QuadPart * ms_per_QPC_tick; |
13 | } |
14 | return -1;//do whatever you do on an error |
15 | } |
edit:
How should I use Orz's routines?
I need to see a main loop implemented with those routines to understand how they work.
That is the main loop there in my first post, and again in this post. It's untested, but is a demonstration of the concepts. If you want one with delta-timing instead, just ask.
From Pmask libray you have test.c and test2.c which use Orz's timing routines so all your need are filled .
Hm... not necessarily a good idea... (edit: I feel kinda stupid saying that something of mine isn't a good example for some purpose, but really the PMASK tests weren't written with this is mind... perhaps I should clean up test2.c sometime...)
test.c does not use any timing routines from my get_time.c or anything else except Allegro. It does not use high resolution time. So, it is not really a good example. edit: its timing method is also totally inappropriate for a game
test.2 does use my timing routines. However, it's not the best example as the main loop is kinda quick-and-dirty, cluttered up with text output and keyboard input stuff, and whatnot. Still, it is an example of using my time functions to time something in a relatively smart manner compared to many allegro example programs. It's also undocumented, and uses different priorities than many games might prefer. (for instance it will never use more than 50% of CPU time on graphics)
Thank you very much! I'll try to implement your routines now!