8
$\begingroup$

I have a sequence of compute shaders that generates an indexed mesh.The last one of those writes the generated indices like this:

void addTriangle (uint i0, uint i1, uint i2) {
    uint ic = atomicCounterIncrement(indirectIndexCount);
    meshIndices[ic*3+0] = i0;
    meshIndices[ic*3+1] = i1;
    meshIndices[ic*3+2] = i2;
}

After the mesh has been generated, it is drawn with glDrawElementsIndirect. The indirectIndexCount in the code above is an atomic_uint counter at position 0 inside the GL_DRAW_INDIRECT_BUFFER (see the struct called DrawElementsIndirectCommand). This counter is now obviously too small by a factor of three, since it was incremented only once for each triangle. Currently, I multiply it by 3 afterwards just before issuing the draw call.

(At the moment this is done by mapping the buffer and multiplying on the CPU, which is of course nonsense, but shows that the whole thing basically works. Everything is drawn correctly. I could do it with an invocation of a single 1x1x1x1x1x1 compute shader, but that seems only slightly less silly.)

How do I get rid of this extra multiplication step?

Since this seems to be an obvious problem whenever meshes with variable index count are generated, I guess there must be some easy solution that I'm overlooking?

$\endgroup$
4
  • 1
    $\begingroup$ Can you atomic-add 3 to the counter instead of incrementing it? $\endgroup$ Commented Oct 12, 2015 at 22:38
  • 1
    $\begingroup$ No, atomic counters can only be queried, incremented (by 1) or decremented (by 1). See the Article in the opengl wiki. They are not the same as atomic Add etc on Images. You only have 8 (or so) of them and they are supposedly much faster when accessed extremely often (like when generating (and thus counting) thousands of ... things.) $\endgroup$
    – cupe
    Commented Oct 12, 2015 at 23:21
  • $\begingroup$ Yeah, I guess you would have to turn it from an "atomic counter" to just a variable in an SSBO. Would be interesting to see if that's actually any slower (it might not be, depending on HW). Other than that, the only thing I can think of is to do like you said and run a compute shader to multiply the value by 3. $\endgroup$ Commented Oct 13, 2015 at 0:35
  • $\begingroup$ Hi cupe, is it possible to take a look to the code somewhere? Ps: are you checking also for multiple entries? $\endgroup$
    – elect
    Commented Mar 13, 2016 at 18:35

1 Answer 1

7
$\begingroup$

If you have access to glsl 4.3+ (or glsl ES 3.1) you can use atomicAdd

The next option is to use a barrier() after all vertices are generated in the compute and then multiplying the value in the counter:

main(){

    // generate vertices

    barrier();
    if(gl_localInvocationID == vec3(0)){
        indirectCount = atomicCounter(indirectIndexCount)*3;
    }
}

This emulates running another 1x1x1 compute shader to multiply the index.

Otherwise you can use a second atomic to hold the vertexcount:

void addTriangle (uint i0, uint i1, uint i2) {
    uint ic = atomicCounterIncrement(indirectIndex);
    meshIndices[ic*3+0] = i0;
    meshIndices[ic*3+1] = i1;
    meshIndices[ic*3+2] = i2;
    atomicCounterIncrement(indirectIndexCount);
    atomicCounterIncrement(indirectIndexCount);
    atomicCounterIncrement(indirectIndexCount);
}

Both indirectIndex and indirectIndexCount are initialized to 0 and indirectIndexCount is what is passed through to glDrawElementsIndirect.

$\endgroup$
2
  • $\begingroup$ I like the second idea because it makes clear that I need two different things: A way to atomically add triangles and a way to get the final index count. Doesn't have to be the same mechanism. And yes, I'll try atomicAdd as well. As Nathan suggested in a comment above, it would be interesting to see if it really is slower. I'll report back with the findings. $\endgroup$
    – cupe
    Commented Oct 13, 2015 at 14:33
  • 1
    $\begingroup$ Can confirm that atomicAdd is indeed not slower for this use case on nvidia kepler ( ~1.5k indices: 0.1ms - probably needs larger size for meaningful benchmark). Binding the indirect command buffer as an SSBO and writing to the uint at position 0 works fine. Sometimes one should just try the easy solutions first... $\endgroup$
    – cupe
    Commented Oct 13, 2015 at 22:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.