Sign in to follow this  
3Dgonewild

Accurate event timer

Recommended Posts

3Dgonewild    174
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....

Share this post


Link to post
Share on other sites
rogierpennink    400
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

Share this post


Link to post
Share on other sites
AriusMyst    130
I was learning to do this last night. I found this thread which gives an example of what rogierpennink is talking about:

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

As for the linux side, I have no idea.

Share this post


Link to post
Share on other sites
3Dgonewild    174


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
{
//command
unsigned int opcode;
//will execute in defined time
double 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]

Share this post


Link to post
Share on other sites
rogierpennink    400
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

Share this post


Link to post
Share on other sites
3Dgonewild    174
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..

Share this post


Link to post
Share on other sites
rogierpennink    400
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.

Share this post


Link to post
Share on other sites
3Dgonewild    174
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 animation
script->addcommand(OP_EXPL,2000);
//play explosion sound after 2ms have passed
script->addcommandExt(OP_PLAYSOUND,s,1990);
script->execute





Then , it will first play the sound, then the explosion..

Share this post


Link to post
Share on other sites
Antheus    2409
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.

Share this post


Link to post
Share on other sites
rogierpennink    400
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.

Share this post


Link to post
Share on other sites
Antheus    2409
Quote:
Original post by rogierpennink
I see, but remember that the list would be sorted.


Yes. And typical priority queue offers O(log n) insertion and O(1) access to highest priority element.

Sorting is implied here, typically through use of heap for implementation.

Share this post


Link to post
Share on other sites
rogierpennink    400
Quote:
Original post by Antheus
Yes. And typical priority queue offers O(log n) insertion and O(1) access to highest priority element.

Sorting is implied here, typically through use of heap for implementation.


I understand, I was replying to 3DGoneWild but you beat me to it, so it looks like I'm replying to your post now :P

Anyway, the usage of a list was merely an example; I am far from an expert in datastructures, I just wanted to express the importance of the event data being sorted by their time values so you can have O(1) access to the next event that is to take place.

Share this post


Link to post
Share on other sites
3Dgonewild    174
@antheus:

Thanks ,i'll check it out , but to be honest , itlooks kinda messy :)

Quote:
Original post by rogierpennink

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.


I lost you here...
You mean to replace OP code enumerator list , and create classes instead?

Something like:


class OP_SND
{
std::string file,name;
unsigned int status,**more options**
};

class OP_3DBOX
{
std::string name;
vector3 pos[4];
};

etc..?



But then how am i supposed to store them..??polymorphism..maybe?





Share this post


Link to post
Share on other sites
rogierpennink    400
If you want to use any sort of data structure for your events, you will have to define a uniform event type. Since an event has a number of parameters you need to specify a 'holder' for those parameters or you can't put it in any type of container. You don't even need polymorphism for this (at least, judging by the code you've shown so far), even a simple struct would do:
struct Event
{
int cmdType;
string name;
int numMilliSeconds;
};

Now, Event is an entity that you can put in a container, and when you retrieve an Event object from your container (because it ought to be triggered), you can simply call your addCommandExt function, pass the variables in the struct to it as arguments, and call your script->execute function.

Share this post


Link to post
Share on other sites
3Dgonewild    174
Quote:
Original post by rogierpennink
If you want to use any sort of data structure for your events, you will have to define a uniform event type. Since an event has a number of parameters you need to specify a 'holder' for those parameters or you can't put it in any type of container. You don't even need polymorphism for this (at least, judging by the code you've shown so far), even a simple struct would do:
struct Event
{
int cmdType;
string name;
int numMilliSeconds;
};

Now, Event is an entity that you can put in a container, and when you retrieve an Event object from your container (because it ought to be triggered), you can simply call your addCommandExt function, pass the variables in the struct to it as arguments, and call your script->execute function.


Thank you , probably i will do something like this.

Can't find a clean way to do it with polymorphism.

Well , anyway , here's what i came up with(i can't figure out how to
call the commands from the "opcode" class):


***SORRY for the "dirty" code , but its just an example while im trying to figure how to follow the "polyMorphic way"..***


#include <iostream>
#include <vector>
class opcode
{
private:
std::vector<opcode*> code;
public:
virtual void execute(){return;}
virtual double getExecutionTime(){return (0.0);}
void addCommand(opcode*op){code.push_back(op);}
};

class OP_NOP:public opcode{
public:
void execute(){}
};

class OP_PRINT:public opcode{
private:
std::string text;
double time;
public:
OP_PRINT(std::string s,double t){text=s;time=t;}
double getExecutionTime(){return time;}
void execute(){std::cout<<text<<std::endl;}
};

int main()
{
opcode *script = new opcode;
OP_PRINT p1("-3D Gone wild..",333),p2("-o'rly?..",339);
script->addCommand(&p1);
script->addCommand(&p2);
script->execute();//<---DOES nothing -_-"
p1.execute();
p2.execute();
delete script;
script=NULL;
return 0;
}




Share this post


Link to post
Share on other sites
3Dgonewild    174
Thank you so much , your suggestion worked!
Well , i had to do some critical changes in the timer , but anyway, without your help probably i would had to search more on the subject.

Thanks again.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this