带有Qt 5.1的共享QGLWidgets问题的螺纹OpenGL

时间:2013-10-05 20:23:28

标签: c++ qt qthread qtgui qtcore

我使用两个QGLWidgets。一个用于加载纹理,一个用于渲染,但它不起作用。

我使用了以下解释 http://blog.qt.digia.com/blog/2011/06/03/threaded-opengl-in-4-8/

  

纹理上传线程   由于将数据量推送到GPU,上载许多(或大)纹理通常是昂贵的操作。同样,这是可能不必要地阻止主线程的那些操作之一。在4.8中,您可以通过创建一对共享QGLWidgets来解决此问题。其中一个小部件在一个单独的线程中是最新的,但从未在屏幕上显示。主线程通知上传线程要上传哪些图像,上传线程只是在每个图像上调用bindTexture(),然后在每个图像完成时通知主线程,以便可以将其绘制到屏幕上。

使用Qt 4.8和MinGW它工作正常,但现在我使用Qt 5.1和MSVC。当我想让线程中的小部件变为当前时,我收到错误:

  

无法使QOpenGLContext在另一个线程中保持最新状态

我理解错误,但我该如何修复它。当我没有设置小部件当前我无法加载纹理(在bindTexture()函数冻结)。我也想知道,为什么它适用于我的旧QT版本。当错误出现时,我可以按“忽略错误”,程序无论如何都会加载纹理。

以下是一些示例代码:

加载纹理:

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);
}

编辑:

当我将myTextureWidget的上下文移动到它运行的线程时,但是当构建GUI时,我从API获取makeCurrent Error(堆栈跟踪在QT5Widgetsd中的QLineEdit :: setPlaceHolderText函数中说明)。当我在主窗口显示几秒后将myTextureWidget移动到线程时,一切正常。但是我怎么知道qt什么时候完成了所有GUI构建的东西?我使用QGLWidget视口将GUI绘制到QGraphicsView。

myTextureWidget->context()->moveToThread(myTextureLoadingThread);

2 个答案:

答案 0 :(得分:4)

在开始新的Thread并调用makeCurrent()之前,你必须启动doneCurrent(),例如。

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

然后致电

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

这就是我为解决这个错误所做的工作。不幸的是,我没有使用线程进行渲染的完美解决方案。

//编辑

我上传了一个例子:https://dl.dropboxusercontent.com/u/165223/thread_example.zip

答案 1 :(得分:1)

可能为时已晚,但我遇到了同样的问题,并找到了解决方案,所以这就是我所做的,希望它能帮助未来的编码员:

Omgodie走在正确的轨道上。我认为你仍然得到相同的错误,因为主线程也调用paintEvent(),它可能试图使上下文最新。但是,相同的上下文在您的第二个线程中已经是当前的,因此是错误。

因此,当您的第二个线程处于活动状态时,您基本上需要阻止主线程尝试在窗口小部件中呈现。我通过在我的QGLWidget中添加一个布尔属性并在创建第二个线程之前将其设置为true来做到这一点,并在我的线程完成时返回false。然后我将我的小部件的paintEvent()修改为仅在布尔值设置为false时呈现。最后,我从第二个线程手动调用render函数。 这是一些代码:

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

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

线程完成后,不要忘记将上下文从第二个线程发送回主线程!

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

HTH