Wednesday, 26 January 2011

Super high precision wxDateTime

I've been working slowly on the promised program that would show some test patterns for your beloved monitor.
Recently I got stuck on the response time "patterns" for two reasons:
1. wxWidgets that I used for the platform don't exactly support fast rendering. Only method supported is GDI and OpenGL has issues with text.
2. Even deciding on what to use I still had a serious issue of wxWidgets time functions not having the required resolution. The best they manage - with wxDateTime::UNow() - is around 15ms resolution which suffices for 67FPS assuming those FPS match screen refresh intervals.

It doesn't help if I can reach 250000 FPS, but the numbers on those frames remain the same, so I went looking for a better / more precise version.

Turns out this isn't in such a high demand or at least that there aren't many solutions around.
So I just wrote my own.
I used the wxDateTime class which is already precise to the millisecond (in data, not in implementation). Unfortunately for me, but still precise enough since my solution manages quite a bit more. On my particular computer, 1/3339736 seconds precision. That is better than 1 microsecond precision.
Also, my solution is platform specific (windows) since I don't yet have any relevant Linux development VMs. If anyone cares to add cross platform code, I'm all inbox for changes required :)

I give you super high precision timer by courtesy of WTFPL license. Enjoy.

supertimer.h
#ifndef __supertimer_h
#define __supertimer_h

#include "windows.h"
#include "wx/datetime.h"

void initSuperTimer();
wxDateTime getDateTime();

#endif


supertimer.cpp
#include "supertimer.h"

LARGE_INTEGER offsetCounter, perfFreq;
wxDateTime refTime;

void initSuperTimer()
{
//Initializes global variables for use in getDateTime() function
QueryPerformanceFrequency(&perfFreq);
wxDateTime a;
a = wxDateTime::UNow();
while (((refTime = wxDateTime::UNow()) - a).GetMilliseconds() == 0)
; //This loop really hopes that UNow() has a decent resolution, otherwise it will take forever :(
QueryPerformanceCounter(&offsetCounter);
}

wxDateTime getDateTime()
{
//Gets system time accurate to the millisecond
//It could do more, but unfortunately wxDateTime isn't that precise
wxDateTime now, ref;
LARGE_INTEGER pc;
QueryPerformanceCounter(&pc);
pc.QuadPart -= offsetCounter.QuadPart;
pc.QuadPart *= 1000;
pc.QuadPart = pc.QuadPart / perfFreq.QuadPart;
ref = wxDateTime::UNow(); //Get system time for reference
now = refTime + wxTimeSpan(0, 0, 0, pc.QuadPart); //Calculate current time from reference time and time elapsed since then
if ((now - ref).GetMilliseconds() > 125)
{ //If there is more than 125ms difference between calculated and system time, reinitialize
//This also assumes that wxDateTime::UNow() is at least precise to 125ms. If it's not, this
//will constantly reinitialize the globals
initSuperTimer();
return getDateTime();
}
return now;
}

1 comment:

David Borrowman said...

Nice code! I know this is a sort of old thread, but I happen to have another page open that shows how to do high resolution timers in Mac, Linux, and Windows... gettimeofday() is available through sys/time.h on linux/mac which provides time comparison accurate to microseconds...

See: http://www.songho.ca/misc/timer/timer.html for example code

One could easily combine these two examples for high resolution time comparison in wxWidgets....

One thing to note, when using wxWidgets (which I am also using!), you will be limited by the speed of the message pump in the main wx thread... While your time comparisons will be accurate, you are still limited by the frequency of that thread... So if your planning to run some sort of code say every 10 microseconds, your gonna run into bottlenecks due to the wxWidgets message pump.....

Which is exactly what I need to do... I'm planning on spawning a separate thread (I always do this in an os-specific fashion, but I think wxWidgets will do it natively with wx/thread.h) which will do nothing but service high resolution timers...

So anyone attempting to build a high resolution version of wxTimer may want to do something like that... Just be aware you'll have to deal with all the headaches of making thread safe code! (something I paid no attention to as a new programmer!)