Drawing in one draw call vertex buffer with different programs

Started by
6 comments, last by 21st Century Moose 9 years, 9 months ago

Hi all,

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.

Thanks!

Advertisement

Can I do it with a single draw call?

No.
You have already found the solution.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Yeah, either you have to combine your shaders (and then only if you can make a shader that will run on GPU efficiently, e.g. with a minimum a branching or dynamic loops), or just suck it up and use two draw calls.

Remember, multiple draw calls _is not a problem_, so long as they don't scale linearly with the number of objects in your scene! You can easily have hundreds of draw calls even on mobile devices. It's better to have a system that always needs 57 draw calls to render any scene (whether it has 1 object or 10,000) than it is to need "only" 1 draw call per object (meaning you'd only ever be able to have a few hundred objects in a scene at once on low-end devices).

Look into instancing or deferred approaches to help ensure you only use one draw call per material to the extent possible.

Sean Middleditch – Game Systems Engineer – Join my team!

Thanks for the help.

And It is possible, using the new

  • GL_ARB_multi_draw_indirect
  • "Approaching Zero Driver Overhead"

Thanks for the help.

And It is possible, using the new

  • GL_ARB_multi_draw_indirect
  • "Approaching Zero Driver Overhead"
Those still have one shader per each draw call.
You can't use two different programs within one call, ever.

Thanks for the help.

And It is possible, using the new

  • GL_ARB_multi_draw_indirect
  • "Approaching Zero Driver Overhead"
Those still have one shader per each draw call.
You can't use two different programs within one call, ever.

By implementing an übershader .. and using shader subroutines, which are the GLSL equivalent of function pointers. These can be formed into arrays and different implementations of the subroutine can have entirely different behavior.

That kind of ubershader is still one program though ;-)

Also, I would recommend only ever using that approach if you're desperate to reduce CPU-side overhead. That technique allows you to drastically reduce the amount of gl* calls that the CPU has to make, but it's also likely to cause a huge amount of overhead on the GPU-side - so you're wasting GPU-time in order to gain CPU-time.
If your game is GPU-bound, then you'd be better off making more draw calls with non-uber shaders ;)

Currently what I do is creating two different VAOs with vertex attribute pointers pointing to related parts of the vertex array.

Just to address this part, you don't need this kind of set up at all. You can do it with a single VAO and a single set of attrib pointers, but still with two draw calls, so it takes away some of the overhead.

So, say you're using glDrawArrays: look at the documentation for that and you'll see that it has a first parameter. Let's assume that you have 200 vertices, and you want the first 100 drawn with ShaderProgramA, the second 100 with ShaderProgramB. Your code is:

glBindVertexArray (LetsUseASingleVAOHere);

glUseProgram (ShaderProgramA);

glDrawArrays (GL_TRIANGLES, 0, 100);

glUseProgram (ShaderProgramB);

glDrawArrays (GL_TRIANGLES, 100, 100);

Similarly with glDrawElements you can see that it has a pointer parameter, (GLvoid *indices), as well as a count parameter, so again rather than using two sets of attrib pointers, you just use a single set and then adjust the parameters of your draw call to specify which range of the buffers to draw.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

This topic is closed to new replies.

Advertisement