I've found a strange behavior of the drivers I can't figure out while using OpenGL 4.3.
I'm drawing some triangles with glDrawArraysIndirect()
on the default FBO.
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, command);
glBindVertexArray(VAO);
glDrawArraysIndirect(GL_TRIANGLES, nullptr);
glBindVertexArray(0);
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
The command
is defined like this:
struct DrawArraysIndirectCommand
{
GLuint count;
GLuint primCount;
GLuint first;
GLuint baseInstance;
};
It's updated in the fragment shader of a previous draw call made on an offscreen FBO, where it's bound as an atomic counter:
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, command);
In the fragment shader it's updated in this way:
layout(binding = 0, offset = 0) uniform atomic_uint vertex_count;
main() {
...
if(some condition) { atomicCounterIncrement(vertex_count); }
...
}
The problem arises from a different behavior between nvidia drivers and intel drivers. At first I bound the command
as an atomic counter at the beginning of the program, before any draw call and I never unbound it until the end of the program:
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...
}
In this way it worked fine with nvidia drivers. But when I tested the application with intel drivers it worked only for a few minutes and then crashed (the crash originated in the graphic driver, but no error was produced). I solved this problem by binding the atomic counter every time before the draw call:
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...
}
In this way it works fine also on the intel drivers. But why? Why do I need to rebind it every time? It's like if the driver sometimes lose the binding, but there isn't any other glBindBufferBase()
that bind at the same index. I think the problem is related with the fact that command
is used both as an atomic counter and as an indirect command buffer, but I can't understand the link.