8

I use two QGLWidgets. One for loading textures and one for rendering, but it is not working.

I used the following explanation from http://blog.qt.digia.com/blog/2011/06/03/threaded-opengl-in-4-8/

Texture uploading thread Uploading many (or large) textures is typically an expensive operation because of the amount of data being pushed to the GPU. Again, this is one of those operations that can unnecessarily block your main thread. In 4.8 you can solve this problem by creating a pair of shared QGLWidgets. One of the widgets is made current in a separate thread, but is never made visible on screen. The main thread informs the uploading thread which images to upload and the uploading thread simply calls bindTexture() on each of these images and then notifies the main thread when each one has finished so it can be drawn to screen.

With Qt 4.8 with MinGW it works fine, but now i use Qt 5.1 with MSVC. I am getting an error when I want make the widget in the thread current:

Cannot make QOpenGLContext current in a different thread

I understand the error but how can i fix it. When i don't set the widget current i can't load textures (freezed at the bindTexture() function). I also wondering, why it works with my old QT Version. When the error appears i can press "ignore error" and the programm loads the textures anyway.

Here is some sample code:

Loading textures:

GLContext::GLContext(QWidget *parent, QGLWidget *myDisplayWidget) :
  QGLWidget(parent,myDisplayWidget)
{
}

...

GLContext* myTextureWidget = new GLContext(this,myDisplayWidget);

...

void TextureLoadingThread::run()
{    
    makeCurrent(); //Here is the bug!
    QImage *im = new QImage(filename);
    GLuint textid = myTextureWidget->bindTexture(*im, GL_TEXTURE_2D, GL_RGBA);
}

EDIT:

When i move the context of the myTextureWidget to the thread it works, but then i get the makeCurrent Error from the API when the GUI will build (the stack trace said at the QLineEdit::setPlaceHolderText function in QT5Widgetsd). When i move the myTextureWidget to the thread some seconds after the mainwindow has been shown, all works fine. But how can i know when qt finished all GUI building Stuff? I draw the GUI to a QGraphicsView with a QGLWidget viewport.

myTextureWidget->context()->moveToThread(myTextureLoadingThread);
riv333
  • 389
  • 2
  • 12
  • There was no "QOpenGLContext" in Qt 4.8, so I am not sure what you mean by "it works fine". Perhaps you mean a different design and implementation with QtOpenGL? – László Papp Oct 06 '13 at 05:58
  • I didn't change very much in my programm code, with exception some lines for the porting to QT 5.1. I use the QT OpenGL Version (not ANGLE) and still two QGLWidgets to share the context. I think i will do some debugging today. I tried yesterday to move the context of myTextureWidget to the TextureLoadingThread. Then i get only at start the makeCurrent error, although i never called makeCurrent in my source before the error occurs. But when i ignore the error i can call makeCurrent as often i want and the error not occurs any more. – riv333 Oct 06 '13 at 09:28
  • did you find a solution yet? I have exactly the same problem... – user2950911 Mar 13 '14 at 11:54

2 Answers2

4

Before you start the new Thread and call makeCurrent() you have to initiate doneCurrent() e.g.

void QGLWidget::startRendering()
{
    doneCurrent();
    context()->moveToThread(mTextureLoadingThread);
}

and then call

void TextureLoadingThread::run()
{    
    makeCurrent(); //Here is the bug!
    ...
}

This is what i've done to work around this error. Unfortunately i don't have the perfect solution for using a thread for rendering.

// EDIT

I have uploaded an example: https://dl.dropboxusercontent.com/u/165223/thread_example.zip

omgodie
  • 209
  • 1
  • 2
  • 13
  • 1
    Hmm i tried it already, but i still get the same error. But i agree it is important too. I have the feeling that i can't call makeCurrent before the viewport is initialized. – riv333 Oct 10 '13 at 16:44
  • i made an example for you and some ppl at work, i'll post it in a few hours. just have to add comments :) – omgodie Oct 12 '13 at 10:06
  • thank you for the example, but i already understood your solution, but it is not exact the problem of my programm, I use a QGraphicsview with an OpenGL viewport, in which i render standard QT components. The error also never appears at my makeCurrent call (when i move the context to the thread), it appears anywhere in the API where QT call the makeCurrent method. But i think the tutorial is very helpfull for other people, who do standard OPenGL stuff so thx again ;) Perhaps it is a bug of QT, when i have time i will extend your example to be sure thats a bug. – riv333 Oct 17 '13 at 23:43
  • I run into the smae problem. I ended with an awful solution while (!_widget->isVisible()) { } _widget->makeCurrent(); perhaps using QWindow it possible to test if itExposed() – Luciano Lorenti Mar 05 '14 at 12:42
1

It's probably too late but I have had the same problem and found the solution, so here is what I did, hoping that it will help future coders:

Omgodie was on the right track. I think you still get the same error because the main thread is also calling paintEvent() which probably tries to make the context current. However, the same context is already current in your second thread, hence the error.

So you basically need to stop the main thread from trying to render in your widget while your second thread is active. I did that by adding a boolean attribute to my QGLWidget and set it to true before creating my second thread, and back to false when my thread was done. Then I modified the paintEvent() of my widget to only render when the boolean is set to false. Finally, I call the render function manually from the second thread. Here is some code:

//GLWidget derives from QGLWidget:
void GLWidget::paintEvent(QPaintEvent *e) {
      if ( !_second_thread_active )
           render();
}

//Then in your thread:
void Thread::doWork() {
      //Do stuff
      render();
}

Once your thread is done, don't forget to send the context back from the second thread to the main thread!

doneCurrent();
context()->moveToThread(&qapp->thread());

HTH

Gpack
  • 1,878
  • 3
  • 18
  • 45