gl_PrimitiveID in fragment shader with tessellation enabled

Hello,

I am trying to visualize my tessellations by using a lookup-table inside fragment shader which is indexed using a gl_PrimitiveID like this:


precision mediump float;
layout(location = 0) out vec4 color;

// Color Look-up table of 8 entries
vec3 color_lut[8] = vec3[]( vec3( 0.0, 0.0, 0.0 ),
vec3( 0.5, 0.5, 0.5 ),
vec3( 1.0, 0.5, 0.5 ),
vec3( 0.5, 1.0, 0.5 ),
vec3( 0.5, 0.5, 1.0 ),
vec3( 0.5, 1.0, 1.0 ),
vec3( 1.0, 0.5, 1.0 ),
vec3( 1.0, 1.0, 0.5 ) );

void main() {
int prim_color_index = gl_PrimitiveID % 8;
    color = vec4(color_lut[prim_color_index], 1.0f);
}

But I am not seeing the result as expected and only see a single color over my original untessellated triangle patch. I then checked if gl_PrimitiveID varies for each tessellated triangle and that wasn’t the case. I am getting a 0 for all of them. I was under the impression that gl_PrimitiveID would be unique for each tessellated triangle. I then checked the OpenGL Wiki and it says that gl_PrimitiveID is supposed to be unique for each primitive: Built-in Variable (GLSL) - OpenGL Wiki

gl_PrimitiveID : This value is the index of the current primitive being rendered by this drawing command. This includes any Tessellation applied to the mesh, so each individual primitive will have a unique index. However, if a Geometry Shader is active, then the gl_PrimitiveID is exactly and only what the GS provided as output. Normally, gl_PrimitiveID is guaranteed to be unique, so if two FS invocations have the same primitive ID, they come from the same primitive. But if a GS is active and outputs non-unique values, then different fragment shader invocations for different primitives will get the same value. If the GS did not output a value for gl_PrimitiveID, then the fragment shader gets an undefined value.

So it looks like my GPU isn’t following the OpenGL spec well?

[QUOTE=Orbb256;397936]
I then checked if gl_PrimitiveID varies for each tessellated triangle and that wasn’t the case. I am getting a 0 for all of them. I was under the impression that gl_PrimitiveID would be unique for each tessellated triangle. I then checked the OpenGL Wiki and it says that gl_PrimitiveID is supposed to be unique for each primitive: Built-in Variable (GLSL) - OpenGL Wiki

So it looks like my GPU isn’t following the OpenGL spec well?[/QUOTE]
It isn’t following what’s on the wiki, but the specification says nothing about tessellation.

It does say that

For polygons drawn in point or line mode, the primitive ID counter is incremented only once, even though multiple points or lines may be drawn.

Which implies that it’s related to the primitives generated by the drawing command rather than to what gets rasterised. On the other hand, it also says:

The first primitive generated by a drawing command is numbered zero, and the primitive ID counter is incremented after every individual point, line, or polygon primitive is processed.

Note that it doesn’t say “patches”. In spite of the fact that tessellation control and evaluation shaders get passed gl_PrimitiveID as an input. Bear in mind that tessellation control shaders are invoked a fixed number of times per patch, and tessellation evaluation shaders are invoked for each generated vertex, so its unclear what gl_PrimitiveID should refer to in that context.

If you want unique IDs for primitives resulting from tessellation, I think you’re going to need to use a geometry shader.

That seemed to be the case but the spec specifically says that gl_PrimitiveID should contain the number of primitives processed by the rasterizer. I’m looking at OpenGL ES 3.2 spec Chapter 14, Section 14.2.2 Page 374 :

“If a geometry shader is active, the built-in variable gl_PrimitiveID contains the ID value emitted by the geometry shader for the provoking vertex. If no geometry shader is active, gl_PrimitiveID contains the number of primitives processed by the rasterizer since the last drawing command was called. The first primitive generated by a drawing command is numbered zero, and the primitive ID counter is incremented after every individual point, line, or polygon primitive is processed.”

Since HW Rasterizers typically only deal with simple geometries like points, lines and triangles; this seems to suggest that the primitive ID should be unique for all primitives which come out of the rasterizer and into the fragment shader.

I know what it says. My point is that the specification is fairly vague in this area, and where it’s clear it seems to contradict itself. E.g. if it intends “primitive” to mean the entities processed by the “rasterization” part of the specification (§14.4-§14.6 in the 4.6 spec), then the language regarding “polygons drawn in point or line mode” wouldn’t be there (each edge or vertex would get a distinct gl_PrimitiveID value). And reading the specifications for the extension on which tessellation is based doesn’t add anything.

To the extent that the specification contradicts the observed behaviour: if this is eventually addressed, it’s entirely likely that this will end up being viewed as a bug in the specification, with the specification changing to match the observed behaviour. So if you need gl_PrimitiveID to have different values for different triangles generated by tessellation, I’d look into using a geometry shader to set it. I wouldn’t hold your breath waiting for the implementation to change.

Thanks for your answer. Let me file this issue on Khronos bugzilla and see what happens. Your opinion gives me confidence that I wasn’t reading the spec wrong :smiley:

I wouldn’t hold my breath either for implementations to change any time soon but atleast the spec should be more consistent in what it means.

Bugzilla is gone, best to post on Github under the Khronos Organization The Khronos Group · GitHub

Specifically, API bugs go in this repo. GLSL bugs go in this repo.

1 Like

Just found this by accident by browsing through an email regarding the issues to be discussed in the next Khronos meeting. [link removed]

I just added a comment to that summarizing the discussions in this thread.

Apparently, you need some form of membership to reach that area. Khronos’s GitHub issue trackers are public.

That one has more issues and seems to be more active than the github one. I don’t know if there’s a way to connect them or it’s supposed to be that way.

If this is eventually addressed, it’s entirely likely that this will end up being viewed as a bug in the specification, with the specification changing to match the observed behaviour.

Adding weight to this is the Vulkan definition of PrimitiveID. That definition never mentions “rasterized”, so its use of the term “processed” pretty much has to refer to what gets fed into the system, not what gets generated by the system.

And since nobody else seemed interested in doing so, I opened a public issue on the subject.

1 Like