Привязка косвенной команды как draw_indirect_buffer и как атомного счетчика приводит к сбою - PullRequest
1 голос
/ 10 октября 2019

Я обнаружил странное поведение драйверов, которое не могу понять при использовании 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, где он связан как атомарныйcounter:

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. Но когда я тестировал приложение с драйверами Intel, оно работало всего несколько минут, а затем зависало (сбой произошел в графическом драйвере, но ошибки не было). Я решил эту проблему, привязав атомный счетчик каждый раз перед вызовом отрисовки:

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...
 }

Таким образом, он отлично работает и на драйверах Intel. Но почему? Зачем мне каждый раз перепривязывать? Это как если бы драйвер иногда терял привязку, но нет других glBindBufferBase(), которые связываются с тем же индексом. Я думаю, что проблема связана с тем фактом, что command используется как в качестве атомного счетчика, так и в качестве косвенного буфера команд, но я не могу понять ссылку.

...