首页 > 解决方案 > 将间接命令绑定为 draw_indirect_buffer 和原子计数器会导致崩溃

问题描述

我发现在使用 OpenGL 4.3 时我无法弄清楚的驱动程序的奇怪行为。

glDrawArraysIndirect()在默认的 FBO 上绘制了一些三角形。

    glBindBuffer(GL_DRAW_INDIRECT_BUFFER, command);
    glBindVertexArray(VAO);
        glDrawArraysIndirect(GL_TRIANGLES, nullptr);
    glBindVertexArray(0);
    glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);

是这样定义的command

struct DrawArraysIndirectCommand 
{
    GLuint count;
    GLuint primCount;
    GLuint first;
    GLuint baseInstance;
};

它在屏幕外 FBO 上进行的先前绘制调用的片段着色器中进行了更新,它被绑定为原子计数器:

glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, command);

在片段着色器中,它以这种方式更新:

layout(binding = 0, offset = 0) uniform atomic_uint vertex_count;
main() { 
    ...
    if(some condition) { atomicCounterIncrement(vertex_count); }
    ...
}

问题源于 nvidia 驱动程序和 intel 驱动程序之间的不同行为。起初,我command在程序开始时将其绑定为原子计数器,在任何绘图调用之前,直到程序结束我才解除绑定:

init() {
     // Generation of the buffer
     glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, command); // <--- FIX
}
draw() {
    glBindFramebuffer(GL_FRAMEBUFFER, offscreen_FBO);
        // Clear indirect draw command buffer
        glBindBuffer(GL_DRAW_INDIRECT_BUFFER, command);
            glBufferSubData(GL_DRAW_INDIRECT_BUFFER, 0, sizeof(DrawArraysIndirectCommand), 
                            &clearedCommand);
        glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);

        glBindVertexArray(VAO2);
             // Draw call in which the command is updated from the fragment shader
             glDrawArrays(GL_TRIANGLES, 0, another_vertex_count);
        glBindVertexArray(0);

        //use of the updated command in another draw call...
}

通过这种方式,它与 nvidia 驱动程序配合得很好。但是当我使用英特尔驱动程序测试该应用程序时,它只工作了几分钟然后就崩溃了(崩溃源于图形驱动程序,但没有产生错误)。我通过每次在绘图调用之前绑定原子计数器来解决这个问题:

draw() {
    glBindFramebuffer(GL_FRAMEBUFFER, offscreen_FBO);
        // Clear indirect draw command buffer
        glBindBuffer(GL_DRAW_INDIRECT_BUFFER, command);
            glBufferSubData(GL_DRAW_INDIRECT_BUFFER, 0, sizeof(DrawArraysIndirectCommand), 
                            &clearedCommand);
        glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);

        glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, command); // <--- FIX
        glBindVertexArray(VAO2);
             // Draw call in which the command is updated from the fragment shader
             glDrawArrays(GL_TRIANGLES, 0, another_vertex_count);
        glBindVertexArray(0);

        //use of the updated command in another draw call...
 }

通过这种方式,它在英特尔驱动程序上也可以正常工作。但为什么?为什么每次都需要重新绑定?这就像驱动程序有时会丢失绑定,但没有任何其他glBindBufferBase()绑定在同一索引处。command我认为问题与用作原子计数器和间接命令缓冲区的事实有关,但我无法理解链接。

标签: openglopengl-4

解决方案


推荐阅读