Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

How to render a VAO in multiple QOpenglWidget



  • I'm using QT 5.12 on Windows 10. My project renders the scene by using the old fixed pipeline (glBegin/glEnd). Now I need to change several geometries to the VAO method. But I'm having some strange problems.

    My application is designed to have two parts (left part and right part, calling window A and B). The goal is to draw (same) multiple geometries of different state in two windows. For example: window A shows an geometry while the window B hides it, things in window A are transparent while in window B are not, window A and window B have different right click menu...and so on.

    The current project has the following design:

    a) Scene class: holds all the geometries needs to be rendered;

    b) MView class: inherits from QOpenGLWidget and is responsible for the rendering job (for both windows);

    b) EEM class: generates two objects of MView and one object of Scene. The Scene object is transfered into and shared by two MView.

    According to the introduction to QOpenglWidget, the two MView will share the OpenGL context. So in my understanding, the ideal executing order is:

    Step 1. create two MView and one Scene in EEM;

    Step 2. create opengl context in initializeGL() of MView;

    Step 3. create VAOs in Scene; (glGenVertexArrays...)

    glGenVertexArrays(1, &m_vao);
    glGenBuffers(1, &m_vboPos);
    glGenBuffers(1, &m_vboNor);
    glGenBuffers(1, &m_ebo);
    

    Step 4. pack the geometries into VAOs; (glBufferData, glVertexAttribPointer...)

    // packed data into VAO
    glBindVertexArray(m_vao);
    glBindBuffer(GL_ARRAY_BUFFER, m_vboPos);
    glBufferData(GL_ARRAY_BUFFER, m_ptCorList.size() * sizeof(float), &m_ptCorList[0], GL_STATIC_DRAW);
    glVertexAttribPointer(5, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(5);
    ......
    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    

    Step 5. draw the geometries in paintGL(); (glDrawElements, glDrawElementsInstanced...)

    // render VAO
    glBindVertexArray(m_vao);
    glDrawElements(GL_TRIANGLES, m_ptIndList.size(), GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
    

    But this is not actually the case, when I changed my code from glBegin/glEnd to VAO. Something strange happens.

    Geometry A: Each time I redraw the scene(step 5), VAO of geometry A must be defined again (step 4). Only in this way can the geometry A be drawn on the window.

    If I only call the render VAO code, there will be an access violation crash while performing glDrawElements:

    Exception thrown at 0x00007FFCF79F0C63 (nvoglv64.dll) in application.exe: 0xC0000005: Access violation reading location 0x0000000000000000.
    

    However, if I define a geometry A in each MView, which means each MView has a VAO, then geometry A can be drawn normally. But I think that's a waste of resources.

    The problem has been bothering me for a long time and I would be very appreciated if anyone could give me some clue to the issue. Thank you!



  • According to the information I found about sharing objects between OpenGL contexts, data "containers" like VAO cannot be shared between contexts, but VBO can be shared.

    So, the most efficient way I could find out to solve the problem is that:

    Step 4: pack the geometries into VBOs:

    // packed data into VBOs
    glBindBuffer(GL_ARRAY_BUFFER, m_vboPos);
    glBufferData(GL_ARRAY_BUFFER, m_ptCorList.size() * sizeof(float), &m_ptCorList[0], GL_STATIC_DRAW);
    ......
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    

    Step 5: redefine the VAO and draw the geometries in paintGL() every time;

    // define VAO
    glBindVertexArray(m_vao);
    glBindBuffer(GL_ARRAY_BUFFER, m_vboPos);
    glVertexAttribPointer(5, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(5);
    ...
    // render VAO
    glDrawElements(GL_TRIANGLES, m_ptIndList.size(), GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    

    The main difference between two solutions lies in the place of glBufferData().



  • According to the information I found about sharing objects between OpenGL contexts, data "containers" like VAO cannot be shared between contexts, but VBO can be shared.

    So, the most efficient way I could find out to solve the problem is that:

    Step 4: pack the geometries into VBOs:

    // packed data into VBOs
    glBindBuffer(GL_ARRAY_BUFFER, m_vboPos);
    glBufferData(GL_ARRAY_BUFFER, m_ptCorList.size() * sizeof(float), &m_ptCorList[0], GL_STATIC_DRAW);
    ......
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    

    Step 5: redefine the VAO and draw the geometries in paintGL() every time;

    // define VAO
    glBindVertexArray(m_vao);
    glBindBuffer(GL_ARRAY_BUFFER, m_vboPos);
    glVertexAttribPointer(5, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(5);
    ...
    // render VAO
    glDrawElements(GL_TRIANGLES, m_ptIndList.size(), GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    

    The main difference between two solutions lies in the place of glBufferData().


Log in to reply