Accurate event timer

Started by
14 comments, last by 3Dgonewild 16 years, 1 month ago
Haven't been around for a long time,since i was too busy with school&school projects. Now that im back , i decided to create something simple in my free time. Im trying to make something like a simple 3d demo(with sdl &opengl). I can hanlde most of the tasks(since i have old code that i can use) , but there is something really important that im not sure how to implement it. As title says ,im trying to create an accurate event timer, and in a demo , its REALLY important to be accurate, since the engine will execute a command set(like opcodes) based on time.. No , im not retard , i can calculate ms->secs->whatever, BUT , how can i make sure that it will be accurate on other machines,and the most part...it must be run on both linux & windows. A simple example would be more than helpful....
Advertisement
I'm not sure what kind of accuracy you're looking for, but timing in a 3D application is usually done by calculating a timedelta value (the time it took to do one iteration of the game loop). Using the timedelta value you can compensate for slower or faster hardware (slower hardware <=> timedelta is larger, faster hardware <=> timedelta is smaller), by simply multiplying all your transformations with timedelta.

I assume you are going to need that value if you're going to have any animation in your demo and as far as my knowledge goes, you can calculate timedelta quite accurately on windows (I'm not sure about linux, I don't develop for that platform). You can use the QueryPerformanceCounter function to retrieve the number of processor 'ticks' and the QueryPerformanceFrequency function to get a rather accurate result (think nanoseconds, as opposed to getTickCount, or timeGetTime).

Knowing this, I am sure you can figure out a way to add these values to a 'totalTimePassed' variable to allow you to accurately post events at an interval rate set by you. Unfortunately, I can't help you with the linux part of things...

Regards,

Rogier
I was learning to do this last night. I found this thread which gives an example of what rogierpennink is talking about:

http://www.gamedev.net/community/forums/topic.asp?topic_id=113457&whichpage=1򕌅

As for the linux side, I have no idea.
-...-Squeeged third eye.


Sorry , let me explain what im asking more detailed.

First off- , im not asking how to implement frame independent movement.

Lets start with some code:


//script class
typedef enum OP_LIST{OP_EXPL      	= 0,OP_CAMZOOMIN 	= 1,OP_CAMZOOMOUT	= 2,OP_QUIT         = 3,OP_UPDGFX       = 4};struct CscriptInfo{//commandunsigned int opcode;//will execute in defined timedouble   time;};Cscript::Cscript{        //command index        head=0;	//script size	bytes=0;}void Cscript::addTimerEvent(const unsigned int opcode,const double time){         //add event	m_Code.push_back( CscriptInfo(opcode,time) );        //update size 	bytes=m_Code.size();}void Cscript::next(){        //next code	head++;        //ensure script index is in valid range	assert(((head<bytes)&&(head>-1)));}double Cscript::getActiveEventTime() const;{        //get active script	const CscriptInfo* s = &m_Code[head];        //return time	return s.time;}void  Cscript::execute() {	use the op code...etc}


//Lets use the class..

        //create the script object	Cscript* script = new Cscript;        //memory allocated?	assert(script!=NULL);                //current time..        double currentTime=0.0;        //lets add a few events        //zoom camera after 500ms have passed	script->addTimerEvent(OP_CAMZOOMIN,500);	//make an explosion after 2000ms have passed	script->addTimerEvent(OP_EXPL,2000);        //our main loop	while(running)	{        //check if its about time to execute the active event	if(script->getActiveEventTime()==currentTime)        {        //yay! time to work..	//run the code 		script->execute();	//get next..	script->next();	}	currentTime+=1.0;	}        //release memory	delete script;        script = NULL;        //exit        return 0;


Now i think everyone's understand what i have in mind...right?

[Edited by - 3Dgonewild on February 29, 2008 8:51:52 AM]
I think I see what you mean, but wouldn't the tools remain the same? I have unfortunately never implemented anything like what you describe, but considering you still need to use a rather accurate timer like window's high performance timer, it all boils down to simply adding the delta_time of each frame to a totalTimePassed value (in my eyes, I could be entirely wrong of course).

I think the easiest way to have your events happening at exactly the right time is to have them sorted in a container. Let's assume you use something as simple as a list (assuming it's sorted), you would only have to compare the first element of that list to the current totalTimePassed value and if it's equal or greater than you execute the event and remove it from the list. Then the first element of the list corresponds to the next event so you'll only ever have to check the first element...

Again, I could be totally wrong, but this just seems the most sensible way of approaching what you're after....

Regards,

Rogier
Quote:Original post by rogierpennink
I think I see what you mean, but wouldn't the tools remain the same? I have unfortunately never implemented anything like what you describe, but considering you still need to use a rather accurate timer like window's high performance timer, it all boils down to simply adding the delta_time of each frame to a totalTimePassed value (in my eyes, I could be entirely wrong of course).

I think the easiest way to have your events happening at exactly the right time is to have them sorted in a container. Let's assume you use something as simple as a list (assuming it's sorted), you would only have to compare the first element of that list to the current totalTimePassed value and if it's equal or greater than you execute the event and remove it from the list. Then the first element of the list corresponds to the next event so you'll only ever have to check the first element...

Again, I could be totally wrong, but this just seems the most sensible way of approaching what you're after....

Regards,

Rogier


I really appreciate your help so far.
I'll wait for some more ideas , and then i will decide which path to follow.

Actually , my main problem is the counter,which adds +1.0 ms on every frame, and i
have a feeling that it wont work well on every pc..

I also have another idea.

If i create something like an event editor(were you can place events etc), and update the counter on every frame , like this:

timepassed += 0.01


Then i could add each event on button press for example..
Ah but that is where the timedelta comes in. You shouldn't add a fixed timevalue to the total time that has passed each frame, instead you should calculate how much time has passed since the previous frame and add that value to the total time that has passed. This is the essence of hardware-independent timing.

Let's just assume we have a function called 'getTime()' which returns the time that has passed since boot in nanoseconds:
total_time_passed = 0;previous_time = getTime();while ( running ){    if ( sorted_script_list.firstElement().time <= total_time_passed )        execute_script( sorted_script_list.firstElement() );    current_time = getTime();    timedelta = current_time - previous_time;    previous_time = current_time;    total_time_passed += timedelta;}

Doing it this way will ensure that you always measure the correct amount of time that has passed. If you keep the list of scripts sorted based on the time values you will only ever have to check the first element (provided you throw away the first element after you've 'executed' it). Of course, other constructions are possible here, but I think this is the only reliable way to measure the real amount of time that has passed.
Quote:Original post by rogierpennink
Ah but that is where the timedelta comes in. You shouldn't add a fixed timevalue to the total time that has passed each frame, instead you should calculate how much time has passed since the previous frame and add that value to the total time that has passed. This is the essence of hardware-independent timing.

Let's just assume we have a function called 'getTime()' which returns the time that has passed since boot in nanoseconds:
total_time_passed = 0;previous_time = getTime();while ( running ){    if ( sorted_script_list.firstElement().time <= total_time_passed )        execute_script( sorted_script_list.firstElement() );    current_time = getTime();    timedelta = current_time - previous_time;    previous_time = current_time;    total_time_passed += timedelta;}

Doing it this way will ensure that you always measure the correct amount of time that has passed. If you keep the list of scripts sorted based on the time values you will only ever have to check the first element (provided you throw away the first element after you've 'executed' it). Of course, other constructions are possible here, but I think this is the only reliable way to measure the real amount of time that has passed.



WOah , now thats something that i have to test out.

And as for sorting the list , its impossible , because it will mess the indexing.

For example , lets say that i have the following commands:

script->registerVariable("SOUND0","sample1.wav");//play sound #0 const std::string s = script->getVariable("SOUND0");//explosion animationscript->addcommand(OP_EXPL,2000);//play explosion sound after 2ms have passedscript->addcommandExt(OP_PLAYSOUND,s,1990);script->execute


Then , it will first play the sound, then the explosion..
A priority queue data structure exists to do just that, C++ provides std::priority_queue.

On every tick, you dequeue all events that have expired. This means that all event executions will be late.

For accurate events, you'd need a real-time OS.
I see, but remember that the list would be sorted. In other words, you could first add the explosion command, so the list would look like this:
+-------------+| EXPL - 2000 |+-------------+

Then you would add the sound command and notice that the first item in the list has a larger time value associated with it, so you insert the sound command in front of the explosion command:
+-------------+      +-------------+| SND - 1990  |      | EXPL - 2000 |+-------------+      +-------------+

However, for this to work well you would probably have to wrap your commands (and their possible parameters) into a class, so that you can just pass objects of that class into the list.

This topic is closed to new replies.

Advertisement