8

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

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

纹理上传线程 上传许多(或大)纹理通常是一项昂贵的操作,因为要推送到 GPU 的数据量很大。同样,这是可能不必要地阻塞主线程的操作之一。在 4.8 中你可以通过创建一对共享的 QGLWidgets 来解决这个问题。其中一个小部件在单独的线程中成为当前的,但永远不会在屏幕上显示。主线程通知上传线程要上传哪些图像,上传线程只需对这些图像中的每一个调用 bindTexture(),然后在每个图像完成时通知主线程,以便将其绘制到屏幕上。

使用带有 MinGW 的 Qt 4.8 可以正常工作,但现在我使用带有 MSVC 的 Qt 5.1。当我想让线程中的小部件成为当前小部件时出现错误:

不能在不同的线程中使 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 错误(堆栈跟踪在 QT5Widgetsd 中的 QLineEdit::setPlaceHolderText 函数中说)。当我在显示主窗口几秒钟后将 myTextureWidget 移动到线程时,一切正常。但是我怎么知道qt什么时候完成了所有的GUI构建东西?我将 GUI 绘制到带有 QGLWidget 视口的 QGraphicsView 上。

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

2 回答 2

4

在启动新线程并调用 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

于 2013-10-09T18:59:01.887 回答
1

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

Omgodie 走在了正确的轨道上。我认为您仍然会遇到相同的错误,因为主线程也在调用paintEvent(),这可能试图使上下文成为当前的。但是,第二个线程中已经存在相同的上下文,因此出现错误。

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

//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());

高温高压

于 2014-12-02T05:50:24.513 回答