Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

glMultiDrawElementsIndirect custom drawID with multiple instances per DrawElementsIndirectCommand

Ask Question

If I setup a custom ascending integer drawID vertex buffer stream for per instance data with:

glVertexAttribDivisor(drawIDVertexStreamIdx, 1)

Using glMultiDrawElementsIndirect() given:

struct DrawElementsIndirectCommand
    uint  count;
    uint  instanceCount;
    uint  firstIndex;
    uint  baseVertex;
    uint  baseInstance;

When setting the instanceCount to more than one, I am confused looking at old notes and online about what exactly happens regards the drawID passed to the shader?

If say there are two DrawElementsIndirectCommand records invoked from one glMultiDrawElementsIndirect(), with the first record having an instanceCount of 3, and the second an instanceCount of say 1, what do the instances actually see in the shader? (Assuming the drawID vertex stream contains 0,1,2,3 etc)

Are they supposed to see 0,1,2 for the first DrawElementsIndirectCommand record instances, and 3 for the second DrawElementsIndirectCommand record instance?

All the examples I can find online seem to specifically set instanceCount to one and rely on multiple DrawElementsIndirectCommand records which makes me now doubt this understanding is correct?

const int CustomDrawIDIdx = 1;
const int VertexCount = 4;
DrawElementsIndirectCommand drawCallRecords[2] =
    { VertexCount, 3, 0, 0, 0 },
    { VertexCount, 1, 0, 0, 0 },
//  Attempt to set up custom drawID from a vertex attribute, where the vertex stream for it is a sequence of integers 0, 1, 2 etc
glVertexAttribIPointer(CustomDrawIDIdx, 1, GL_UNSIGNED_INT, 0, NULL);
glVertexAttribDivisor(CustomDrawIDIdx, 1);
glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, &drawCallRecords, 2, 0);

In the vertex shader:

layout (location = 1) in uint customDrawID;
void main()
    bool match = (customDrawID== gl_InstanceID); 

So this should cause 4 actual draw calls, as actual draw calls caused by glMultiDrawElementsIndirect() are determined by the number DrawElementsIndirectCommand records and their contained instance counts.

For each draw call gl_InstanceID should start from zero and count up for each instance, but go back to zero after each DrawElementsIndirectCommand record is processed?

So gl_InstanceID should do (0, 1, 2) for drawCallRecords[0], and then (0) for drawCallRecords[1]? What does the customDrawID do?

I am also curious if ARB_shader_draw_parameters on nVidia (GTX1070+) is still not recommended compared to using a custom drawID from a vertex stream?

*** UPDATE to reflect the answer by the very patient and helpful Nicol Bolas:

So given:

DrawElementsIndirectCommand drawCallRecords[2] =
    { VertexCount, 3, 0, 0, 0 },
    { VertexCount, 1, 0, 0, 3 /*baseInstance will push us along in customDrawID vertex stream*/ },

Then customDrawID will then do (0, 1, 2) and (3) across all instances in the multidrawindirect.

Which means that each drawn instance across the TWO draw calls, and the 3 + 1 total instances drawn (3 instances of one 'object', 1 instance of another 'object'), in one multidrawindirect call could reference completely unique transformation matrices for example. And in essence that emulates the functionality of gl_DrawID as long as you keep bumping the baseInstance like that (exclusive sum style) for each DrawElementsIndirectCommand record.

The baseInstance in each DrawElementsIndirectCommand record will push the offset into the customDrawID vertex stream, giving a customDrawID that is unique to each instance across all objects drawn.

Can you explain what you mean by "a custom drawID"? Are you talking about the value of gl_DrawID or the value of something else? – Nicol Bolas Nov 24, 2018 at 18:31 By custom drawID I mean a value sourced from a vertex buffer stream not the built in gl_DrawID: openglsuperbible.com/2013/10/16/the-road-to-one-million-draws – iam Nov 24, 2018 at 18:53 I don't see where any of those sources say that shader_draw_parameters is "not recommended" for this purpose. – Nicol Bolas Nov 24, 2018 at 19:55

So this should cause 4 actual draw calls, as actual draw calls caused by glMultiDrawElementsIndirect() are determined by the number DrawElementsIndirectCommand records and their contained instance counts.

No. Instances and "draw calls" are not the same thing.

A single draw call is defined as if by a call to glDraw*InstancedBaseVertexBaseInstance; that's what happens when the system reads a single entry from the array of draw data. That single draw call includes all instances. Instancing is a thing that happens within a draw call.

This is also why per-instance values are not guaranteed to be dynamically uniform expressions.

Individual draws within a multi-draw command are, gl_DrawID aside, completely separate from one another. They do not interact. The values your shader gets for instanced arrays or gl_InstanceID would be no different from issuing each draw call separately.

Your "custom drawID" is not a draw ID at all; it is an instanced array value. Therefore, it follows the rules of instancing, and cares nothing for which draw call it is in.

Even if the actual array you use to feed customDrawID is just a zero-based integer index, instances arrays and gl_InstanceID don't work the same way.

gl_InstanceID ignores the base instance. Instance arrays do not. As such, the instance value fetched from any instance array will always be offset first by the base instance.

So if you want the customDrawID for a specific draw call to start from a particular value, then you set baseInstance to be the instance index of that particular value. So given a zero-based integer index array, if you want a particular draw call to have its first instance receive the customDrawID value of "3", you set baseInstance to 3.

It's specifically the behaviour of a 'custom drawID' sourced from a vertex buffer stream/attribute (where the vertex buffer is a list of sequential integers) that I am interested in (so I can actually profile it against gl_DrawID correctly). – iam Nov 25, 2018 at 3:53 @iam: That only affects how you use my answer. Your "custom drawID" is not a draw ID; it's just an instanced value, and therefore acts in accord with the rules of instancing. Which I state in the first and last paragraphs. – Nicol Bolas Nov 25, 2018 at 4:07 Sorry this isn't clear to me still. With "instances all happen within a draw call" does that mean one DrawElementsIndirectCommand record is a draw call or all DrawElementsIndirectCommand records issued from one glMultiDrawElementsIndirect() are counted as one draw call from the instancing point of view? In the former case I would then expect to see (0, 0, 0) and (0) as the custom drawID in the example I state in the question, in the latter I would expect (0, 1, 2) and (3). – iam Nov 25, 2018 at 4:48 Or actually are you saying I would see (0, 1, 2) and (0). And that without using the real gl_drawID there is no way to differentiate between the DrawElementsIndirectCommand records? – iam Nov 25, 2018 at 4:51 @iam: What you call "records" in your multidraw command are more correctly called "draw calls". Because that's what they are: indirect rendering is defined exactly as if the implementation read the data structure and made a call to glDraw*InstancedBaseVertexBaseInstance with the parameters read from the data structure. A multi-draw-indirect call is just doing that more than once. But each draw's parameters are completely independent of any other draw's parameters. – Nicol Bolas Nov 25, 2018 at 4:57

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.