PDA

View Full Version : QT + OpenGL + Thread => aaaahhhhh !



anthibug
17th July 2008, 13:01
Hi, I've got a problem with threads and OpenGL in QT.
Could you help me please ?

I'm currently using QT 4.4 with C++.
In my program, I've got a class which is subclassing the GL widget : " class gldrawer : public QGLWidget ".
Everything works well : i'm drawing a cloud of points, the maximum number i tried with my program was 31 000 000 :cool:
See attached file interface.jpg to get an idea...

I'm a student with not enough experience in the QT domain but I managed to implement a free fly camera, into my class gldrawer.
That is probably not the best way to do it but it was easier for me to do it that way to begin with Qt + opengl.

The surface is updated ONLY when a user interact, for example when the user push the forward button or change colors... => CPU is at 0% most of the time.
To have a fluid animation during moving phases (user keeps pushing forward for example), I managed to draw the scene indefinitely => CPU at 100% on one core.
I did that with a timer and a 0ms delay : " qTimerRedraw.start(0); ". The main reason was in fact my lack of knowledge with QThread at the time (I was just beginning with Qt!).

The job is done, user can interact with opengl & GUI buttons in the same time, the event is just add to the event list so its sometimes a little lagging, but at least it's no crashing.
(I've got more lags with QSliders when the opengl scene is huge and long to render...). I know this is not perfect, but it kinda works.


Now I'm trying to launch an automated animation : you click a button and the camera have to follow a list of points, indefinitely. You reclick and it stops the camera.
My idea was to create a thread to run to the points and replace the camera at each step ; this way the loop is not in the same thread than the GUI => no freeze, can reclick to stop the camera.

As you know, QT + OpenGL + Threads = complex situation !
Moreover the Qt documentation isn't enough clear about that, just a word about makeCurrent() but no example... I don't know how to separate my classes...


Whatever I did some tests and this is the best I've come to :
-I launch a thread when I click on the button, the thread is corerctly terminated when I reclick
-Inside this thread, the loop is correctly executed, calculations for the camera are OK :
I've got a pointer to my gldrawer object and i call a public method of it with the new camera coordinates : this->mygl->setPosition(current, cible); where this->mygl is a gldrawer * obtain in the constructor.

As you notice, setPosition can be called from 2 different thread at the same time.... And guess what, setPosition is calling this->updateGL()...


The code compile and run... But nothing happend when I click the button to launch the 'camera' thread. Well in fact the code is executed (new thread, loop, setposition, ...) , values are updated, ... but the screen is not refreshed :confused:

Why ? :crying:

I tried to lock the GL Context with makeCurrent(); and a mutex, like in the following code, but without success.

//-------------
I've found a multithreaded opengl + qt example right there : http://apsy.gse.uni-magdeburg.de/main/index.psp?sec=1&page=hanke/threadedcube
It's a great example, it works well. Very interesting.
//-------------

Thanks for taking time.

wysota
17th July 2008, 13:48
You don't need threads nor a 0 timeout timer. Decide how many frames per second you want to draw and set a timer to that frequency (for instance 25fps would give you a 40ms timer period). Connect the timer to a slot that will update your scene (including processing input events) and call updateGL() when it's done. This way you'll have a fluent animation with limited CPU usage and a single thread.

anthibug
17th July 2008, 14:06
Yes i know that, and that's what i did with my "qTimerRedraw".
I chose a 0 ms delay because I wanted to have as much fps as i could when in animation mode. But I don't care because I know how to deal with this.

I said this might not be the best way because it's putting events in the queue and that's bad for the gui because my glwidget is in the same thread than my mainwindow.
=>You could tell me to increase the timer to 40ms ; but it doesn't matter : the draw instructions are way too long ! (remember : 30M points!)

My problem is to call a method of my gldrawer from another thread : the call is done (in which updateGL() is also called), but the screen is not updated :( wtf?

wysota
17th July 2008, 14:09
updateGL has to be called in the main thread. And of course you realize that by using threads you slow down your application, right?

anthibug
17th July 2008, 16:27
updateGL has to be called in the main thread.
Flashbang !
Ok, that is the thing !
So stupid it was not clear for me...

I absolutely know i'm slowing my application by using mutex.
But i really don't care here, it's just for launching a camera motion move and having the ability to play/pause that.

//------------------------------------

This morning I did a test : using a timer into gldrawer, with all the code into the gldrawer `thread`. It did works.
But... I can't stop the animation until it's finished.

void gldrawer::moveCamera()
{
int nb = number of key points (BIG)
for(int i=0; i < nb; i++)
{
go to the next point
draw()
}
}


connect(&qMoveRedraw, SIGNAL(timeout()), this, SLOT(moveCamera()));
qMoveRedraw.start(30);

i simplify the moveCamera() code but the idea is here... takes time !
as soon as i resize or even click in the window : crash, and that's normal...


//------------------------------------

ok back to the beginning of my message :
i just did some tests and i manage to get what i need :)

In fact i just tell my gldrawer to draw continously meanwhile the second thread do stuff !

I also did some tests with mutex & QWaitCondition but i think i will use sleep rather than mutex.

Thanks again for your help !

wysota
17th July 2008, 17:34
I absolutely know i'm slowing my application by using mutex.
No, by using threads... Contrary to what people think threads tend to slow down execution of code, not speed it up. Unless of course you have more processing units than threads in your application and no other cpu consuming task is running.



But i really don't care here, it's just for launching a camera motion move and having the ability to play/pause that.
What do you use threads for, anyway?


In fact i just tell my gldrawer to draw continously meanwhile the second thread do stuff !
Why should it draw all the time? It doesn't make sense as the movement (not redrawing) depends on the other tasks running on your machine. You should first run a benchmark to determine how fast you can draw and then adjust the timer so that you have a constant frequency of the "game loop" and redraw as often as you know is possible.

anthibug
17th July 2008, 18:10
No, by using threads... Contrary to what people think threads tend to slow down execution of code, not speed it up. Unless of course you have more processing units than threads in your application and no other cpu consuming task is running.
I completely agree, you misunderstand me !
Re-read what i said : i'm slowing my application
Not solving, slowing ! Sorry for my english...






But i really don't care here, it's just for launching a camera motion move and having the ability to play/pause that.
What do you use threads for, anyway?
Yep that was not really explicit, give me another chance :
-my gldrawer is in the same thread than the gui
-i want to launch a processing to take screenshots from A point to B point into the cloud points scene
-if i do that from the `main` thread, i'm freezeing my application : no buttons anymore, no resize, etc... or crash
I've to wait until the end of animation and cross finger that a crash doesn't happend cause of events.

=> so my idea was to create a second thread from which the translation of the camera is done in an infinite loop



Why should it draw all the time? It doesn't make sense as the movement (not redrawing) depends on the other tasks running on your machine. You should first run a benchmark to determine how fast you can draw and then adjust the timer so that you have a constant frequency of the "game loop" and redraw as often as you know is possible.

-it's not a game but i think you know that
-why draw all the time : because, as you said : "updateGL has to be called in the main thread"
=>with the second thread looping and moving the camera, the only way to refresh the screen was to draw from the first thread.
I'm also re-saying : i don't care to use a timer with a 0ms delay that take 100% of one core of my CPU.

-in the latest modifications, i'm using a QMutex and a QWaitCondition* in a way that the second thread wait that the first thread do all the painting [wakeAll() after updateGL()]

Of course the animation is not perfectly fluid, but i don't care because i just need to take the screenshot of the gl surface at every frame , i'm going to recompose a video from the captures. (I'm doing that because the scene is huuuuuuuuuuuge). [yes i know i didn't clearly explain all of that, but i aldready said a lot...]

So this way i'm minimazing the call in terms of 'redrawing'...


Sure it appears pretty messed up from your eye, and i'm far from beeing a pro at QT+OpenGL+Huge scenes... Even more far from beeing a good teller..
So as a novice, who knew nothing a few weeks ago, it was hard to figure it out and explain my problem(s) but I manage to resolve them.

I'm assuring you this thread animation thing is just one of the functionnalities ; it's not impacting the viewer in itself, with the free fly camera.



*about that i can't find QWaitCondition in the assistant, only found here : http://doc.trolltech.com/4.4/qwaitcondition.html

wysota
26th July 2008, 13:36
-i want to launch a processing to take screenshots from A point to B point into the cloud points scene
-if i do that from the `main` thread, i'm freezeing my application : no buttons anymore, no resize, etc... or crash
I've to wait until the end of animation and cross finger that a crash doesn't happend cause of events.
It won't freeze as long as you let the application process its events from time to time. Make a single shot at a time instead of trying to make all at once in one long loop.


-it's not a game but i think you know that
It's still called a game loop.


-why draw all the time : because, as you said : "updateGL has to be called in the main thread"
But why all the time? :) If nothing changes, what's the point of redrawing the screen? If the content is already stale, drop the frame and continue calculations, no need to redraw the display again.

You surely have some function that changes the position of the camera that you call periodically - that's the "game loop". To have a steady movement it is important that you call the loop in regular intervals, otherwise your animation will get messed up. So call the loop every 40ms or so to get 25 frames per second, recalculate the position there, call updateGL if you want to redraw the display and make a shot. After you do that, return the flow to the event loop and let the timer timeout again 40ms-<time you spent on calculations/redrawing/snapshotting> later. All that in one thread and in one function call. If your computer cannot keep up, slow down the loop (have less fps but increase the step to compensate the animation).