0

I am trying to control behavior of fragment shader by calculating vertex count in geometry shader so that if I have a vertex stream of 1000 triangles ,when the count reaches 500 I set some varying for fragment shader which signals that the later must switch its processing.To count total vertices(or triangles) processed I use Atomic counter in geometry shader.I planned to do it in vertex shader first,but then I read somewhere that because of vertex caching counter won't increment on each vertex invocation.But now it seems that doing it in geometry shader doesn't execute the count precisely either.

In my geometry shader I am doing this:

layout(triangles) in;

layout (triangle_strip ,max_vertices = 3) out; 

layout(binding=0, offset=0) uniform atomic_uint ac;

out flat float isExterior;

void main()
{
    memoryBarrier();
    uint counter = atomicCounter(ac);
    float switcher = 0.0;
    if (counter >= exteriorSize)
    {
        switcher = 2.0;
    }
    else
    {
        atomicCounterIncrement(ac);
        atomicCounterIncrement(ac);
        atomicCounterIncrement(ac);
    }
    isExterior = switcher;

    // here just emitting primitive....  

exteriorSize is a uniform holding a number equal to number of vertices in an array.When I read out the value of counter on CPU it never equals to exteriorSize.But it is almost 2 times smaller than it.Is there a vertex caching in geometry stage as well?Or am I doing something wrong?

Basically what I need is to tell fragment shader: "after vertex number X start doing work Y.As lont as vertex number is less than X do work Z" And I can't get that exact X from atomic counter even though I increment it up till it reach that limit.

UPDATE:

I suspect the problem is with atomic writes synchronization.If I set memoryBarrier in different places the counter values change.But I still can't get it return the exact value that equals to exteriorSize.

UPDATE 2:

Well,I didn't figure out the issue with atomic counter synchronization so I did it using indirect draw . Works like a charm.

Michael IV
  • 11,016
  • 12
  • 92
  • 223

1 Answers1

1

The geometry shader executes per-primitive (triangle in this case), whereas the vertex shader executes per-vertex, almost. Using glDrawElements allows vertex results to be shared between triangles (e.g. indexing 0,1,2 then 0,2,3 uses 0 and 2 twice: 4 verts, 2 triangles and 6 references). As you say, a limited cache is used to share the results, so if the same vertex is referenced a long time later it has to be recomputed.

It looks like there's a potential issue with updates to the counter occurring between atomicCounter and atomicCounterIncrement. If you want an entire section of code like this to work, it needs to be locked. This can get very slow depending on what you're locking.

Instead, it's going to be far easier to always call atomicCounterIncrement and potentially allow ac to grow beyond exteriorSize.

AFAIK reading back values from the atomic counter buffer should stall until the memory operations have completed, but I've been caught out not calling glMemoryBarrier between passes before.

It sounds like exteriorSize should be equal to the number of triangles and not vertices if this is executing in the geometry shader. If instead you do want per-vertex processing, then maybe change to GL_POINTS or save the vertex shader results using the transform feedback extension and then drawing triangles from that (essentially doing the caching yourself but with a buffer that holds everything). If you use glDrawArrays or never reuse vertices then a standard vertex shader should be fine.

Lastly, calling atomicCounterIncrement three times is a waste. Call once and use counter * 3.

Community
  • 1
  • 1
jozxyqk
  • 16,424
  • 12
  • 91
  • 180
  • memoryBarrier doesn't help. also memoryBarrierAtomicCounter doesn't help either. You note on vertex vs triangle count is right.I tried it all.Let's say exteriorSize equal 620 so incrementing the counter only once per execution till it reaches 620 returns a much bigger number when I read it back to CPU (smth like 900+ ) So I suspect that the counter synch in geom shader happens "at will" . – Michael IV Jul 09 '14 at 10:12
  • Basically what I need is to tell fragment shade: "after vertex number X start doing work Y" And I can't get that exact X from atomic counter even though I increment it up till it reach that limit. – Michael IV Jul 09 '14 at 10:17
  • @MichaelIV `memoryBarrier` is the GLSL call, [`glMemoryBarrier`](https://www.opengl.org/sdk/docs/man/docbook4/xhtml/glMemoryBarrier.xml) is a host call. It sounds like there might still be confusion over vertex count, triangle count and vertex reference count. I've never seen GL atomic counters get the wrong result. Atomics aren't cheap (branching can be bad too). How about simply splitting this into two draw calls? – jozxyqk Jul 09 '14 at 10:21
  • That's the problem,I am trying to squeeze it from 2 to 1 draw call due to some special need.I know that glMemoryBarrier is host call and I dont care about it as I need to read the output values on host only for debug.But what I am saying is that "if (counter >= exteriorSize)" doesn't happen at a time.Because if it were I would be getting on the host the value equal to exteriorSize – Michael IV Jul 09 '14 at 10:23
  • Maybe indirect draw can help here?But how in such a case fragment shader will know to do something special for the stuff of second instance... – Michael IV Jul 09 '14 at 10:46
  • Oh, there's also [`gl_VertexID`](https://www.opengl.org/sdk/docs/man/html/gl_VertexID.xhtml) and [`gl_PrimitiveID`](https://www.opengl.org/sdk/docs/man/html/gl_PrimitiveID.xhtml) which are probably far more helpful. Should have thought of them sooner. – jozxyqk Jul 09 '14 at 10:56
  • Solved it with indirect drawing. – Michael IV Jul 09 '14 at 17:51