PDA

View Full Version : QTimer ->start(0) + OpenGL + resize/move window => crash



anthibug
3rd July 2008, 10:51
Hi,

I've got an issue with OpenGL.
I've got a QMainWindow (loaded from a .ui), that include a QGLWidget, well in fact an instance of a personnalize class that inherits from QGLWidget, ...

Actually I'm migrating an SDL+OpenGL application into a QT 4.4 + OpenGL app ;)
It's kind of a 3D cloud points viewer.:cool:

Ok by now everything works, I see the scene, I can interact with it (keyboard, mouse), but it's not perfect.

In the first time my idea was to do like in SDL : an infinit while(true) loop.
I managed to do that with this code in constructor :

connect(&qTimerRedraw, SIGNAL(timeout()), this, SLOT(dessiner()));
qTimerRedraw.start(0); // CPU = 100%

As the comment explains, it's pretty obvious that one core is going to be at 100% because of the QTimer interval set to 0. And I really don't care if so, because I know how to slow down the framerate in order to reduce the CPU usage. (I could also see the FPS in live).
It's great, very fluid with key events (stay pressed, release, ...).

The problem is that : my application is sometimes crashing when I move the window or when I resize it. Precisely, the crash occurs when i release the mouse button. Why does this crash appears errraticaly ?
I tried to QDebug ResizeGL() without success.

After I realize the bug was due to the infinite signal<->slot loop, I had a second idea : pause the timer during the "work time" :

void gldrawer::paintGL()
{
qTimerRedraw.stop(0); // wait before the end

// paint job ... (heavy work)

qTimerRedraw.start(0); // restart
}

This might be a good idea with a long "work time" but it doesn't resolve my issue.

My third idea was a different approch : during my research I discover that lots of example works in another way to handle user interaction. The scene is refreshed only when a user interaction leads to a repaint (keyboard or mouse event typically).
That is a very good idea because you do not need to do an infinite loop (100%CPU) so it was solving my problem in 2 ways :
-no loop => no crash when moving or resizing:)
-my application does not have animations, it's only when the user interact that I have to redraw:)

So I made modifications to my code, but now there is another problem : keys events.
-I can't use modifiers like SHIFT, ALT, ...
->I can't check the modifiers during a keyPressEvent call
->It stops the other key :
i see keyReleaseEvent calls even if i stay pressed, what the ??!
-When you constantly press a key, the reaction is like in a text application : small delay before the event is fire continously

In the two solutions, the application is fluid when the user keeps the key down, so that's cool. But in both cases, I've got glitches...
What is the best thing to do ? How can I fix my problems ?

Do I need to post some code ?

Thanks!

spud
3rd July 2008, 15:33
Your third approach seems to be correct since you aren't using animations.
Use QKeyEvent::isAutoRepeat () to find out if your handling an 'real' key press.
For fluid motion when a key is pressed i suggest you start a timer when a key is pressed and kill it when all keys have been released. You could use a QSet to keep track of pressed keys.


//member
QSet<int> pressedKeys;

void keyPressEvent(QKeyEvent* e)
{
if(e->isAutoRepeat())
return;
if(pressedKeys.isEmpty())
startTimer(40);
pressedKeys.insert(e->key());
}

void timerEvent(QTimerEvent* e)
{
if(pressedKeys.contains(Qt::Key_Up))
...;
if(pressedKeys.contains(Qt::Key_Left))
...;
updateGL();
}

void keyReleaseEvent(QKeyEvent* e)
{
pressedKeys.remove(e->key());
if(pressedKeys.isEmpty())
startTimer(40);
}
This way you should be able to catch any combination of keys being pressed.

anthibug
3rd July 2008, 16:34
I'm going to kill me. This crash was due to a simple division by 0 in my FPS counter. ahah :p
I discover it after compiling debug libraries and try a debug...
At least I discover some QT functionnalities...


last_time = current_time;
current_time = QTime::currentTime();
elapsed_time = (current_time.second()*1000 + current_time.msec()) - (last_time.second()*1000 + last_time.msec());
this->fps = 1000 / elapsed_time;


I don't exactly know why elapsed_time could be set to 0, at least I know it was (only) happening on some resize events.
I'm guessing it's due to the functions call order ; my guest is that :
-when a resize event happens it stops the current function execution
-the rest of the code is not executed until and of resize
-... i don't know

i just did a test and of course, during a resize, my infinite loop function is not called

whatever, this solved my problem :


if(elapsed_time <= 0)
this->fps = 0;
else
this->fps = 1000 / elapsed_time;


so simple....

I'm gonna check on your tips in the following days spud, thanks for helping !
If it works I will improve a LOT my application.

A little preview right there:
http://img296.imageshack.us/img296/4826/screenshot5eh6.th.jpg (http://img296.imageshack.us/my.php?image=screenshot5eh6.jpg)

jacek
3rd July 2008, 22:21
elapsed_time = (current_time.second()*1000 + current_time.msec()) - (last_time.second()*1000 + last_time.msec());
Take a look at QTime::msecsTo().

anthibug
7th July 2008, 15:21
Thanks jacek, I missed it !:o

Thanks spud you got the idea : a timer to refresh the scene if their is still a pressed key....
I managed to do this with minor modifications :


void gldrawer::keyPressEvent(QKeyEvent * event)
{
if(event->isAutoRepeat())
return;
if(_activeKeystates == 0)
qTimerRedraw.start(0);
for(KeyStates::iterator it = _keystates.begin();it != _keystates.end();
it++)
{
if (event->key() == it->first)
{
it->second = true;
_activeKeystates++;

break;
}
}
}

void gldrawer::keyReleaseEvent(QKeyEvent * event)
{
for (KeyStates::iterator it = _keystates.begin();it != _keystates.end();
it++)
{
if (event->key() == it->first && !event->isAutoRepeat())
{
it->second = false;
_activeKeystates--;
break;
}
}
if(_activeKeystates == 0)
qTimerRedraw.stop();
}

where _keystates is a std::map<Qt::Key, bool> (yes I know I could use QMap, I will ...)

Right now CPU is at 0%.
If I push a key (and stay pushed) the scene is continously refreshed : one core is at 100%.
I can push keys with modifiers :-)

Thanks !


----


I'm currently having problems dealing with QSlider :
I have to repaint the scene during a call to my slot corresponding to the "valueChanged" signal of the QSlider. The problem is that it's make the slider freeze/lag.
In can understand that because each time I move the slider it redraws the scene, and that takes time.
The scene is updated in real time according to the position of the slider, but the position of the slider itself is lagging (juste visually)...
Could be a user issue in the future.
I tried the "sliderReleased()" but the problem is that the scene is not updated in real time.
:(

anthibug
8th July 2008, 11:01
I managed to do that the same way I did with the keys :
-if the QSlider is moved then repaint (qTimerRedraw.start(0);)
-when released stop to repaint

... so easy ;)