PDA

View Full Version : Fast Timer Updates vs. Monitor Refresh Rates



artoonie
5th December 2010, 08:09
I need to create a rapidly refreshing widget, updating at around 60Hz.

I have a prototype, and it seems to lag (skip a frame) every 5-20 frames.

I would guess that one of two things is happening:
- The monitor refresh rate of 60Hz is not synchronized with the applications refresh, or
- The widget does not actually update every 6ms


The application is meant to flicker between two gradients rapidly. I have made the paintEvent as simple as possible:


void Flickerer::paintEvent(QPaintEvent*)
{
p->begin(this);
if(showingG1)
p->drawPixmap(*rect, *buffer1);
else
p->drawPixmap(*rect, *buffer2);
p->end();
}

A link to the prototype (http://www.priceypixel.com/personal/gardenpath/gardenpath.zip), if it will help (use preset 2 @ 30-60Hz).


How can I make the lag go away? Is there a way to synchronize to the screen's refresh? Or is there a method faster than drawPixmap()? Perhaps having two widgets, then just showing/hiding one of them on every update?

Thanks!

tbscope
5th December 2010, 08:50
Maybe you have already read this blog post?
http://labs.qt.nokia.com/2010/12/02/velvet-and-the-qml-scene-graph/

It is about QML but you can take the general guidelines for regular qt widgets too.
I think the idea is to use some of the OpenGL functions to synchronize with the refresh rate of the screen.

artoonie
5th December 2010, 09:04
I hadn't actually, thank you!

I had begun this using OpenGL but figured I needed double buffering, which requires GLUT, which conflicts with Qt (or so I read), so I had to switch to Qt-native functions to get automatic double buffering.

Looks like I'll be going back to OpenGL and testing out swapBuffers.



Thanks! I'll update this thread on how it goes (for legacy purposes).

-Armin

artoonie
5th December 2010, 20:34
Hi,
I switched over to a GLWidget, but OpenGl gives the same results. So I tested out the function noted in the link you posted:

QGLFormat fmt;
fmt.setSwapInterval(60); // Should update once per second (but updates faster)
this->setFormat(fmt);

But the setSwapInterval seems to have no effect. I tried setting the timerInterval to 0 and that didn't work either.


How do I sync the timer to update with the swapInterval?

wysota
5th December 2010, 23:13
It may sound as a dumb question but why do you want to update the widget every 6ms? This gives about 167 frames per second, I can hardly think of a reason why anyone would want so many ui updates per second. Especially with a 60Hz display. I can't even think of a hardware that would be capable of doing as many updates and still be able to provide some data to update the ui with.

artoonie
5th December 2010, 23:49
It may sound as a dumb question but why do you want to update the widget every 6ms? This gives about 167 frames per second, I can hardly think of a reason why anyone would want so many ui updates per second. Especially with a 60Hz display. I can't even think of a hardware that would be capable of doing as many updates and still be able to provide some data to update the ui with.
Haha, silly typo, my bad-
I meant 16ms, or more precisely, 16.666...ms.
I need 60Hz, meaning refresh every time the screen refreshes.

wysota
6th December 2010, 07:53
Ok, so how exactly do you force your widget to redraw?

artoonie
6th December 2010, 20:11
I'm using a QTimer...is there another way, maybe blocking until a vertical sync event? I've tried using swapBuffers but I can't get it to work without a timer.

wysota
7th December 2010, 00:54
I'm using a QTimer...
Show us the code.

ChrisW67
7th December 2010, 02:43
I would guess that one of two things is happening:
- The monitor refresh rate of 60Hz is not synchronized with the applications refresh, or
- The widget does not actually update every 6ms

Both of these things are happening.

QTimer can be set in whole milliseconds only. Approximating a 1/60 seconds rate with 16 msecs will lead a whole frame difference in time after 24 or 25 frames even if the timer events were perfectly spaced. A 17 msec approximation will extend the sync out to around 48-50 frames. QTimer events are processed when the program returns to the event loop after the time has expired. Depending on what else the program is doing, like rendering your gradient, this might be a significant delay. The combination of the two will lead to a mismatch that is variable with activity.

MythTV can use timers to maintain video refresh sync, but I believe it also has extensive frame counting over relatively long periods to determine the average frame rate and drops/duplicates frames to realign things periodically. The preferred method in MythTV is to use the OpenGL vsync. I don't know how this can be accessed.

artoonie
7th December 2010, 04:10
I'm using a QTimer...QUOTE]
Show us the code.

It's been established that timers are not the way to go, but I'll paste what I have:


Flickerer::Flickerer(int timerInterval, QWidget *parent) {
m_timer = new QTimer( this );
connect( m_timer, SIGNAL(timeout()), this, SLOT(timeOutSlot()) );
m_timer->start( timerInterval );
}
void Flickerer::timeOutSlot()
{
showingG1 = !showingG1;
updateGL();
}
void Flickerer::paintGL()
{
if(showingG1) /* paints gradient 1 */ else /* paints gradient 2 */
}






QTimer can be set in whole milliseconds only. Approximating a 1/60 seconds rate with 16 msecs will lead a whole frame difference in time after 24 or 25 frames even if the timer events were perfectly spaced. A 17 msec approximation will extend the sync out to around 48-50 frames. QTimer events are processed when the program returns to the event loop after the time has expired. Depending on what else the program is doing, like rendering your gradient, this might be a significant delay. The combination of the two will lead to a mismatch that is variable with activity.

MythTV can use timers to maintain video refresh sync, but I believe it also has extensive frame counting over relatively long periods to determine the average frame rate and drops/duplicates frames to realign things periodically. The preferred method in MythTV is to use the OpenGL vsync. I don't know how this can be accessed.
I agree- I need to use OpenGL vsync but I don't know how to get a signal from the display so I'd know when to update.



Thanks you.

tbscope
7th December 2010, 04:25
You do not need to get the signal from the display. OpenGl has a function that does that for you.

What I would try to do:
Create a graphicsview that contains the user interface. Make it so that everything inside the graphicsview is painted via OpenGl
There are lots of tutorials in the Qt labs and documentation.
And then use the information from the blog post about blocking the eventloop and synchronising the eventloop to the refresh rate of the monitor.

If you use a graphicsview and opengl, the effects possible with your user interface are infinite, although limited to the contents of the graphicsview.

Edit:
Here's a nice example:
http://labs.qt.nokia.com/2008/06/27/accelerate-your-widgets-with-opengl/

Edit 2:
You need this function:
http://doc.qt.nokia.com/latest/qglformat.html#setSwapInterval
Set it to 1

Tip:
QML does this out of the box. It's worth studying the code.

artoonie
9th December 2010, 06:39
[Deleted...]

Added after 59 minutes:

I've narrowed my problems down to here:


QGLFormat fmt;
fmt.setSwapInterval(1);
QGLWidget* w = new QGLWidget();
w->setFormat(fmt);

qDebug("Swap interval: %d", fmt.swapInterval()); // Prints 1
qDebug("Swap interval: %d", w->format().swapInterval()); // Prints -1
Why is the swap interval not being accepted by the widget? Or am I doing something else wrong here?

--Update--
Okay, this is odd. The docs have a format() (http://doc.qt.nokia.com/stable/qglwidget.html#format) method, but the setFormat() method (which is linked from the format() description) does not exist, even in the list of all members (http://doc.qt.nokia.com/stable/qglwidget-members.html).

wysota
9th December 2010, 09:48
And this code was compiling? There is a constructor to QGLWidget that takes a format parameter.

artoonie
9th December 2010, 10:11
Both ways are equivalent. I get the same behavior with

QGLFormat fmt;
fmt.setSwapInterval(1);
QGLWidget* w = new QGLWidget(fmt);


Also, quote from here (http://doc.qt.nokia.com/latest/qglformat.html#swapInterval):

Returns the currently set swap interval. -1 is returned if setting the swap interval isn't supported in the system GL implementation.
Possibly it's just my system?

wysota
9th December 2010, 11:29
Possibly it's just my system?
Hard to believe that unless you have some really broken OpenGL implementation.

Check for this:

Under Windows the WGL_EXT_swap_control extension has to be present, and under X11 the GLX_SGI_video_sync extension has to be present.

artoonie
9th December 2010, 18:17
...looks like that's my problem? My hardware supports OpenGL 1.1 and 10% of OpenGL 1.2. WGL_SWAP_LAYER_BUFFERS_ARB is set to false and WGL_EXT_SWAP_CONTROL is grayed out on the OpenGL Extensions Viewer.

Guess I'll be testing it elsewhere and let you know.

artoonie
10th December 2010, 03:07
So that turned out to be the problem- perhaps vertex and fragment shaders are also needed?
I'm using a different computer and I get a message saying the shaders are linked.

HOWEVER, it still isn't updating without a QTimer...I do remove tearing with fmt->setSwapInterval(1) (whereas tearing is horrid with that line commented out). Is this the expected behavior?

wysota
10th December 2010, 07:49
HOWEVER, it still isn't updating without a QTimer...
Why would it? Read what the swap interval does. It doesn't cause any periodic updates.

artoonie
10th December 2010, 09:19
Right, right, I naively assumed some magic might happen without a timer.

Thank you so much for your help, I really appreciate it.


-Armin