PDA

View Full Version : frame rate and QTimer accuracy



totem
4th April 2011, 11:10
I have to read a file that describes animations frames at constant interval. i know this interval so I used QTimer to regularly trigger an update.
The problem is, when i approximately measure the time it takes (with a stopwatch), i see an important difference between theoretical and actual times.

So i wrote an example code showing my problem : It's a shape that go around in a scene. every round it makes I decrease the framerate and display few things :
- the time it took to make a round
- the time it should have taken, regarding current frame rate
- the relative difference between these 2 times



#include <QApplication>
#include <QtGui>


qreal interval = 1.0 ;
const qreal nbFrames = 100.0 ;


class View : public QGraphicsView
{
Q_OBJECT
QGraphicsScene *scene ;
QGraphicsRectItem *item ;
int timerid ;
qreal angle, step, radius ;
QTime t0, t1 ;

public :
View(void)
: scene(new QGraphicsScene),
angle(0), // start angle=0°
step(360.0/nbFrames), // 1 round == 852 frames
radius(150.0) // circle radius
{
scene->setBackgroundBrush(Qt::darkBlue) ;
setScene(scene) ;
// draw circle path
new QGraphicsEllipseItem(QRectF(-radius,-radius,2*radius,2*radius),0,scene) ;
// draw shape
item = new QGraphicsRectItem(QRectF(-25,-15,50,30),0,scene) ;
item->setBrush(Qt::darkYellow) ;
item->setPen(QPen(Qt::black)) ;
// draw start point
QGraphicsEllipseItem *start = new QGraphicsEllipseItem(QRectF(radius-2,-2,4,4),0,scene) ;
start->setBrush(Qt::red) ;
start->setPen(QPen(Qt::black)) ;
// start animation
t0 = QTime::currentTime() ;
qDebug() << "start at" << t0.toString("hh:mm:ss.zzz") << ", interval =" << interval ;
timerid = startTimer(interval) ;
}

protected :
void timerEvent(QTimerEvent *e)
{
if(angle>=360)
{
// compute time and restart
t1 = QTime::currentTime() ;
qreal theoric = 0.001*interval*nbFrames ;
qreal measured = 0.001*t0.msecsTo(t1) ;
qreal diff = (measured-theoric) ;
qreal delta = 100*diff/measured ;
qDebug() << "round time :" << measured << "s, should be" << theoric << "s (diff ="<<diff<<"("<<delta<<"%) for"<<interval<<")" ;
killTimer(timerid) ;
interval++ ; // decrease framerate
angle = 0 ;
t0 = QTime::currentTime() ;
timerid = startTimer(interval) ;
}
else
{
// compute new position
qreal radians = M_PI*angle/180.0 ;
qreal x = radius*cos(radians) ;
qreal y = radius*sin(radians) ;
item->setPos(x,y) ;
// step ahead
angle += step ;
}
}
} ;

int main(int argc, char** argv)
{
QApplication app(argc,argv) ;
View view ;
view.setGeometry(QRect(100,100,600,500)) ;
view.show() ;
return app.exec() ;
}

#include "main.moc"


And here is what I obtain on my comps (one desktop and one laptop, both on XP SP3 i guess) : After an interval of 20ms, whatever interval i give, the total time of an animation is the same for a range of a dozen framerate :



round time : 1.418 s, should be 1.4 s (diff = 0.018 ( 1.26939 %) for 14 )
round time : 1.512 s, should be 1.5 s (diff = 0.012 ( 0.793651 %) for 15
round time : 1.606 s, should be 1.6 s (diff = 0.006 ( 0.373599 %) for 16
round time : 1.715 s, should be 1.7 s (diff = 0.015 ( 0.874636 %) for 17
round time : 1.808 s, should be 1.8 s (diff = 0.008 ( 0.442478 %) for 18
round time : 1.917 s, should be 1.9 s (diff = 0.017 ( 0.886802 %) for 19
round time : 2.011 s, should be 2 s (diff = 0.011 ( 0.546992 %) for 20 )
round time : 3.149 s, should be 2.1 s (diff = 1.049 ( 33.3122 %) for 21 )
round time : 3.148 s, should be 2.2 s (diff = 0.948 ( 30.1144 %) for 22 )
round time : 3.149 s, should be 2.3 s (diff = 0.849 ( 26.9609 %) for 23 )
round time : 3.149 s, should be 2.4 s (diff = 0.749 ( 23.7853 %) for 24 )
round time : 3.149 s, should be 2.5 s (diff = 0.649 ( 20.6097 %) for 25 )
round time : 3.148 s, should be 2.6 s (diff = 0.548 ( 17.4079 %) for 26 )
round time : 3.149 s, should be 2.7 s (diff = 0.449 ( 14.2585 %) for 27 )
round time : 3.149 s, should be 2.8 s (diff = 0.349 ( 11.0829 %) for 28 )
round time : 3.149 s, should be 2.9 s (diff = 0.249 ( 7.90727 %) for 29 )
round time : 3.149 s, should be 3 s (diff = 0.149 ( 4.73166 %) for 30 )
round time : 3.149 s, should be 3.1 s (diff = 0.049 ( 1.55605 %) for 31 )
round time : 4.193 s, should be 3.2 s (diff = 0.993 ( 23.6823 %) for 32 )
round time : 4.723 s, should be 3.3 s (diff = 1.423 ( 30.1292 %) for 33 )
round time : 4.724 s, should be 3.4 s (diff = 1.324 ( 28.0271 %) for 34 )
round time : 4.723 s, should be 3.5 s (diff = 1.223 ( 25.8946 %) for 35 )
round time : 4.724 s, should be 3.6 s (diff = 1.124 ( 23.7934 %) for 36 )
round time : 4.723 s, should be 3.7 s (diff = 1.023 ( 21.66 %) for 37 )
round time : 4.723 s, should be 3.8 s (diff = 0.923 ( 19.5427 %) for 38 )
round time : 4.724 s, should be 3.9 s (diff = 0.824 ( 17.4428 %) for 39 )
round time : 4.723 s, should be 4 s (diff = 0.723 ( 15.3081 %) for 40 )
round time : 4.723 s, should be 4.1 s (diff = 0.623 ( 13.1908 %) for 41 )
round time : 4.724 s, should be 4.2 s (diff = 0.524 ( 11.0923 %) for 42 )
round time : 4.723 s, should be 4.3 s (diff = 0.423 ( 8.95617 %) for 43 )
round time : 4.724 s, should be 4.4 s (diff = 0.324 ( 6.85859 %) for 44 )
round time : 4.723 s, should be 4.5 s (diff = 0.223 ( 4.72158 %) for 45 )
round time : 4.723 s, should be 4.6 s (diff = 0.123 ( 2.60428 %) for 46 )
round time : 5.082 s, should be 4.7 s (diff = 0.382 ( 7.51673 %) for 47 )
round time : 6.298 s, should be 4.8 s (diff = 1.498 ( 23.7853 %) for 48 )
round time : 6.298 s, should be 4.9 s (diff = 1.398 ( 22.1975 %) for 49 )
round time : 6.298 s, should be 5 s (diff = 1.298 ( 20.6097 %) for 50 )
round time : 6.298 s, should be 5.1 s (diff = 1.198 ( 19.0219 %) for 51 )
round time : 6.297 s, should be 5.2 s (diff = 1.097 ( 17.421 %) for 52 )
round time : 6.298 s, should be 5.3 s (diff = 0.998 ( 15.8463 %) for 53 )
round time : 6.298 s, should be 5.4 s (diff = 0.898 ( 14.2585 %) for 54 )
round time : 6.298 s, should be 5.5 s (diff = 0.798 ( 12.6707 %) for 55 )
round time : 6.298 s, should be 5.6 s (diff = 0.698 ( 11.0829 %) for 56 )
round time : 6.298 s, should be 5.7 s (diff = 0.598 ( 9.49508 %) for 57 )
round time : 6.297 s, should be 5.8 s (diff = 0.497 ( 7.89265 %) for 58 )
round time : 6.298 s, should be 5.9 s (diff = 0.398 ( 6.31947 %) for 59 )
round time : 6.298 s, should be 6 s (diff = 0.298 ( 4.73166 %) for 60 )
round time : 6.298 s, should be 6.1 s (diff = 0.198 ( 3.14386 %) for 61 )
round time : 6.298 s, should be 6.2 s (diff = 0.098 ( 1.55605 %) for 62 )
round time : 7.545 s, should be 6.3 s (diff = 1.245 ( 16.501 %) for 63 )
round time : 7.872 s, should be 6.4 s (diff = 1.472 ( 18.6992 %) for 64 )



Unfortunately for my app i need an interval of 33ms, which is obviously one of the worse result : 30% of difference. It means that for an animation of 10s, my app will display an animation which lasts 13s.

Is it a known issue, and what are the possible alternatives ?

JohannesMunk
4th April 2011, 12:11
Hi Totem!

I had a long workout with timers a while ago.. I measured them with a performanceCounter and found that their reported frequency could be way off. But thats not the matter here now.

I guess you found another weakness in how the windows kernel schedules its timers. But if you know about it, this one can easily be resolved:



#include <QtGui>

class View : public QGraphicsView
{
Q_OBJECT
int nbFrames;
QGraphicsScene *scene ;
QGraphicsRectItem *item ;
qreal angle, step, radius ;
QTime t0, t1 ;
int interval;
QTimer* timer;

public :
View(void)
: scene(new QGraphicsScene),
nbFrames(100),
angle(0), // start angle=0°
step(360.0/nbFrames), // 1 round == 852 frames
radius(150.0), // circle radius
interval(1)
{
scene->setBackgroundBrush(Qt::darkBlue) ;
setScene(scene) ;
// draw circle path
new QGraphicsEllipseItem(QRectF(-radius,-radius,2*radius,2*radius),0,scene) ;
// draw shape
item = new QGraphicsRectItem(QRectF(-25,-15,50,30),0,scene) ;
item->setBrush(Qt::darkYellow) ;
item->setPen(QPen(Qt::black)) ;
// draw start point
QGraphicsEllipseItem *start = new QGraphicsEllipseItem(QRectF(radius-2,-2,4,4),0,scene) ;
start->setBrush(Qt::red) ;
start->setPen(QPen(Qt::black)) ;
// start animation
t0 = QTime::currentTime() ;
qDebug() << "start at" << t0.toString("hh:mm:ss.zzz") << ", interval =" << interval ;
timer = new QTimer();
connect(timer,SIGNAL(timeout()),this,SLOT(doTimer( )));
timer->start(1);
}

protected slots:
void doTimer()
{
static int timerCounter = 0;
++timerCounter;
if(angle>=360)
{
// compute time and restart
t1 = QTime::currentTime() ;
qreal theoric = 0.001*interval*nbFrames ;
qreal measured = 0.001*t0.msecsTo(t1) ;
qreal diff = (measured-theoric) ;
qreal delta = 100*diff/measured ;
qDebug() << "round time :" << measured << "s, should be" << theoric << "s (diff ="<<diff<<"("<<delta<<"%) for"<<interval<<")" ;
//killTimer(timerid) ;
interval++ ; // decrease framerate
angle = 0 ;
//the timer doesn't take into account the time you spend here, why do you?
t0 = t1;
// no restarting..
//timer->start(interval);
}
else
{
if (timerCounter % interval == 0)
{
// compute new position
qreal radians = M_PI*angle/180.0 ;
qreal x = radius*cos(radians) ;
qreal y = radius*sin(radians) ;
item->setPos(x,y) ;
// step ahead
angle += step ;
}
}
}
} ;




start at "11:55:14.630" , interval = 1
round time : 0.132 s, should be 0.1 s (diff = 0.032 ( 24.2424 %) for 1 )
round time : 0.2 s, should be 0.2 s (diff = -6.93889e-18 ( -3.46945e-15 %) for 2 )
round time : 0.3 s, should be 0.3 s (diff = 1.73472e-17 ( 5.78241e-15 %) for 3 )
round time : 0.4 s, should be 0.4 s (diff = -1.38778e-17 ( -3.46945e-15 %) for 4 )
round time : 0.5 s, should be 0.5 s (diff = 1.04083e-17 ( 2.08167e-15 %) for 5 )
round time : 0.603 s, should be 0.6 s (diff = 0.003 ( 0.497512 %) for 6 )
round time : 0.7 s, should be 0.7 s (diff = -5.20417e-17 ( -7.43453e-15 %) for 7 )
round time : 0.8 s, should be 0.8 s (diff = -2.77556e-17 ( -3.46945e-15 %) for 8 )
round time : 0.9 s, should be 0.9 s (diff = -3.46945e-18 ( -3.85494e-16 %) for 9 )
round time : 1 s, should be 1 s (diff = 2.08167e-17 ( 2.08167e-15 %) for 10 )
round time : 1.1 s, should be 1.1 s (diff = -6.59195e-17 ( -5.99268e-15 %) for 11 )
round time : 1.2 s, should be 1.2 s (diff = 6.93889e-17 ( 5.78241e-15 %) for 12 )
round time : 1.302 s, should be 1.3 s (diff = 0.002 ( 0.15361 %) for 13 )
round time : 1.4 s, should be 1.4 s (diff = -1.04083e-16 ( -7.43453e-15 %) for 14 )
round time : 1.5 s, should be 1.5 s (diff = 3.1225e-17 ( 2.08167e-15 %) for 15 )
round time : 1.6 s, should be 1.6 s (diff = -5.55112e-17 ( -3.46945e-15 %) for 16 )
round time : 1.7 s, should be 1.7 s (diff = 7.97973e-17 ( 4.69396e-15 %) for 17 )
round time : 1.8 s, should be 1.8 s (diff = -6.93889e-18 ( -3.85494e-16 %) for 18 )
round time : 1.9 s, should be 1.9 s (diff = -9.36751e-17 ( -4.93027e-15 %) for 19 )
round time : 2 s, should be 2 s (diff = 4.16334e-17 ( 2.08167e-15 %) for 20 )
round time : 2.1 s, should be 2.1 s (diff = -4.51028e-17 ( -2.14775e-15 %) for 21 )
round time : 2.2 s, should be 2.2 s (diff = -1.31839e-16 ( -5.99268e-15 %) for 22 )
round time : 2.301 s, should be 2.3 s (diff = 0.001 ( 0.0434594 %) for 23 )
round time : 2.4 s, should be 2.4 s (diff = 1.38778e-16 ( 5.78241e-15 %) for 24 )
round time : 2.5 s, should be 2.5 s (diff = 5.20417e-17 ( 2.08167e-15 %) for 25 )
round time : 2.6 s, should be 2.6 s (diff = -3.46945e-17 ( -1.3344e-15 %) for 26 )
round time : 2.7 s, should be 2.7 s (diff = -1.21431e-16 ( -4.49743e-15 %) for 27 )
round time : 2.8 s, should be 2.8 s (diff = -2.08167e-16 ( -7.43453e-15 %) for 28 )
round time : 2.901 s, should be 2.9 s (diff = 0.001 ( 0.0344709 %) for 29 )
round time : 3 s, should be 3 s (diff = 6.245e-17 ( 2.08167e-15 %) for 30 )
round time : 3.1 s, should be 3.1 s (diff = -2.42861e-17 ( -7.83424e-16 %) for 31 )
round time : 3.2 s, should be 3.2 s (diff = -1.11022e-16 ( -3.46945e-15 %) for 32 )
round time : 3.3 s, should be 3.3 s (diff = -1.97758e-16 ( -5.99268e-15 %) for 33 )
round time : 3.4 s, should be 3.4 s (diff = 1.59595e-16 ( 4.69396e-15 %) for 34 )
round time : 3.501 s, should be 3.5 s (diff = 0.001 ( 0.0285633 %) for 35 )
round time : 3.6 s, should be 3.6 s (diff = -1.38778e-17 ( -3.85494e-16 %) for 36 )
round time : 3.7 s, should be 3.7 s (diff = -1.00614e-16 ( -2.7193e-15 %) for 37 )
round time : 3.8 s, should be 3.8 s (diff = -1.8735e-16 ( -4.93027e-15 %) for 38 )
round time : 3.9 s, should be 3.9 s (diff = 1.70003e-16 ( 4.35905e-15 %) for 39 )
round time : 4.001 s, should be 4 s (diff = 0.001 ( 0.0249938 %) for 40 )
round time : 4.1 s, should be 4.1 s (diff = 4.4062e-16 ( 1.07468e-14 %) for 41 )
round time : 4.2 s, should be 4.2 s (diff = -9.02056e-17 ( -2.14775e-15 %) for 42 )
round time : 4.3 s, should be 4.3 s (diff = 2.67147e-16 ( 6.21273e-15 %) for 43 )
round time : 4.401 s, should be 4.4 s (diff = 0.001 ( 0.0227221 %) for 44 )
round time : 4.5 s, should be 4.5 s (diff = 9.36751e-17 ( 2.08167e-15 %) for 45 )
round time : 4.6 s, should be 4.6 s (diff = -4.3715e-16 ( -9.50327e-15 %) for 46 )
round time : 4.7 s, should be 4.7 s (diff = -7.97973e-17 ( -1.69781e-15 %) for 47 )
round time : 4.801 s, should be 4.8 s (diff = 0.001 ( 0.020829 %) for 48 )
round time : 4.9 s, should be 4.9 s (diff = -2.5327e-16 ( -5.16877e-15 %) for 49 )
round time : 5 s, should be 5 s (diff = 1.04083e-16 ( 2.08167e-15 %) for 50 )
round time : 5.101 s, should be 5.1 s (diff = 0.001 ( 0.019604 %) for 51 )
round time : 5.2 s, should be 5.2 s (diff = -6.93889e-17 ( -1.3344e-15 %) for 52 )
round time : 5.3 s, should be 5.3 s (diff = 2.87964e-16 ( 5.43328e-15 %) for 53 )
round time : 5.401 s, should be 5.4 s (diff = 0.001 ( 0.0185151 %) for 54 )
round time : 5.5 s, should be 5.5 s (diff = 1.14492e-16 ( 2.08167e-15 %) for 55 )
round time : 5.6 s, should be 5.6 s (diff = -4.16334e-16 ( -7.43453e-15 %) for 56 )
round time : 5.7 s, should be 5.7 s (diff = -5.89806e-17 ( -1.03475e-15 %) for 57 )


QTimer seems to adjust windows timer resolution if required. That defaults to 10ms but is increased by applications that require more.. like winamp .. One has to set that manually in order for QThread::sleep() to work at 1ms precision:



#ifdef Q_OS_WIN
#include "windows.h"
DWORD AdjustPrivileges()
{
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
// get the current process token handle
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
return GetLastError();
}
// get the LUID for system-time privilege.
LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1; // one privilege to set
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// get set-time privilege for this process.
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,(PTOKEN_PRIVILEGES) NULL, 0);
// cannot test return value of AdjustTokenPrivileges.
return GetLastError();
}
#endif
..
AdjustPrivileges();
if (timeBeginPeriod(1) == TIMERR_NOCANDO)
qWarning() << "Could not start the time period!";
...
timeEndPeriod(1);


HIH

Johannes

totem
4th April 2011, 13:55
:) thank you very much, i'll try your solution as soon as possible

high_flyer
5th April 2011, 12:14
You might want to have a try at this:
http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Time/NtSetTimerResolution.html

Added after 6 minutes:

@Johannes:
Just out of curiosity, your code is setting privileges for setting the system time.
MSDN for SE_SYSTEMTIME_NAME:

Required to modify the system time.

User Right: Change the system time.
How does this change the timer resolution for the privileged process?

JohannesMunk
5th April 2011, 12:32
Hi High_flyer!

You are absolutely right! The setting for the high resolution timer is done only by the timeBeginPeriod call.

And the privilege adjustment is not required for it! I didn't realize! It was only in context of an NTP-Clock sync, that this made sense.

But as I mentioned none of it is required to have a 1 ms QTimer work correctly. Only if you want QThread::sleep to work accurately you will need to set the timer resolution yourself.
I just mentioned it, because I had trouble with it in context of high precision timing.

Joh

high_flyer
5th April 2011, 13:02
But as I mentioned none of it is required to have a 1 ms QTimer work correctly. Only if you want QThread::sleep to work accurately you will need to set the timer resolution yourself.
So just to see if I understand what you are saying is that for having QThread::sleep() work accurately, one must set the windows timer resolution (per process) via timeBeginPeriod() + timeEndPeriod(), is that correct?

JohannesMunk
5th April 2011, 13:49
Yes, this is correct for Windows. I experienced that on WinXPSP3,Win7-32 and 64bit. The timer resolution equals the kernels schedulers granularity so unless you set it to 1ms you can't get accurate sleep intervals.

To prove it, I hacked together a small test application:

It runs msleep(1) 100x times. As the default timer resolution of windows is 10ms, you get about 1sec for 100 calls to msleep(1):



999
998
BeginPeriod!
110
94
110
EndPeriod!
998
1014
Before you run it, make sure all other processes that might request a higher timer resolution are closed. This includes winamp, your browser (flash plugin!), ..
You can then check the current timer resolution with sysinternals clockres (http://technet.microsoft.com/en-us/sysinternals/bb897568).

Here is the sourcecode:



main.h
#ifndef MAIN_H
#define MAIN_H

#include <QtGui>

#ifdef Q_OS_WIN
#include "windows.h"
#endif

class SleeperThread : public QThread
{ Q_OBJECT
public:
SleeperThread(int interval,QObject* parent = 0) : QThread(parent), m_interval(interval) {}
protected:
virtual void run()
{
QElapsedTimer timer;
timer.start();
for (int i=0;i<100;++i)
{
msleep(m_interval);
}
QMetaObject::invokeMethod(parent(),"showResult",Q_ARG(int, timer.elapsed()));
}
private:
int m_interval;
};

class MainWindow : public QMainWindow
{ Q_OBJECT
public:
MainWindow()
{
QPushButton* start_pb = new QPushButton("Test Sleep");
connect(start_pb,SIGNAL(clicked()),this,SLOT(testS leep()));

QPushButton* beginPeriod_pb = new QPushButton("Begin Period");
connect(beginPeriod_pb,SIGNAL(clicked()),this,SLOT (beginPeriod()));
QPushButton* endPeriod_pb = new QPushButton("End Period");
connect(endPeriod_pb,SIGNAL(clicked()),this,SLOT(e ndPeriod()));

QHBoxLayout* hl = new QHBoxLayout();
hl->addWidget(beginPeriod_pb);
hl->addWidget(start_pb);
hl->addWidget(endPeriod_pb);

output_te = new QPlainTextEdit();
output_te->setReadOnly(true);

QVBoxLayout* vl = new QVBoxLayout();
vl->addLayout(hl);
vl->addWidget(output_te);


QWidget* w = new QWidget();
w->setLayout(vl);

setCentralWidget(w);
}
public slots:
void testSleep()
{
SleeperThread* st = new SleeperThread(1,this);
connect(st,SIGNAL(result(int)),this,SLOT(showResul t(int)));
connect(st,SIGNAL(finished()),st,SLOT(deleteLater( )));
st->start();
}

void beginPeriod()
{
output_te->appendPlainText("BeginPeriod!");
#ifdef Q_OS_WIN
if (timeBeginPeriod(1) == TIMERR_NOCANDO)
qWarning() << "Could not start the time period!";
#endif
}

void endPeriod()
{
output_te->appendPlainText("EndPeriod!");
#ifdef Q_OS_WIN
timeEndPeriod(1);
#endif
}

void showResult(int elapsed)
{
output_te->appendPlainText(QString::number(elapsed));
}
private:
QPlainTextEdit* output_te;
};

#endif // MAIN_H

main.cpp:

#include <QApplication>
#include <QtGui>

#include "main.h"

int main(int argc, char** argv)
{
QApplication app(argc,argv) ;

MainWindow mw;
mw.show();

return app.exec();
}
Johannes

high_flyer
5th April 2011, 14:14
for the sake if completeness, here is a good link I just found on the subject:
http://msdn.microsoft.com/en-us/magazine/cc163996.aspx

JohannesMunk
5th April 2011, 14:34
Actually the article is not about a timer that can periodically schedule or delay something, but about using the Performance Counter API to supplement the precision of the system time and thus interval measurement. They explain the lack of resolution by the lack of resolution of the system timer, though.

Joh

Added after 9 minutes:

To get back on the original topic, I just tested, that when you start a QTimer with an arbitrary number above 20ms it will not adapt the timer resolution. So that a timer of 33ms will fire either after 30ms or 40ms, because the system timer resolution is at 10ms.

That problem can be resolved as i suggested by using a 1ms timer and counting its timeouts or probably (I didn't check this) by using a 33ms timer and manually setting the windows timer resolution to 1ms with timeBeginPeriod!

Joh

high_flyer
5th April 2011, 14:34
They explain the lack of resolution by the lack of resolution of the system timer, though.
Yes, but also how to increase the timer resolution in the limits of a given system, which I think is what the OP was generally after.

JohannesMunk
5th April 2011, 14:45
When I look at the similar threads below this seems to be a rather common problem.

http://www.qtcentre.org/threads/23167-How-can-I-get-a-30-or-60-frame-rate-when-using-QGLWidget-QTimer-is-not-acurate

Maybe I should create a WIKI-Article about precision timing under windows sometime. I have spent quite a lot of time with performance counters and their unreliable frequency statements and with thread-switching times, due to a real time data acquisition suite i'm currently developing.

Joh

high_flyer
5th April 2011, 15:52
Maybe I should create a WIKI-Article about precision timing under windows sometime. I have spent quite a lot of time with performance counters and their unreliable frequency statements and with thread-switching times, due to a real time data acquisition suite i'm currently developing.

I am not going to stop you! :)

totem
5th April 2011, 17:13
The setting for the high resolution timer is done only by the timeBeginPeriod call.

And the privilege adjustment is not required for it! I didn't realize! It was only in context of an NTP-Clock sync, that this made sense.

But as I mentioned none of it is required to have a 1 ms QTimer work correctly. Only if you want QThread::sleep to work accurately you will need to set the timer resolution yourself.
I just mentioned it, because I had trouble with it in context of high precision timing.
Joh

So If I don't use QThread::sleep I don't need to include windows-specific code ?
I'd be glad because it makes me link against some MS lib (i got linker error with timeBeginPeriod call I did not even tried to resolve)

By the way I tried your solution and it seems to work for me, thanks again.
As I said I needed a frame interval of 33ms so I used a timer interval of 11ms, which implies an update every 3 timer events. Now animation duration seems consistent

JohannesMunk
5th April 2011, 17:26
Hi Totem!

Yes, below 20ms QTimer-interval, windows timer resolution is set to 1ms, without you doing anything.
Thats why your 11ms should be quite accurate.

Glad that your problem is resolved,

Happy coding,

Johannes