Tutorial : Timing And Movement Part 1
hack hack hack!!
I used Serg's class! but (always a but!) someone told me the internal irrlicht time was not good enough. so I made some 'improvements.'. This clock should be accurate up to microseconds. It is not quite done - does someone want to help with the windows specific code? (fill in the blanks!!)
Code: Select all
#ifndef IRRTIMER_H
#define IRRTIMER_H
#include <irrlicht.h>
// #include "iosys.h"
#ifdef _WIN32
#include <windows.h> // ??
#endif
#ifdef _POSIX
#include <sys/time.h>
#endif
using namespace irr;
class irrtimer {
public:
IrrlichtDevice *m_device; // pointer to Irrlicht device
irrtimer() {};
irrtimer(IrrlichtDevice *device){
m_device = device;
Reset(); // init timer
};
// irrlicht built-in timer
int m_Delta, m_Time; // time since last clock update, current time
float m_Factor; // time factor
#ifdef _WIN32 // win32 high-res timer
// ??
#endif
#ifdef _POSIX // POSIX high-res timer ( Linux, BSD, Mac )
struct timeval tv; //time since last clock update
struct timeval ts; //time since last clock update
float m_fDelta, m_fTime, m_fFactor;
irr::f32 m_iFactor; // time factor
#endif
void Reset() {
// irrlicht built in timer
m_Time = m_device->getTimer()->getTime();
#ifdef _WIN32 // win32 high-res timer
// ??
#endif
#ifdef _POSIX // POSIX high-res timer
gettimeofday( &ts, 0);
gettimeofday( &tv, 0);
m_fTime = ( tv.tv_sec * 1000) + ( tv.tv_usec / 1000 );
#endif
};
void Update(){
// irrlicht built in timer
m_Delta = m_device->getTimer()->getTime() - m_Time;
m_Time = m_Time + m_Delta;
m_Factor = m_Delta * 0.001f;
// cout << "irrlicht built in time :" << m_Factor << endl;
#ifdef _WIN32 // win32 high-res timer
// ??
#endif
#ifdef _POSIX // POSIX high-res timer
gettimeofday(&tv, 0);
m_fDelta = ( (( tv.tv_sec * 1000) + ( tv.tv_usec / 1000 )) -
(( ts.tv_sec * 1000) + ( ts.tv_usec / 1000 )) -
m_fTime);
m_fTime = m_fTime + m_fDelta;
m_fFactor = m_fDelta * 0.001f;
// cout << "POSIX high-res time :" << m_fFactor << endl;
#endif
};
};
#endif // IRRTIMER_H
Last edited by xhrit on Thu Apr 13, 2006 6:12 pm, edited 1 time in total.
Core2Duo E8400 3.0ghz - 2048mb DDR2 800 - geForce 9600 - Slackware12.1
Word-image-symbol programming limits, controls, and imprisons the individual.
Smash the control images, smash the control machine.
Word-image-symbol programming limits, controls, and imprisons the individual.
Smash the control images, smash the control machine.
I don't see where you made a real progress compared to Irrlicht native Timer. You both use gettimeofday such that the base is the same. What's the puspose of your delta calculations then? Moreover, gettimeofday can give precision in microseconds, so why not use these values? On the other hand, having milliseconds allows for 1000 FPS if each cycle can be used, so what benefits would come from this?
...
Here is how I am using it for fixed timestep.
uncomment the output lines and you will get something like this in the console window :
...
irrlicht built in time :0.021
POSIX high-res time :0.021
irrlicht built in time :0.02
POSIX high-res time :0.019
irrlicht built in time :0.019
POSIX high-res time :0.02
irrlicht built in time :0.02
POSIX high-res time :0.021
...
As you can see... they are not the same. what one is more accurate? I don't know. All I know was that someone in irc told me there are issues with the built in timer.
It has been brought to my attention that to get microsecond accuracy I needed to use a 64bit datatype to store the value in. How would I go about doing that?
>You both use gettimeofday such that the base is the same.
What do you mean?
>having milliseconds allows for 1000 FPS if each cycle can be used, so what benefits would come from this?
It is all about the accumulator and smoothing out the simulation. I find that overall there is less jitteryness on lower end systems with the high res timer.
Code: Select all
sim_timer->Update();
float delta = sim_timer->m_fFactor;
delta += accumulator;
if ( delta > step*5 ) { delta = step*5; }
if ( delta < step/5 ) { delta = step/5; }
for(; delta >= step; delta -= step){
m_simsys.Update(keybuffer);
}
accumulator = delta;
uncomment the output lines and you will get something like this in the console window :
...
irrlicht built in time :0.021
POSIX high-res time :0.021
irrlicht built in time :0.02
POSIX high-res time :0.019
irrlicht built in time :0.019
POSIX high-res time :0.02
irrlicht built in time :0.02
POSIX high-res time :0.021
...
As you can see... they are not the same. what one is more accurate? I don't know. All I know was that someone in irc told me there are issues with the built in timer.
It has been brought to my attention that to get microsecond accuracy I needed to use a 64bit datatype to store the value in. How would I go about doing that?
>You both use gettimeofday such that the base is the same.
What do you mean?
>having milliseconds allows for 1000 FPS if each cycle can be used, so what benefits would come from this?
It is all about the accumulator and smoothing out the simulation. I find that overall there is less jitteryness on lower end systems with the high res timer.
Core2Duo E8400 3.0ghz - 2048mb DDR2 800 - geForce 9600 - Slackware12.1
Word-image-symbol programming limits, controls, and imprisons the individual.
Smash the control images, smash the control machine.
Word-image-symbol programming limits, controls, and imprisons the individual.
Smash the control images, smash the control machine.
I just happened to be searching and came across your post for windows specific timer.. here is a class I have used for a long time. Feel free to pick pieces out of it or use the whole thing
/*******************************************************************
* Copyright (C) 2003 Brett R. Jones issued to public domain
*********************************************************************/
#ifndef TIMER_H
#define TIMER_H
//=== VxTimer Class object ===//
//there are a couple of ways to use the timer
// any call to StartTime with any value sets the start time
// so that calls to ElapsedTime return the Time since last called StartTimer or timer was constucted,
// The second mode is StartTimer with a time till done
// Susequent calls to TimeDone return FALSE if the specified TimeTillDone has not been
// reached or TRUE if the time has expired
//
// NOTE: even after the TimerDone has expired, calls to ElapsedTimer are still valid
class VxTimer
{
private:
__int64 m_i64StartTick;
__int64 m_i64TimerFrequency;
double m_dInverseFrequency;
double m_dTimeTillDone;
public:
VxTimer(){Init();}
~VxTimer( void ){}
void Init( void );//this function only added for instances when constructor doesnt get called
double ElapsedSec( void ); //return the elapsed time in seconds since
//StartTimer was called or VxTimer was constructed...it doesnt mater if the time
//specified in the StartTimer parameter has elapsed or not
double ElapsedMilliSec( void ){return (ElapsedSec()*1000);}
double ElapsedMicroSec( void ){return (ElapsedSec()*1000000);};
//just sets start time for elapsed time
void StartTimer( void ){QueryPerformanceCounter((LARGE_INTEGER *) &m_i64StartTick );}
//starts a timer and sets time till TimerDone returns true
void StartTimer( double dSec );
void StartMilliTimer( double dMilliSec ){ StartTimer( dMilliSec * 1000.0 );}
void StartMicroTimer( double dMicoSec ){ StartTimer( dMicoSec * 1000000.0 );}
BOOL TimerDone( void ){return ElapsedSec() * 1000000 > m_dTimeTillDone?1:0;}
void WaitTime( double dMicroSec );
};
//declare one for global use
extern VxTimer g_gTimer;
#endif //TIMER_H
/*******************************************************************
* Copyright (C) 2003 Brett R. Jones issued to public domain
*********************************************************************/
#include <stdafx.h>
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
//#define TEST_TIMER 1 //define this to use the test program
#include "util/VxTimer.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
VxTimer g_gTimer;//global declaration
void VxTimer::Init( void )
{
QueryPerformanceFrequency( (LARGE_INTEGER *) &m_i64TimerFrequency );
QueryPerformanceCounter( (LARGE_INTEGER *) &m_i64StartTick );
m_dInverseFrequency = (1.0 / ((double)m_i64TimerFrequency));
}
void VxTimer::StartTimer( double dSec )
{
//get current tick count
//currently the Counter Frequency for win95 intel is 1,193,180 Hz or approx .000000838 sec
//a 32 bit counter will rollover in 3600 seconds or 1 Hour
//the 64 bit counter will rollover in 4294967296 hours or 502,000 years
QueryPerformanceCounter( (LARGE_INTEGER *) &m_i64StartTick );
m_dTimeTillDone = dSec * 1000000;
}
void VxTimer::WaitTime( double dMicroSec )
{
//===for all other modes just loop and wait for time to run out ===//
StartMicroTimer( dMicroSec );
int iFirstTime = TRUE;
while(! TimerDone())
{
// we want to sleep as much as possible so we dont waist cpu cycles but we
// want to be awake when timer is done
if( iFirstTime )
{
iFirstTime = FALSE;
if( dMicroSec > 2000 )
Sleep((long)(( dMicroSec -2000 ) / 1000.0));//should wake up 2ms before timer is done
iFirstTime = 0;
}
}
}
double VxTimer::ElapsedSec( void )
{
__int64 i64EndTick;
QueryPerformanceCounter( (LARGE_INTEGER *) &i64EndTick);
return (double)( i64EndTick-m_i64StartTick) * m_dInverseFrequency;
}
#ifdef TEST_TIMER
int main(int argc, char *argv[])
{
VxTimer gTimer;
double dDelay;
long lLoopCnt;
long i;
while(TRUE)
{
printf("\n VxTimer Frequency = %lf", gTimer.GetTimerFrequency());
printf("\n Enter Time To Delay in Micro Seconds ");
scanf("%lf", &dDelay);
printf("\n Enter The Number of Times to Delay ");
scanf("%ld", &lLoopCnt);
printf("\nNow Executing using TimerDone function");
for(i = 0; i < lLoopCnt; i++)
{
gTimer.StartMicroTimer(dDelay);
while(FALSE == gTimer.TimerDone())
;
}
printf("\nNow Executing using Elapsed Time function");
for(i = 0; i < lLoopCnt; i++)
{
gTimer.StartMicroTimer(0);
while(dDelay > gTimer.ElapsedMicroSec())
;
}
printf("\n*****************Finished******************");
printf("\nPress X to exit or any other key to do again \n");
fflush(stdin);
int val = getch();
if('X' == val || 'x' == val)
return 0;
}
}
#endif //TEST_TIMER
/*******************************************************************
* Copyright (C) 2003 Brett R. Jones issued to public domain
*********************************************************************/
#ifndef TIMER_H
#define TIMER_H
//=== VxTimer Class object ===//
//there are a couple of ways to use the timer
// any call to StartTime with any value sets the start time
// so that calls to ElapsedTime return the Time since last called StartTimer or timer was constucted,
// The second mode is StartTimer with a time till done
// Susequent calls to TimeDone return FALSE if the specified TimeTillDone has not been
// reached or TRUE if the time has expired
//
// NOTE: even after the TimerDone has expired, calls to ElapsedTimer are still valid
class VxTimer
{
private:
__int64 m_i64StartTick;
__int64 m_i64TimerFrequency;
double m_dInverseFrequency;
double m_dTimeTillDone;
public:
VxTimer(){Init();}
~VxTimer( void ){}
void Init( void );//this function only added for instances when constructor doesnt get called
double ElapsedSec( void ); //return the elapsed time in seconds since
//StartTimer was called or VxTimer was constructed...it doesnt mater if the time
//specified in the StartTimer parameter has elapsed or not
double ElapsedMilliSec( void ){return (ElapsedSec()*1000);}
double ElapsedMicroSec( void ){return (ElapsedSec()*1000000);};
//just sets start time for elapsed time
void StartTimer( void ){QueryPerformanceCounter((LARGE_INTEGER *) &m_i64StartTick );}
//starts a timer and sets time till TimerDone returns true
void StartTimer( double dSec );
void StartMilliTimer( double dMilliSec ){ StartTimer( dMilliSec * 1000.0 );}
void StartMicroTimer( double dMicoSec ){ StartTimer( dMicoSec * 1000000.0 );}
BOOL TimerDone( void ){return ElapsedSec() * 1000000 > m_dTimeTillDone?1:0;}
void WaitTime( double dMicroSec );
};
//declare one for global use
extern VxTimer g_gTimer;
#endif //TIMER_H
/*******************************************************************
* Copyright (C) 2003 Brett R. Jones issued to public domain
*********************************************************************/
#include <stdafx.h>
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
//#define TEST_TIMER 1 //define this to use the test program
#include "util/VxTimer.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
VxTimer g_gTimer;//global declaration
void VxTimer::Init( void )
{
QueryPerformanceFrequency( (LARGE_INTEGER *) &m_i64TimerFrequency );
QueryPerformanceCounter( (LARGE_INTEGER *) &m_i64StartTick );
m_dInverseFrequency = (1.0 / ((double)m_i64TimerFrequency));
}
void VxTimer::StartTimer( double dSec )
{
//get current tick count
//currently the Counter Frequency for win95 intel is 1,193,180 Hz or approx .000000838 sec
//a 32 bit counter will rollover in 3600 seconds or 1 Hour
//the 64 bit counter will rollover in 4294967296 hours or 502,000 years
QueryPerformanceCounter( (LARGE_INTEGER *) &m_i64StartTick );
m_dTimeTillDone = dSec * 1000000;
}
void VxTimer::WaitTime( double dMicroSec )
{
//===for all other modes just loop and wait for time to run out ===//
StartMicroTimer( dMicroSec );
int iFirstTime = TRUE;
while(! TimerDone())
{
// we want to sleep as much as possible so we dont waist cpu cycles but we
// want to be awake when timer is done
if( iFirstTime )
{
iFirstTime = FALSE;
if( dMicroSec > 2000 )
Sleep((long)(( dMicroSec -2000 ) / 1000.0));//should wake up 2ms before timer is done
iFirstTime = 0;
}
}
}
double VxTimer::ElapsedSec( void )
{
__int64 i64EndTick;
QueryPerformanceCounter( (LARGE_INTEGER *) &i64EndTick);
return (double)( i64EndTick-m_i64StartTick) * m_dInverseFrequency;
}
#ifdef TEST_TIMER
int main(int argc, char *argv[])
{
VxTimer gTimer;
double dDelay;
long lLoopCnt;
long i;
while(TRUE)
{
printf("\n VxTimer Frequency = %lf", gTimer.GetTimerFrequency());
printf("\n Enter Time To Delay in Micro Seconds ");
scanf("%lf", &dDelay);
printf("\n Enter The Number of Times to Delay ");
scanf("%ld", &lLoopCnt);
printf("\nNow Executing using TimerDone function");
for(i = 0; i < lLoopCnt; i++)
{
gTimer.StartMicroTimer(dDelay);
while(FALSE == gTimer.TimerDone())
;
}
printf("\nNow Executing using Elapsed Time function");
for(i = 0; i < lLoopCnt; i++)
{
gTimer.StartMicroTimer(0);
while(dDelay > gTimer.ElapsedMicroSec())
;
}
printf("\n*****************Finished******************");
printf("\nPress X to exit or any other key to do again \n");
fflush(stdin);
int val = getch();
if('X' == val || 'x' == val)
return 0;
}
}
#endif //TEST_TIMER
This article may interest you for a good timer
http://www.gamedev.net/reference/progra ... es/timing/
I personally don't recommend using the irrlicht timer, I havn't had good experiences with it. The timer represented in that article seems to be a good start at the very least. I also like the platform independant timer that Ogre uses, it also represents a good starting point for someone, although Ogre does use a platform manager so it would require a bit more digging around to find its implementation.
http://www.gamedev.net/reference/progra ... es/timing/
I personally don't recommend using the irrlicht timer, I havn't had good experiences with it. The timer represented in that article seems to be a good start at the very least. I also like the platform independant timer that Ogre uses, it also represents a good starting point for someone, although Ogre does use a platform manager so it would require a bit more digging around to find its implementation.