I need a high resolution timer with QPC
Paul whoknows

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.

orz

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 }

CGamesPlay
Quote:

QPC: slow; yields incorrect results on many computers…
gettimeofday: …many yield errors on some computers

I'm going to need you to prove this.

Quote:

And anyway, sub-millisecond precision for regular user interactions is pointless.

But it leads to more accurate simulation of physics and other game functions.

Quote:

RDTSC: crashes on 486s and earlier…
gettimeofday: changes when system time changed

These are completely non-issues ::)

Richard Phipps

Orz: Wrong I'm afraid.

orz
Quote:

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.

Quote:

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.

Quote:

Orz: Wrong I'm afraid.

About what? Be more specific.

Richard Phipps

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.

CGamesPlay
Quote:

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?

GullRaDriel

You are totally high jacking the thread.

CGamesPlay
Quote:

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:"

orz

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:

Quote:

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:

Quote:

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.

Richard Phipps

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. :)

CGamesPlay
Quote:

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?

GullRaDriel

CGamesPlay: if you QueryPerformanceFrequency each time you get the timer it is good with frequency scaled processors

CGamesPlay

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 :)

Richard Phipps

What is the precision of gettimeofday? If it is too low it won't look very good even if it is more reliable.

CGamesPlay

It's microseconds. :P

Richard Phipps

Ok.... ::)

orz

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.

GullRaDriel
Hijackers have something for you Paul, and all come from one of the latest a.cc news.

Here are Orz's routines for timing:

get_time.h
[code]
#ifndef _GET_TIME_H
#define _GET_TIME_H

#ifndef _TYPES_H
#include "types.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif

void init_time(); //to initialize time stuff
void deinit_time(); //to de-initialize time stuff

/*
three forms of get_time
NAME / ALTERNATE NAME RETURNS UNITS PRECISION SPEED
get_time() / get_time_ms() int milliseconds moderate fast
get_time_s() / get_time_seconds() double seconds high slow on WIN32
get_time_ticks() Sint64 unspecified high fast on intel

the current time is measured relative to the call
to init_time() for get_time_ms() and get_time_s(), but NOT for get_time_ticks()
*/

volatile int get_time(); //to get the current time in milliseconds
#define get_time_ms get_time

volatile double get_time_s(); //to get the current time in seconds at high precision
#define get_time_seconds get_time_s

volatile Sint64 get_time_ticks(); //to get the current time in unspecified units
volatile double get_time_tick_period();
/*note: The period of the ticks returned by get_time_ticks() may change over time
and the value reported is only an aproximation.
*/

//set this to determine how you want to react to errors
extern void (*get_time_error_function) (char *);

int idle ( int time ); //to yeild a number of milliseconds to the OS
extern int _no_idle; //to disable the above function

#ifdef __cplusplus
}
#endif


#endif
[/code]

get_time.c
[code]
//#define NO_WINMM_TGT
#define NO_WINMM_QPC
#define NO_RDTSC
#define NO_LIBC
//#define NO_ALLEGRO_TIME
//#define NO_SDL_TIME
//#define NO_GETTIMEOFDAY

//#define PLATFORM_IS_ALLEGRO
//#define PLATFORM_IS_SDL

/*
These #defines are used to control what underlying system-dependant
time functions are used to calculate the current time.

WINMM_TGT:
requires: Windows, linking with winmm.lib
resolution: varies, between 1 and 10 milliseconds
efficiency: fast
other notes: recommended method of timing on Windows at this time

WINMM_QPC:
requires: Windows, linking with winmm.lib
resolution: varies, typically between 1 nanosecond and 1 microseconds
efficiency: slow, typically 0.1-1.0 microseconds per call
other notes: buggy on many computers

RDTSC:
requires: x86 (including x86-64) CPU, Pentium or more recent
resolution: varies, typically between 0.1 and 30 nanoseconds
efficiency: fast, typically between 5 and 100 nanoseconds
other notes: buggy on some multi-CPU systems (different times on diff. CPUs)

gettimeofday:
requires: UNIX (including linux, and many other unix-like systems)
resolution: varies, at best 1 microsecond, at worst ?100 microseconds? (guess)
efficiency: no idea (untested)
other notes: may run backwards due if the system time is adjusted

LIBC:
requires: an implementation of the C standard library
resolution: shitty: 1 second
efficiency: varies
other notes: the method of last resort

Allegro:
requires: Allegro
resolution: typically between 1 and 10 milliseconds
efficiency: extremely fast on a per-call basis, but high overhead to install at all
other notes: none

SDL:
requires: SDL
resolution: typically between 1 and 10 milliseconds
efficiency: ?fast? (untested)
other notes: none

other methods to consider for future use:
GetTickCount:
requires: Windows ???
resolution: typically between 55 and ?1? milliseconds
efficiency: ???
other notes: ???
libC clock:
requires: standard C library
resolution: frequently 55 milliseconds
efficiency: ???
other notes: documentation varies widely, inconsistent
clock_gettime:

*/


#ifndef NO_LIBC
# include <time.h>
# include <math.h>
#endif
#include "types.h"
#include "get_time.h"
//#include "errors.h"

//static void get_time_error_function_impl ( const char *desc ) {}
//void (*get_time_error_function) (char *) = get_time_error_function_impl;
void (*get_time_error_function) (char *) = 0;
static void get_time_error(char * msg) {
get_time_error_function(msg);
}


#ifdef NO_ALLEGRO_TIME
# undef PLATFORM_IS_ALLEGRO
#endif
#ifdef NO_SDL_TIME
# undef PLATFORM_IS_SDL
#endif

//converting all x86-declaring preprocessor symbols to __x86__
#ifndef __x86__
# if defined __i386__ || defined __i386 || defined i386 || defined __I386__ || defined _M_IX86 || defined _X86 || defined __386__ || defined __x86__
# define __x86__
# endif
# if defined __x86_64__ || defined _M_X64
# define __x86__
# endif
#endif
#ifndef __x86_64__
# if defined __x86_64__ || defined _M_X64
# define __x86__
# define __x86_64__
# endif
#endif

//same for unix
#ifndef __unix__
# if defined __unix || defined _unix || defined _UNIX || defined __UNIX__ || defined __UNIX || defined __unix_
# define __unix__
# endif
#endif
//same for win32
#ifndef WIN32
# if defined _WIN32 || defined __WIN32
# define WIN32
# endif
#endif

/*
------------------------------
Timers & Stuff
------------------------------

Three sections:
1. idle() functions for Allegro / SDL / unrecognized platform
2. Platform-specific time functions:
LibC time returns integer seconds (fast?)
Allegro interrupt returns integer milliseconds (fast)
SDL ticks returns integer milliseconds (fast?)
Windows Multi-Media timeGetTime returns integer milliseconds (fast)
Windows Multi-Media Performance Counter returns integer high resolution ticks (slow)
Intel RDTSC returns integer high resolution ticks (fast)
unix gettimeofday() returns integer microseconds (???)
3. Wrappers to pick which time functions to use
get_time_ms() order of preference:
Windows Multi-Media timeGettime
SDL ticks
Allegro interrupt
unix gettimeofday()
Windows Multi-Media Performance Counter
Libc time
get_time_s() order of preference:
Windows Multi-Media Performance Counter
unix gettimeofday()
get_time_ms()
get_time_ticks() order of preference:
Intel RDTSC
Windows Multi-Media Performance Counter
unix gettimeofday()
get_time_ms()
*/







//1. idle() functions for Allegro / SDL / unknown platform




int _no_idle = 0;

#if defined(PLATFORM_IS_ALLEGRO) && !defined(NO_ALLEGRO_TIME)
# include <allegro.h>
# if defined WIN32
# include <winalleg.h>
# endif
int idle ( int time ) {
if (_no_idle) return 0;
rest(time);
return time;
}
#elif defined(PLATFORM_IS_SDL) && !defined(NO_SDL_TIME)
# include <SDL.h>
int idle ( int time ) {
if (_no_idle) return 0;
SDL_Delay ( time );
return time;
}
#else
//# error get_time.c - unknown platform
int idle ( int time ) {
if (_no_idle) return 0;
return 0;
/*int i = get_time() + time;
while (i > get_time()) {
//do nothing real fast
}
return time;*/
}
#endif




//2. Platform specific time functions:



#if !defined NO_GETTIMEOFDAY && defined __unix__
# define GETTIMEOFDAY_TIME
# include <sys/time.h>
static volatile Sint64 gettimeofday_get_time_microseconds() {
timeval tv;
gettimeofday( &tv, NULL );
return (Sint64(tv.tv_sec) * 1000000) + tv.tv_usec;
}
static volatile double gettimeofday_get_time_seconds() {
timeval tv;
gettimeofday( &tv, NULL );
return tv.tv_sec + 0.000001d * tv.tv_usec;
}
#endif

//RDTSC:
//RDTSC is only valid on pentiums & later, right?
//so how do we detect this at run-time? (without crashing)
#if !defined NO_RDTSC
# if defined _MSC_VER && defined __x86__
# define RDTSC_TIMER
static volatile Sint64 rdtsc_get_time() {
unsigned int a, b;
__asm RDTSC
__asm mov a, eax
__asm mov b, edx
return (a + ((Sint64)b << 32));
}
# elif defined __GNUC__ && defined __x86__
# define RDTSC_TIMER
static __volatile__ Uint64 rdtsc_get_time() {
// unsigned int a, b;
// asm ("RDTSC" : "=a" (a), "=d" (b) : );
// return (a + ((Sint64)b << 32));
unsigned long long int r;
asm ("RDTSC" : "=A" (r) : );
return r;
}
# endif
#endif

#if defined WIN32 && !(defined NO_WINMM_TGT && defined NO_WINMM_QPC)
# include <windows.h>
# include <Mmsystem.h>
# include <winbase.h>

# if !defined NO_WINMM_TGT
# define WINMM_TGT
# define winmm_tgt_period_i 1
static int winmm_tgt_get_time() {
return timeGetTime();
}
# endif

# if !defined NO_WINMM_QPC
# define WINMM_QPC
static double winmm_qpc_period_f = 1;
static int winmm_qpc_period_i = 0;
static Uint64 winmm_qpc_base = 0;
static volatile Uint64 winmm_qpc_get_time() {
LARGE_INTEGER bob;
if (QueryPerformanceCounter(&bob)) {
return bob.QuadPart;
}
get_time_error ( "Windows Performance Counter failed\n");
return -1;
}
static double winmm_qpc_get_freq() {
LARGE_INTEGER fred;
if (QueryPerformanceFrequency(&fred)) {
return (double)fred.QuadPart;
}
get_time_error ( "Windows Performance Counter failed\n");
return 0;
}
# endif
#endif

#if defined PLATFORM_IS_ALLEGRO
# include <allegro.h>
# define allegro_period 5
static volatile int allegro_time = 0;
static volatile int allegro_get_time() {
return allegro_time;
}
static void allegro_timer_function(void) {
allegro_time += allegro_period;
}
END_OF_STATIC_FUNCTION(allegro_timer_function);
static int allegro_time_inited = 0;
void init_allegro_time() {
if (allegro_time_inited++) return;
LOCK_FUNCTION(allegro_timer_function);
LOCK_VARIABLE(allegro_time);
if (install_timer() < 0) get_time_error("Allegro timer initialization failed");
install_int(&allegro_timer_function, allegro_period);
}
void deinit_allegro_time() {
if (--allegro_time_inited > 0) return;
if (allegro_time_inited < 0) get_time_error("get_time.c - deinit_allegro_time() called incorrectly");
remove_int(&allegro_timer_function);
}
#elif defined PLATFORM_IS_SDL
# include <SDL.h>
static int sdl_time_inited = 0;
static int sdl_get_time() {
return SDL_GetTicks();
}
static void init_sdl_time() {
if (sdl_time_inited++) return;
SDL_InitSubSystem(SDL_INIT_TIMER);
}
static void deinit_sdl_time() {
if (--sdl_time_inited > 0) return;
if (sdl_time_inited < 0) get_time_error("get_time.c - deinit_sdl_time() called incorrectly");
SDL_QuitSubSystem(SDL_INIT_TIMER);
}
#else
#endif

#if !defined NO_LIBC
# define LIBC_TIME
int libc_get_time() {
return time(NULL);
}
#endif





//3. The exported wrappers for time functions




//volatile int get_time();
/* get_time() order of preference:
Windows Multi-Media timeGettime
SDL ticks
Allegro interrupt
unix gettimeofday()
Windows Multi-Media Performance Counter
Libc time
*/
static int get_time_ms_base;
#if 0
#elif defined WINMM_TGT//
volatile int get_time() {return winmm_tgt_get_time() - get_time_ms_base;}
static void init_get_time_ms() {};
static void deinit_get_time_ms() {};
#elif defined PLATFORM_IS_SDL//
volatile int get_time() {return sdl_get_time() - get_time_ms_base;}
static void init_get_time_ms() {init_sdl_time();}
static void deinit_get_time_ms() {deinit_sdl_time();}
#elif defined PLATFORM_IS_ALLEGRO
volatile int get_time() {return allegro_get_time() - get_time_ms_base;}
static void init_get_time_ms() {init_allegro_time();};
static void deinit_get_time_ms() {deinit_allegro_time();};
#elif defined GETTIMEOFDAY_TIME
volatile int get_time() {return int(gettimeofday_get_time_microseconds() / 1000) - get_time_ms_base;}
static void init_get_time_ms() {};
static void deinit_get_time_ms() {};
/*
# elif defined WINMM_QPC
static double get_time_ms_period = 0;
volatile int get_time() {
return (int)(winmm_qpc_get_time() * get_time_ms_period) - get_time_ms_base;
}
static void init_get_time_s() {get_time_ms_period = 1000.0 / winmm_qpc_get_freq();};
static void deinit_get_time_s() {get_time_ms_period = 0;};
*/
#elif defined LIBC_TIME
volatile int get_time() {return libc_get_time() - get_time_ms_base;}
static void init_get_time_ms() {};
static void deinit_get_time_ms() {};
#else
# error No millisecond time function specified!
#endif


//volatile double get_time_s();
/* get_time_s() order of preference:
Windows Multi-Media Performance Counter
unix gettimeofday()
get_time_ms()
*/
static double get_time_s_base;
# if 0
# elif defined WINMM_QPC
static double get_time_s_period = 0;
volatile double get_time_s() {
return winmm_qpc_get_time() * get_time_s_period - get_time_s_base;
}
static void init_get_time_s() {get_time_s_period = 1.0/winmm_qpc_get_freq();};
static void deinit_get_time_s() {get_time_s_period = 0;};
#elif defined GETTIMEOFDAY_TIME
volatile double get_time_s() {return gettimeofday_get_time_s() - get_time_s_base;}
static void init_get_time_s() {};
static void deinit_get_time_s() {};
# else
volatile double get_time_s() {return get_time_ms() * 0.001;}
static void init_get_time_s() {init_get_time_ms();};
static void deinit_get_time_s() {deinit_get_time_ms();};
# endif


//volatile Sint64 get_time_ticks();
/* get_time3() order of preference:
Intel RDTSC
unix gettimeofday()
Windows Multi-Media Performance Counter
get_time_ms()
*/
static Sint64 get_time_ticks_inited;
#if 0
#elif defined RDTSC_TIMER
//require debuging mode, since we don't have run-time check
volatile Sint64 get_time_ticks() {return rdtsc_get_time();}
static void init_get_time_ticks() {};
static void deinit_get_time_ticks() {};
# define NEED_GUESS_TICKS
#elif defined WINMM_QPC
volatile Sint64 get_time_ticks() {return winmm_qpc_get_time();}
static void init_get_time_ticks() {};
static void deinit_get_time_ticks() {};
volatile double get_time_tick_period() {return 1.0 / winmm_qpc_get_freq();}
#elif defined GETTIMEOFDAY_TIME
volatile Sint64 get_time_ticks() {return gettimeofday_get_time_microseconds();}
static void init_get_time_s() {};
static void deinit_get_time_s() {};
volatile double get_time_tick_period() {return 0.000001;}
#else
volatile Sint64 get_time_ticks() {return get_time_ms();}
static void init_get_time_ticks() {init_get_time_ms();};
static void deinit_get_time_ticks() {deinit_get_time_ms();};
volatile double get_time_tick_period() {return 0.001;}
#endif

#ifdef NEED_GUESS_TICKS
volatile double get_time_tick_period() {
return get_time_s() / (get_time_ticks() - get_time_ticks_inited);
}
#endif

static unsigned char time_inited = 0;
void init_time() {
if (time_inited++) get_time_error("get_time - init_time() called repeatedly w/o deinit_time()");
init_get_time_ms();
init_get_time_s();
init_get_time_ticks();
get_time_s_base = get_time_s();
get_time_ms_base = get_time_ms();
get_time_ticks_inited = get_time_ticks();
/* int ms;
if (timer_attributes & 1) return;
timer_attributes |= 1;
# if defined WINMM_TGT
{
int tmp = winmm_tgt_get_time();
int i;
for (i = 0; i < 1000000; i += 1)
if (winmm_tgt_get_time() != tmp) break;
winmm_tgt_base = winmm_tgt_get_time();
}
# endif
# if defined PLATFORM_IS_SDL
init_sdl_time();
sdl_base = sdl_get_time();
# endif
# if defined PLATFORM_IS_ALLEGRO
LOCK_FUNCTION(get_time);
init_allegro_time();
allegro_base = allegro_get_time();
# endif
# if defined LIBC_TIME
libc_base = libc_get_time();
# endif

ms = get_time();
//finished initializing get_time()
//now we do get_time2() / get_time3()

# if defined RDTSC_TIMER
//require debuging mode, since we don't have a run-time check
rdtsc_base = rdtsc_get_time();
# endif
# if defined WINMM_QPC
winmm_qpc_base = winmm_qpc_get_time();
winmm_qpc_period_f = 1000.0 / winmm_qpc_get_freq();
# endif


# if defined RDTSC_TIMER
{
Sint64 tmpl;
double tmpd;
while (get_time() - ms < 100) ;
tmpl = rdtsc_get_time();
tmpd = tmpl * (1.0 / (get_time() - ms));
# if !defined NO_LIBC
{
double tmpd2, tmpd3;
//most clock speeds are multiples of nice numbers
//like 25.0 or 33.333 Mega-Hertz.
tmpd2 = floor(0.5 + tmpd / (100000/4.0)) * (100000/4.0);
tmpd3 = floor(0.5 + tmpd / (100000/3.0)) * (100000/3.0);
if (fabs(tmpd2-tmpd) < fabs(tmpd3-tmpd)) tmpd = tmpd2;
else tmpd = tmpd3;
}
# endif
rdtsc_period_f = 1 / tmpd;
}
# endif
*/
}

void deinit_time() {
if (--time_inited) get_time_error("get_time - deinit_time() called without init_time()");
deinit_get_time_ticks();
deinit_get_time_s();
deinit_get_time_ms();
}
[/code]

All these examples comes from [url http://sourceforge.net/projects/pmask]pmask library[/url], by Orz.

I think looking at it will definitely solve your problem Paul.
CGamesPlay
Quote:

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 :P

Paul whoknows

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 
7struct timer
8{
9LARGE_INTEGER tstart, tticks, tnow, tlast;
10int high_freq, logic_frames, logic_fps, gfx_frames, gfx_fps;
11};
12 
13extern struct timer timer;
14 
15void start_timer(void);
16void reset_timer(void);
17int check_timer(int frac_sec);
18void 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 
8struct timer timer;
9 
10void start_timer(void)
11{
12timer.high_freq = QueryPerformanceFrequency(&timer.tticks);
13timer.logic_frames = 0;
14 
15if (timer.high_freq)
16{
17QueryPerformanceCounter(&timer.tstart);
18timer.tlast = timer.tstart;
19}
20}
21 
22void reset_timer(void)
23{
24if (timer.high_freq)
25{
26 
27QueryPerformanceCounter(&timer.tnow);
28timer.tstart = timer.tnow;
29}
30}
31 
32int check_timer(int frac_sec)
33{
34int t;
35 
36if (timer.high_freq)
37{
38QueryPerformanceCounter(&timer.tnow);
39t = (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?
42if (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 
48timer.tstart = timer.tlast;
49t -= timer.logic_frames;
50timer.logic_fps = timer.logic_frames;
51timer.logic_frames = 0;
52 
53timer.gfx_fps = timer.gfx_frames;
54timer.gfx_frames = 0;
55}
56timer.tlast = timer.tnow;
57return t;
58}
59 
60return 0;
61}

my loop

1start_timer();
2do
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?
.
.
.
.
.
.

Quote:

Paul: read this, especially where he says "How to use KC's routines:"

thread's title said:

I need a high resolution timer with QPC

Quote:

You should not use high resolution timers in main loops

I disagree.

Quote:

You are totally high jacking the thread.

I agree.

.
.
.
[EDIT]
GullRaDriel I'll try that now, thanks!

Richard Phipps

Paul, did you try that change the 240 to 60 thing I mentioned?

GullRaDriel

From Pmask libray you have test.c and test2.c which use Orz's timing routines so all your need are filled .

Paul whoknows
Quote:

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

Richard Phipps

I can't see how that would happen if you had no 240 references anymore.. but anyway, try Orz's code. :)

Paul whoknows

How should I use Orz's routines?
I need to see a main loop implemented with those routines to understand how they work.

orz
Quote:

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.

Quote:

thread's title said:
I need a high resolution timer with QPC

That was the title, but your original post said

Quote:

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:

1double ms_per_QPC_tick=0;
2void 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}
9volatile 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:

Quote:

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.

Quote:

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)

Paul whoknows

Thank you very much! I'll try to implement your routines now!

Thread #589730. Printed from Allegro.cc