QThread在GUI事件上调用glBufferData()时不更新OpenGL VBO并且不呈现任何内容

时间:2014-05-22 04:16:15

标签: c++ qt opengl

我使用的是QT 4.8,Visual Studio 2008和OpenGL 3.3

我有两个需要在OpenGL中渲染(静态)的数据集。我正在使用QThread从我的QGLWidget实现一个单独的显示线程。 如果我先将两个数据集加载到主内存中,然后启动渲染线程,一切都运行正常,但我现在需要的是为用户提供加载和卸载第二个数据集的灵活性,而不会影响第一个数据集。 所以我有一个按钮来加载第一个数据集:

  void MyView::on_btn_start_clicked()
    {
    // Load first data set from file and start the render thread
    // MyGLObject is a QGLWidget object
    MyGLObject->Data1 = new DataSet1();
    if(MyGLObject->Data1->processCSVFile("filename.csv"))
        {
        MyGLObject->initRenderThread();
        }

    }

这是iniRenderThread()的作用:

   void MyGLWidget::initRenderThread()
    {
    doneCurrent();
    displayThread.initModelRender();
    displayThread.start();
    }

displayThread是QThread的子类,run()方法的类似于:

void DisplayThread::run()
 {
    MyGLObject->makeCurrent();
    // OPenGL and GLEW initialization here ....
       ...

    // initialize first data set's VAO and VBO
    glGenVertexArrays(1, &Vao_data1);
    glBindVertexArray(Vao_data1);
    glGenBuffers(1, &Vbo_data1);
    glBindBuffer(GL_ARRAY_BUFFER, Vbo_data1);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(0);
    glBindVertexArray(0);

    // Only generate VAO and VBO for second dataset  (no data there yet)
    glGenVertexArrays(1, &Vao_data2);
    glBindVertexArray(Vao_data2);   
    glGenBuffers(1, &Vbo_data2);
    glBindVertexArray(0);

    // load shader here
        ...

    while(doRendering)
    {
        if(doResize)
        {
        GLResize(w, h);
        doResize = false;
        }
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            // setup model view matrix and pass it as shader uniform
                      ...
            // Draw first dataset
            glBindVertexArray(Vao_data1);
            glDrawArrays(GL_POINTS, 0, NUMVERTS);

            // check if second set is loaded and draw if it is
            if(secondDataLoaded)
               {
                glBindVertexArray(Vao_data2);
                glDrawArrays(GL_LINE_STRIP, start, (end - start));
                }

        MyGLObject->swapBuffers();

        if(!benchmark)
        {
            Mutex.lock();
            updateFrame = false;
            if(!updateFrame && doRendering && !reloadShader)
            WaitCondition.wait(&Mutex);
                Mutex.unlock();
    }

    frameCounter++;
    }  // end while rendering

 }

现在重要的问题是:在哪里为第二个数据集调用glBufferData()?????? 我尝试在DisplyThread类的一个插槽中调用它,以响应按钮单击发出调用信号。 所以显示线程中的一个插槽就像:

     DisplayThread::showSecondSet()
     {
      glBindVertexArray(Vao_data2);
      glBindBuffer(GL_ARRAY_BUFFER, Vbo_data2);
  glBufferData(GL_ARRAY_BUFFER, sizeof(dataSet2->vertices), dataSet2->vertices, GL_STATIC_DRAW);
      secondDataLoaded = true;
      }

渲染中没有出现。我也通过在插槽中分配内存然后在渲染循环中调用glBufferSubData()来尝试解决方案here,但仍然没有得到任何结果。 它正确呈现的唯一方法是,如果我在render循环中调用glBufferData(),这是非常低效的。 无论如何,我只能在执行插槽时创建VBO并将数据复制到它,而不是在渲染循环内部吗?

编辑:我尝试使用qDebug()从displayThread()中的showSecondSet()SLOT打印一些东西,它正在打印,并且插槽内的所有其他函数调用正在执行,除了OpenGL调用。我猜它与当前的GL背景有关

3 个答案:

答案 0 :(得分:0)

将VBO更新放入插槽是正确的做法。但是,您还必须确保在目标上下文处于活动状态时执行插槽,这在大多数情况下归结为在DisplayThread中执行的插槽。

在其中一个项目中我不得不处理这个问题,最后我通过一堆蹦床插槽和QMetaObject::invoke来电调用。例如,在生产代码中有以下内容:

class … {
public slots:
    void doFoobar(
            unsigned int a,
            unsigned int b ) {
                    QMetaObject::invokeMethod(this, "_priv_loslot_doFoobar",
                    Qt::QueuedConnection,
                    Q_ARG(unsigned int, a),
                    Q_ARG(unsigned int, b) );
            }
private slots:
    void _priv_loslot_doFoobar(
            unsigned int a,
            unsigned int b );
};

是的,它是粗糙的,是的,它是一种解决方法,但它完成了工作。请注意,这样做就像你正在尝试的那样非常相似:在OpenGL缓冲区对象中实时更新数据,由具有自己的OpenGL上下文的工作线程执行。

  

渲染中没有出现

只更新VBO是不够的。 OpenGL不是一个场景图,VBO本身只是一块没有形式的内存。您需要调用完整的重绘以使更改可见。

答案 1 :(得分:0)

我建议创建一个新的QObject并将while的主体移动到该对象的一个​​插槽中,并使用QTimer让它重复:

void DisplayHandler::started()//slot connected to the running's QThread's started
{
    MyGLObject->makeCurrent();
    // OPenGL and GLEW initialization here ....
       ...

    // initialize first data set's VAO and VBO
    glGenVertexArrays(1, &Vao_data1);
    glBindVertexArray(Vao_data1);
    glGenBuffers(1, &Vbo_data1);
    glBindBuffer(GL_ARRAY_BUFFER, Vbo_data1);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(0);
    glBindVertexArray(0);

    // Only generate VAO and VBO for second dataset  (no data there yet)
    glGenVertexArrays(1, &Vao_data2);
    glBindVertexArray(Vao_data2);   
    glGenBuffers(1, &Vbo_data2);
    glBindVertexArray(0);

    // load shader here
        ...

}

void DisplayHandler::draw()//slot called when update needs to happen
{

    MyGLObject->makeCurrent();
    if(doResize)
    {
    GLResize(w, h);
    doResize = false;
    }
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        // setup model view matrix and pass it as shader uniform
                  ...
        // Draw first dataset
        glBindVertexArray(Vao_data1);
        glDrawArrays(GL_POINTS, 0, NUMVERTS);

        // check if second set is loaded and draw if it is
        if(secondDataLoaded)
           {
            glBindVertexArray(Vao_data2);
            glDrawArrays(GL_LINE_STRIP, start, (end - start));
            }

    MyGLObject->swapBuffers();

    MyGLObject->doneCurrent();
    frameCounter++;

}

void DisplayHandler::showSecondSet()
{
      MyGLObject->makeCurrent();
      glBindVertexArray(Vao_data2);
      glBindBuffer(GL_ARRAY_BUFFER, Vbo_data2);
  glBufferData(GL_ARRAY_BUFFER, sizeof(dataSet2->vertices), dataSet2->vertices, GL_STATIC_DRAW);
      secondDataLoaded = true;
      MyGLObject->doneCurrent();
}

然后开始线程:

void MyGLWidget::initRenderThread()
{
    doneCurrent();
    QThread* thr = new QThread(this);
    connect(thr, &QThread::started, &displayThread, &DisplayHandler::started);
    connect(this, &QObject::destroyed, thr, &QThread::quit);

    displayThread.moveToThread(thr);
    displayThread.initModelRender();
    thr.start();
}

答案 2 :(得分:0)

我通过将绘制命令中的VBO调用(glGufferData()调用)分开来解决了这个问题。 我现在保留2个名为“secondDataLoaded”和“doReload”的布尔变量 将第二个数据集加载到主机内存后,应用程序将发出一个连接到showSecondSet()插槽的信号 现在showSecondSet()的主体是这样的:

 DisplayThread::showSecondSet()
 {
   secondDataLoaded = true;
   doReload = true;
  }

在渲染循环中,我有:

    if(secondDataLoaded)
{
    glBindVertexArray(Vao_data2);
    if(doReload)
    {   
                createVBOs();
            doReload = false;
    }
    drawData2();
    glBindVertexArray(0);
}

像魅力一样工作:)感谢大家的时间和宝贵的提示。