Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QGLWidget in another thread? What is the documentation referring to?

I'm doing a lot of texture uploading (60 VGA images a second) and it's blocking my UI thread. From the Qt 5.1 QGLWidget manual page (emphasis mine):

Texture uploading in a thread. Doing texture uploads in a thread may be very useful for applications handling large amounts of images that needs to be displayed, like for instance a photo gallery application. This is supported in Qt through the existing bindTexture() API. A simple way of doing this is to create two sharing QGLWidgets. One is made current in the main GUI thread, while the other is made current in the texture upload thread. The widget in the uploading thread is never shown, it is only used for sharing textures with the main thread. For each texture that is bound via bindTexture(), notify the main thread so that it can start using the texture.

What? How can a QWidget-based class such as a QGLWidget be moved to a thread? Attempting to do so results in:

QObject::moveToThread: Widgets cannot be moved to a new thread

I do not understand what the documentation is suggesting I implement in order to move e.g. bindTexture()'s execution out of the UI thread.

This is also mentioned here: Qt4/Opengl bindTexture in separated thread

No code posted there though.

like image 939
njahnke Avatar asked Aug 20 '13 21:08

njahnke


2 Answers

Little late on the answer, but widgets need to be moved to a new thread by the thread that created them. In your case something like:

QGLWidget *glWidget=new QGLWidget();
QThread *newThread=new QThread();

glWidget->doneCurrent();
glWidget->context()->moveToThread(newThread);

Here I only move the openGL context to the new thread, this requires that some overrides in QGLWidget are caught and ignore. You will need to create a new derived class from QGLWidget and override the following.

virtual void glInit();
virtual void glDraw();

virtual void initializeGL();
virtual void resizeGL(int width, int height);
virtual void paintGL();

This stops the standard UI thread from trying to make the OpenGL context current in the UI thread. Once you override the above you will need an event system to notify the thread that some of the events have happened, mainly resizeGl and paintGL otherwise the widget will not react properly with others around it.

The final part is in the creation of the QGLWidget. One of the parameters in the construct is const QGLWidget * shareWidget:

QGLWidget ( QWidget * parent = 0, const QGLWidget * shareWidget = 0, Qt::WindowFlags f = 0 )
QGLWidget ( QGLContext * context, QWidget * parent = 0, const QGLWidget * shareWidget = 0, Qt::WindowFlags f = 0 )
QGLWidget ( const QGLFormat & format, QWidget * parent = 0, const QGLWidget * shareWidget = 0, Qt::WindowFlags f = 0 )

You would then create the ui QGLWidget and the threaded GLWidget(derivided from QGLWidget with all of the overrides mentioned above) making sure when creating the thread GLWidget you provide the QGLWidget as a sharedWidget. This will make the 2 opengl contexted shared and allow you to load a texture in one that both can see. The code should look something like this:

QGLWidget *glWidget=new QGLWidget();
GLWidget *threadedWidget=new GLWidget(0/*parent*/, glWidget);
QThread *newThread=new QThread();

threadedWidget->moveToThread(newThread);

As for code I recently wrote a little example program using threaded QGLWidgets mainly for openGL/openCL interop, but it shows using muliple QGLWidgets in draw threads sharing a single context. The code is on github and a description is here http://www.krazer.com/?p=109

like image 199
Krazer Avatar answered Nov 19 '22 22:11

Krazer


I have to say I don't remember exactly how I did this, anyway I don't think the QGLWidget must be moved to another thread, and in fact that is not what the documentation says. It says to make it current: QGLWidget::makeCurrent(). That will make the OpenGL context of that QGLWidget current in the new thread.

However, I'd try with a QGLContext class. You can instantiate a QGLContext and then call QGLContext::create() to share with the one of the QGLWidget. With the QGLContext you can bind the textures.

like image 43
Luca Carlon Avatar answered Nov 19 '22 22:11

Luca Carlon