glMultiDrawElementsIndirect and custom draw_ID

Hi,

I’m struggling trying to correctly implement a custom draw_ID for glMultiDrawElementsIndirect.

I’m actually succeeding a first time, everything displays correctly.
But when I modify my scene, I produce a new buffer of commands together with new draw_IDs, and this time the draw_IDs do not seem to be correct. When I print them with shaderprinter, many draw_IDs are 0, even though I checked that the values I sent are not 0.

I tried with both a dedicated draw_id buffer, or through the baseInstance field of the commands, to no avail.
A note: the first time, the draw_IDs are an incremental sequence (0, 1, 2… max). And I call glMultiDrawElementsIndirect with ‘max’ as the number of commands.
The second time, the draw_IDs are much more randoms, as they access newly created objects (I keep the not-rendered-anymore data such as vertices in various buffers).

This is the setup code:

    glGenBuffers( 1, &_commands_bo ); CHKGL;
    glBindBuffer( GL_DRAW_INDIRECT_BUFFER, _commands_bo ); CHKGL;
    glBufferData( GL_DRAW_INDIRECT_BUFFER, sizeof(gl2d::draw_cmd_t) * w->_commands_num, nullptr, GL_DYNAMIC_DRAW ); CHKGL;

//feed the draw id to the shader.
#if USE_DRAW_ID_BUFFER
    glGenBuffers( 1, &_draw_id_bo ); CHKGL;
    glBindBuffer( GL_ARRAY_BUFFER, _draw_id_bo ); CHKGL;
    glBufferData( GL_ARRAY_BUFFER, sizeof(GLuint) * w->_commands_num, nullptr, GL_DYNAMIC_DRAW ); CHKGL;
    glVertexAttribIPointer(w->draw_id_loc, 1, GL_UNSIGNED_INT, 0, (GLvoid*)0 ); CHKGL;

#else
    glBindBuffer(GL_ARRAY_BUFFER, _commands_bo); CHKGL;
    glVertexAttribIPointer(
            w->draw_id_loc,
            1,
            GL_UNSIGNED_INT,
            sizeof(gl2d::draw_cmd_t),
            (void*)(offsetof(gl2d::draw_cmd_t, baseInstance))); CHKGL;
#endif

    glEnableVertexAttribArray(w->draw_id_loc); CHKGL;
    glVertexAttribDivisor(w->draw_id_loc, 1); CHKGL; //only once per instance

and the rendering:

    glBindBuffer( GL_DRAW_INDIRECT_BUFFER, _cur_bos->_commands_bo ); CHKGL;
    _cur_bos->_commands_map = glMapBufferRange (GL_DRAW_INDIRECT_BUFFER, 0, _commands_num * sizeof(gl2d::draw_cmd_t), MAP_OPTIONS ); CHKGL;
    assert (_cur_bos->_commands_map);
    
    _cmd_it = _commands;
    draw_children (parent);
    _commands_size = _cmd_it - _commands;
    
    memcpy ( (char*) (_cur_bos->_commands_map), _commands, sizeof(gl2d::draw_cmd_t) * _commands_size);
    
#if GL2D_USE_DRAW_ID_BUFFER
    glBindBuffer( GL_ARRAY_BUFFER, _cur_bos->_draw_id_bo ); CHKGL;
    GLuint* draw_id_map = (GLuint*) glMapBufferRange (GL_ARRAY_BUFFER, 0, _commands_num * sizeof(GLuint), MAP_OPTIONS ); CHKGL;
    assert (draw_id_map);
    for (size_t i=0; i<_commands_size;++i) {
        draw_id_map[i] = _commands[i].baseInstance;
        //commands[i].baseInstance = 0;
    }
    assert (glUnmapBuffer (GL_ARRAY_BUFFER)); CHKGL;
#endif

    glBindBuffer( GL_DRAW_INDIRECT_BUFFER, _cur_bos->_commands_bo ); CHKGL;
    glMultiDrawElementsIndirect (GL_TRIANGLE_STRIP, GL_UNSIGNED_SHORT, nullptr, _commands_size, 0); CHKGL;

I don’t fully understand what you’re trying to do with this code. It seems like your “draw_ID” is just an instanced vertex attribute. Instancing is based on the instanceCount of each draw command, not the number of drawing commands. So this:

And I call glMultiDrawElementsIndirect with ‘max’ as the number of commands.

Is a non-sequitur.

Now maybe the commands in your indirect rendering always draw a single instance and use the baseInstance parameter to set the base instance. That’s what the USE_DRAW_ID_BUFFER seems like its doing when it sets up the glVertexAttribIPointer with a stride/offset based (presumably) on the stride/offset of baseInstance within a draw command.

If that’s what you’re doing, your rendering code doesn’t seem to match. The loop in that USE_DRAW_ID_BUFFER seems antithetical to this, as it’s writing a contiguous sequence of IDs extracted from the commands’ baseInstance. Furthermore, since your "draw_ID"s come from the indirect command buffer, this loop is entirely pointless, since you’re writing to a buffer that won’t be used.

Overall, your code seems a bit confused. What are you trying to do here? How do you know that the “draw_IDs” are “much more randoms”? And when you say “I produce a new buffer of commands together with new draw_IDs”, do you mean you create a new indirect buffer object every frame? If so, you probably need to tell your VAO that wants to read from the indirect buffer about that.


On another subject: your code is using names that violate the rules of C and C++. In C, names beginning with double-underscores are reserved for the implementation and are not allowed to be declared in user code. So your __draw_children function violates this rule. C++ is a bit more draconian in that it forbids use of any name containing double-underscores. And both forbid the use of names starting with an underscore followed by a capital letter.

So you should re-evaluate your naming conventions.

Sorry, when I copied my code, I edited it, and I messed it up: I had erroneously put the wrong code inside #if USE_DRAW_ID_BUFFER in the setup code. It’s fixed in the original message now.

A bit of explanation:
draw_children(parent) will populate the commands, including the baseInstance field with the draw_ID.

I tested both using a field of the command list and using a dedicated BO for draw_ID.
If the code happens to USE_DRAW_ID_BUFFER, I reuse this populated field to populate the draw_ID buffer.

…and, to reply to your other questions…

How do you know that the “draw_IDs” are “much more randoms”?

It’s because I change the scene. I have a single VAO, a single BO for coords, another one for transformation etc. When rendered, objects will get their vertices using a base offset, and other properties with a draw_ID that I use as index with SSBOs. I sometimes create new objects, without removing the older ones, as I may display them again later.
In the first frame, the drawn objects have been created and the command list that displays them uses a set of incrementing draw_IDs:

cmd1 baseInstance:1
cmd2 baseInstance:2
cmd3 baseInstance:3
cmd4 baseInstance:4

With this command list, the scene displays correctly.

When I redisplay the scene with newly created objects, and possibly not-displayed-anymore objects, I recreate a command list with “random” draw_IDs, in the sense of “Random Access Memory”: the draw_IDs in baseInstance are not sequential anymore, but rather something like:

cmd1 baseInstance:1
cmd2 baseInstance:2
cmd3 baseInstance:8
cmd4 baseInstance:14
cmd5 baseInstance:9

etc.
The first, still displayed, objects are correctly rendered, but not the new ones, those with drawIDs that may point elsewhere.

And when you say “I produce a new buffer of commands together with new draw_IDs”, do you mean you create a new indirect buffer object every frame?

no, I map the command buffer, modify it, and unmap it. There is no new buffer object creation.

When I do not use glMultiDrawElementsIndirect, and display objects one by one with the exact same buffers, but with a draw_ID I change using a uniform uint, the scene displays correctly.

So I think I misunderstood how glMultiDrawElementsIndirect is supposed to work…

I hope this is clearer.

I think I nailed it.

As I wrote above, I was trying to use draw_ID as an index in UBOs or SSBOs, in an arbitrary (non-sequential) fashion, as described in Surviving without gl_DrawID.

However, the IDs must not be as arbitrary as being greater than the number of instances, as per this explanation on the OpenGL wiki. I think I ran into the problem of draw_IDs exceeding instanceCount, which might have been turned into zeros all the way.

If I may have understood why it does not work with the baseInstance field trick, I still haven’t figure out why it doesn’t work with an attribute, I guess it has the same ‘limitation’.

I resorted to using a float UBO/SSBO (take care of alignment) and another indirection to transform the sequential draw_ID to a ‘shape_ID’ and everything works as expected now.

Best.