QT在单独的线程中管理OpenGL上下文

时间:2014-07-22 18:06:20

标签: c++ multithreading qt opengl

我已经了解了为Qt QGLWidget hereherehere设置单独的渲染线程。 我还设法得到一种“工作”设置:在视口中清除颜色。看起来没问题。但是我收到以下警告:

  

QOpenGLContext :: swapBuffers()以非暴露窗口,行为调用   未定义

我首先创建一个继承自QGLWidget的小部件。我还设置了OpenGL格式:

在Widget构造函数中:

  QGLFormat format;
  format.setProfile(QGLFormat::CompatibilityProfile);
  format.setVersion(4,3);
  format.setDoubleBuffer(true);
  format.setSwapInterval(1);

  setFormat(format);
  setAutoBufferSwap(false);

然后我在同一个小部件中初始化渲染线程:

void GLThreadedWidget::initRenderThread(void){
   doneCurrent();
    context()->moveToThread(&m_renderThread);
    m_renderThread.start();
}

从那时起,整个渲染就在该线程内完成:

RenderThread构造函数:

RenderThread::RenderThread(GLThreadedWidget *parent)
    :QThread(),glWidget(parent)
{
    doRendering = true;
}

RenderThread run()方法:

    void RenderThread::run(){
         glWidget->makeCurrent();

         GLenum err = glewInit();
         if (GLEW_OK != err) {
             printf("GLEW error: %s\n", glewGetErrorString(err));
         } else {
             printf("Glew loaded; using version %s\n", glewGetString(GLEW_VERSION));
         }
          glInit();
         while (doRendering){
             glWidget->makeCurrent();
             glClear(GL_COLOR_BUFFER_BIT );

             paintGL(); // render actual frame


            glWidget->swapBuffers();
            glWidget->doneCurrent();
            msleep(16);
        }

    }

任何人都可以指出问题出在哪里?如果可以丢弃该消息?另外,对Qt中渲染线程设置的直接和简洁的解释将非常有用。使用Qt 5.2(桌面OpenGL构建)

2 个答案:

答案 0 :(得分:1)

根据您所显示的内容,看起来就像您收到的消息处理程序警告一样,因为您开始触发缓冲区交换"太快"在窗口设置序列中,可以直接通过QGLContext :: / QOpenGLContext :: swapBuffers(),也可以间接通过多种可能的方式,在手动调试之外都无法检测到。我的意思太快就是在小部件的父窗口被标记为"暴露" (在窗口系统显示之前)。

至于消息是否可以被丢弃,它可以......但它不安全,因为它可能会在前几帧左右获得未定义的行为这样做并且窗口没有准备好(特别是如果您在启动时立即调整大小到不同的范围而不是.ui文件指定)。 Qt文档说,在你的窗口暴露之前,Qt必须告诉OpenGL根据什么是有效的不值得信任的范围进行绘制。我不确定个人会发生什么。

使用您显示的代码,可以轻松解决问题 - 避免在您的窗口显示它之前启动渲染逻辑。然而,使用QGLWidget检测曝光并不明显。这里的示例大致与我使用的一样,假设您的QGLWidget中的子类类似于' OGLRocksWidget',它是中央窗口小部件的子项,并且该中央窗口小部件是您实现的子项QMainWindow(这样你的小部件就必须调用parentWidget() - > parentWidget()来获取它的QMainWindow):

OGLRocksWidget::paintGL()
{
    QMainWindow *window_ptr = 
        dynamic_cast<QMainWindow *>(parentWidget() ? parentWidget()->parentWidget() : 0);
    QWindow *qwindow_ptr = (window_ptr ? window_ptr->windowHandle() : 0);

    if (qwindow_ptr && qwindow_ptr->isExposed())
    {
        // don't start rendering until you can get in here, just return...
        // probably even better to make sure QGLWidget::isVisible() too
    }
}

当然,您不必在QGLWidget :: paintGL()的实现中执行此操作,但在您的特定设置中,您最好不要启动渲染线程,直到您的窗口告诉您& #39;暴露。

看起来你可能会遇到比这更大的问题。您没有将正确的GL活动挂钩到代码中的正确位置,而不是QGLWidget的意图。我觉得你所处的位置是因为关于这个的文档有点不稳定和分散。对于那个部分,QGLWidget's detailed description向下说它&#34;这里是QGLWidget子类可能看起来如何的大致轮廓&#34;是一个开始得到这个想法的好地方。您希望覆盖其中包含相关代码的任何关键虚拟虚拟机,并将其移至这些调用中。

例如,您的窗口小部件的构造函数正在进行设置工作,这可能更安全地放入initializeGL()覆盖,因为QGLWidget的意图是在安全时间发出信号时向您发出信号通过那个电话做到这一点。我的意思是更安全每当我说这里是你不会得到看似随机的调试异常(在发布版本中可以默默地对你的运行时稳定性产生影响)。

方面建议:安装Qt源代码,将调试器指向它,并观察代码运行,包括Qt。你上次看到它时,你的setFormat()调用实际上删除了当前底层的QOpenGLContext。这可能很有用,因为你很快就会想要创建一个新的或者至少测试你的选择。

不稳定的风险是我为什么要在一年后尝试将至少某种答案放在一起的原因。我通过很多(太多)调试了解到这一点。我很喜欢Qt团队所做的事情,但是当他们完成将所有内容迁移到QOpenGL *调用时(或者他们看到最终适合他们的OpenGL支持的位置,包括对它的永久考虑因素)时,Qt会好得多。窗口支持在一起)。

答案 1 :(得分:0)

QOpenglWidget带有自己的上下文。如果要使用后台线程进行渲染,则必须将共享上下文传递给线程并执行一些正确的步骤。 https://stackoverflow.com/a/50368372/3082081

中的详细信息