Reputation: 11436
I am trying to figure out how to implement the following concept with modern OpenGL API (4.3)
I have a vertex buffer which has a regular vertex array.But I need half of its data to process with ShaderProgram A, and another half - with ShaderProgram B.
Currently what I do is creating two different VAOs with vertex attribute pointers pointing to related parts of the vertex array.But in this case I must issue 2 draw calls -one per VAO.
Can I do it with a single draw call?
P.S: I thought of primitive restart,but AFAIK it doesn't switch shaders on each restart.
Upvotes: 1
Views: 750
Reputation: 2904
I have a vertex buffer which has a regular vertex array.
First of all, a vertex buffer does not have any arrays. Vertex arrays are state of vertex array objects and even back in the day, when there were client side vertex arrays, they were not part of vertex buffer objects. Vertex arrays, in the sense of the spec, are merely descriptions of the layout of an associated data store. A data store plus some state is what makes up a vertex buffer object. The combination of one or more vertex arrays and an associated data store enables pulling of vertex attributes inside a shader (unless the corresponding arrays are disabled).
I need half of its data to process with ShaderProgram A, and another half - with ShaderProgram B.Currently what I do is creating two different VAOs with vertex attribute pointers pointing to related parts of the vertex array.But in this case I must issue 2 draw calls -one per VAO.
In any case, use a single VAO
If you have the same buffer layout when sourcing vertex attribs with either shader, use a single vertex array and simply provide a starting offset with glDrawArrays()
.
Otherwise use two vertex arrays with the respective offset into the buffer (and other properties warranting separate vertex arrays), bind array A to index 0, array B to index 1 and use explicit attrib locations in your shaders like so (assuming some 4-component attribute):
layout (location = 0) in vec4 Attrib; // inside Shader A
layout (location = 1) in vec4 Attrib; // inside Shader B
Speaking of defining vertex arrays: starting with GL 4.3 you shouldn't use the old way (i.e. glVertexAttribPointer*()
) anymore but go for the more flexible glVertexAttribFormat()
, glBindVertexBuffer()
and glVertexAttribBinding()
. The new way decouples attrib specification and buffer association and works particularly well with interleaved attribs - which is a good thing. You would then do the following (pseudo-code):
bindVAO();
glVertexAttribFormat(0, 4, GL_FLOAT, GL_FALSE, 0);
glVertexAttribFormat(1, 4, GL_FLOAT, GL_FALSE, 0);
glBindVertexBuffer(0, buffer, 0, 0);
glBindVertexBuffer(1, buffer, offsetToBeginOfDataForShaderB, 0); //
glVertexAttribBinding(0, 0)
glVertexAttribBinding(1, 1);
If you suddenly wanted to switch the data processed by either shader, assuming that both shaders can handle that situation, all you needed to do is call:
// assuming your VAO is still bound (which it should be unless you really needed to swtich ... )
glVertexAttribBinding(0, 1)
glVertexAttribBinding(1, 0);
Now shader A starts sourcing attribs for location 0 from buffer binding 1 and vice versa. In you case this doesn't add much or any value. However, if needed, it makes switching layout/buffer associations much easier and reduces API and driver overhead. No need to call glBindBuffer()
nor glVertexAttribPointer()
again if you want some array to source data from another buffer. Another neat thing: we finally got rid of the damn void*
argument to glVertexAttribPointer()
... ;)
Note that a binding index and a attribute index need not be identical, you could also do this, although in your case it'd be pretty senseless:
glBindVertexBuffer(MAX_VERTEX_ATTRIB_BINDINGS - 1, buffer, 0, 0);
glBindVertexBuffer(MAX_VERTEX_ATTRIB_BINDINGS - 2, buffer, offsetToBeginOfDataForShaderB, 0); //
glVertexAttribBinding(0, MAX_VERTEX_ATTRIB_BINDINGS - 1)
glVertexAttribBinding(1, MAX_VERTEX_ATTRIB_BINDINGS - 2);
Can I do it with a single draw call?
No you can't. Ask yourself, how would you switch shaders during a single draw call?
If you spot any errors, please leave a comment.
Upvotes: 1
Reputation: 26569
No, you can't. There's no way to switch shaders in the middle of a draw call.
If you have the same attributes for both programs, you can share the same VAO between them, and use the start
parameter in the various glDraw
calls to draw the second half of the data without needing to switch VAOs (you still need to switch shaders though).
Upvotes: 1