PDA

View Full Version : Primitive Restart Index inside Geometry Shaders / How to detect?



Pivonka
03-01-2018, 11:19 AM
I have some misunderstanding of primitive restart index value use with adjacent primitives inside geometry shaders. An example below will show what I mean:

Motivation:


float vtx1[] = {{-1.0, 0.0, 0.0}, {+1.0, 0.0, 0.0}, {0.0, +1.0, 0.0}};
unsigned int idx1[] = {0, 0xFFFFFFFF, 1, 0xFFFFFFFF, 2, 0xFFFFFFFF};

//
// ... all necessary is made here: buffers created, enabled and bound, shaders loaded, compiled and used, attributes pointed and enabled.
//

glDisable(GL_PRIMITIVE_RESTART); // i don't need primitive restart, i need just some index value off the scope
glPrimitiveRestartIndex(0xFFFFFFFF);
glDrawElements(GL_TRIANGLES_ADJACENCY, ...);

//
// ... and here we go - the triangle on the screen
//


Question:


#version 330 core

layout (triangles_adjacency) in;
layout (triangle_strip, max_vertices = 3) out;

void main()
{
//
// minimal geometry shader for triangles
gl_Position = gl_in[0].gl_Position;
EmitVertex();
gl_Position = gl_in[2].gl_Position;
EmitVertex();
gl_Position = gl_in[4].gl_Position;
EmitVertex();
EmitPrimitive();

//
// ... and NOW there is the QUESTION: what values are in gl_in[1].*, gl_in[3].* and gl_in[5].*?
//
// And how can I detect that inside the GS the primitive restart index value was used on these vertices?
//
}


Am I missing some important information in OpenGL specs? I can't find no explanation for this ... here (https://stackoverflow.com/questions/19036533/opengl-triangle-adjacency-indexing) is similar discussion, but no proper answer.

Also needs to be noted: please do not answer that I should use GL_TRIANGLES instead GL_TRIANGLES_ADJACENCY when drawing single triangle. This is just the simplified illustrative example and there are many serious examples in real world: such a situation occurs on the edge of every triangle mesh that you run out of excessive neighboring vertices to the triangle you are right drawing ...

Thank you for your suggestions!

arekkusu
03-01-2018, 11:46 AM
With primitive restart off, you're asking GL to dereference element 0xFFFFFFFF from each of your enabled attributes.
If your vertex arrays are large enough (i.e. gigabytes) to actually include this memory, then that is what you'll have in the shader.
If they are not, you're dereferencing outside of your allocated memory. In a non-robust context, this will crash if you are lucky. In a robust context it might (depending on the flavor of robustness) result in all zeros, but it won't crash.

As for detecting this index, gl_VertexID isn't visible to geometry shaders, but you can pass it as a flat varying from the vertex shader.

GClements
03-01-2018, 03:16 PM
I have some misunderstanding of primitive restart index value use with adjacent primitives inside geometry shaders.
Primitive restart works the same with any mode: it causes a single draw command to behave like multiple draw commands for the purposes of grouping indices into primitives.

It isn't much use with modes which don't re-use indices for multiple primitives, i.e. anything other than line strip, line loop, triangle strip, triangle fan or (in the compatibility profile) polygons. For other modes, the only significance is that it will cause the preceding vertices to be ignored if there aren't enough of them to form a complete primitive.



Am I missing some important information in OpenGL specs? I can't find no explanation for this ... here (https://stackoverflow.com/questions/19036533/opengl-triangle-adjacency-indexing) is similar discussion, but no proper answer.

The primitive restart index is almost a red herring here. I believe that he's suggesting to use it simply because it's guaranteed not to refer to an actual vertex.

For triangles with adjacency, the implementation will pass six vertices to the geometry shader, obtained by dereferencing six consecutive indices in the element array. If you need to support a "null" vertex, I'd follow arekkusu's suggestion of passing gl_VertexID as a vertex shader output, and either using vertex zero or some other index specified by a uniform. Either way, you can't avoid having the implementation dereference that index, so you'll have to include a dummy vertex in the attribute arrays.

If primitive restart is enabled, you can't use the primitive restart index for this purpose, because it will be used for primitive restarting (your vertex shader will never see gl_VertexID equal to the primitive restart index). And if it isn't enabled, there's no point in setting a primitive restart index, because it won't have any effect (its value isn't visible to shaders, enabled or not).

Pivonka
03-01-2018, 03:29 PM
This explanation sounds good. Is this documented somewhere?

I thought - and that was only and only my impression - that when you only set the glPrimitiveRestartIndex(somevalue), then such as somevalue is automatically excluded from dereferencing (regardless its enabled/disabled state), which makes all the mechanism safe. This my impression stemmed from multiple advices (in internet discussions) to conserve right these unused excessive vertices in adjacent primitives - one such advice is referenced in my original post above.

It has never happened to me that such as code like that I demonstrated above crashed. Perhaps because i have never touched these odd items gl_in[1,3,5].* in my GS-) I have always used only evens gl_in[2,4,6].*

Thank you very much for hint with gl_VertexID, I will try - I'm just curious how my 0xFFFFFFFF will look like inside shader, when gl_VertexID is declared as an int, but indices can be only unsigned types (GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT or GL_UNSIGNED_INT).

Alfonse Reinheart
03-01-2018, 06:21 PM
Is this documented somewhere?

Yes. (https://www.khronos.org/opengl/wiki/Primitive_Restart)


I thought - and that was only and only my impression - that when you only set the glPrimitiveRestartIndex(somevalue), then such as somevalue is automatically excluded from dereferencing (regardless its enabled/disabled state), which makes all the mechanism safe.

If that were true, why would there be an enable/disable state at all?

Pivonka
03-01-2018, 11:42 PM
If that were true, why would there be an enable/disable state at all?

For primitive restarting itself. If enabled, then your special index value even doesn't make its way into shaders as it is discarded and new primitive starts again with next index value. When disabled, I thought that no restart occurs but your special index value has still an exclusive meaning (i.e. the null index which doesn't dereference to any location in the attribute buffers) which gets into shaders to signalize that fact to your shader code. I learned this from few internet discussions where this practice was recommended (like that one from my original post) - I haven't find it nowhere in OpenGL specs.

So what is the official documented and safe way how to cope with non-existent excessive vertices you should have provided to the pipeline when processing primitives with adjacency? Because this situation inevitably happens in every triangle patch or line strip ... some workaround is suggested by GClements above but he also states there is no guarantee of reliability ...

GClements
03-02-2018, 04:44 AM
For primitive restarting itself. If enabled, then your special index value even doesn't make its way into shaders as it is discarded and new primitive starts again with next index value. When disabled, I thought that no restart occurs but your special index value has still an exclusive meaning (i.e. the null index which doesn't dereference to any location in the attribute buffers) which gets into shaders to signalize that fact to your shader code. I learned this from few internet discussions where this practice was recommended (like that one from my original post) - I haven't find it nowhere in OpenGL specs.
You didn't find it because it isn't in there. If GL_PRIMITIVE_RESTART isn't enabled (or if it is, but GL_PRIMITIVE_RESTART_FIXED_INDEX is also enabled), the primitive restart index has no significance.

If primitive restart is disabled and an index in the element array happens to equal the primitive restart index, the index will be dereferenced and the corresponding vertex attributes will be transferred to the shader (so long as the index doesn't lie outside of any attribute array).


So what is the official documented and safe way how to cope with non-existent excessive vertices you should have provided to the pipeline when processing primitives with adjacency? Because this situation inevitably happens in every triangle patch or line strip ... some workaround is suggested by GClements above but he also states there is no guarantee of reliability ...
There is no way to avoid supplying attribute data for a vertex. You will have to include an extra "dummy" vertex in the attribute arrays. You can then compare gl_VertexID to a uniform to identify the case where a vertex is the dummy vertex.

Alfonse Reinheart
03-02-2018, 07:23 AM
I learned this from few internet discussions where this practice was recommended (like that one from my original post)

You misunderstood what was being said. The Stack Overflow answer (https://stackoverflow.com/a/19040791/734069) was not saying that `glPrimitiveRestartIndex` had any special significance on its own; it was simply showing off what function you need to call in order to provide a restart index. Indeed, it very clearly says:


I should also point out that index-based primitive restart is a feature that can be enabled / disabled, and it starts out disabled. So simply setting the restart index is not enough for it to actually do anything.

Emphasis added. So that person never suggested that the enable was not useful; you just didn't read far enough. It happens.

That being said, that answer is quite incorrect. Primitive restart will not do what the OP of that question wants. It's not "ignore vertex"; it's "restart primitive".

Pivonka
03-02-2018, 09:10 AM
Ok, I am on my way to add an extra dummy vertex to all my attribute arrays, send its offset thru brand new integer uniform into my GS and test all incoming vertices against this value - this is exactly what I need.

Thank you very much for your patience, also I have to apologize for me not being careful enough when reading other dev forums - now I think I understand how primitive restart index works and that I was making this mistake for years, thanks God with no single crash for this reason ...

Anyhow: don't you think that exactly this extended functionality of primitive restart index I originally expected by my mistake and which in reality doesn't exist, wouldn't be in fact a nice benefit for developers and shouldn't we ask ARB to think about it as some 'nice-to-have' feature in some future OpenGL revision? It would save developers a lot of effort (at least to me-) ...

GClements
03-02-2018, 10:27 AM
Anyhow: don't you think that exactly this extended functionality of primitive restart index I originally expected by my mistake and which in reality doesn't exist, wouldn't be in fact a nice benefit for developers and shouldn't we ask ARB to think about it as some 'nice-to-have' feature in some future OpenGL revision?
Ultimately the choice of what features OpenGL supports isn't up to the ARB, but the video hardware manufacturers. OpenGL merely provides an interface to the functionality provided by the hardware. In order for glDrawElements() to (efficiently) support a "null vertex", the hardware would need to support that.