2

On the Internet I found some examples of TCS code, where gl_TessLevel* variables are set only for one output patch vertex

// first code snippet
if ( gl_InvocationID == 0 ) // set tessellation level, can do only for one vertex
{
    gl_TessLevelOuter [0] = foo
    gl_TessLevelOuter [1] = bar;
}

instead of just

// second code snippet
gl_TessLevelOuter [0] = foo;
gl_TessLevelOuter [1] = bar;

It works similarly with and without condition checking, but I didn't find anything about such usage on OpenGL wiki.

If to think logically, it should be OK to set these variables only in one TCS invocation, and it would be weird to set them to different values based on gl_InvocationID. So my questions are:

  1. Is this way of setting gl_TessLevel* correct and may it cause errors or crashes on some platforms?
  2. If it's correct, should it be used always? Is it idiomatic?
  3. And finally, how do both snippets affect performance? May the first snippet slow-down performance due to branching? May the second snippet cause redundant and/or idle invocations of subsequent pipeline stages, also slowing down performance?
Sergey
  • 7,985
  • 4
  • 48
  • 80
  • It is qualified with `patch`, so it does only need to be set once per-patch primitive, but I think this is invoking undefined behavior because among other things it assumes the first vertex is [provoking](https://www.opengl.org/wiki/Primitive#Provoking_vertex). The comment definitely is not true -- this can be done for any vertex, it just isn't meaningful if each vertex has a different value. – Andon M. Coleman Aug 11 '16 at 10:37
  • @AndonM.Coleman I suppose the comment says that it can be done for any vertex, but it's also valid to do it for only one vertex in a patch, i.e. they could check `gl_InvocationID == 21` instead of `gl_InvocationID == 0` and yield the same result, assuming number of output vertices is greater than 21. – Sergey Aug 11 '16 at 10:45
  • @AndonM.Coleman What do you mean under "if each vertex has a different value"? Primitive generation stage (next to TCS) generates one abstract patch per one TCS input patch, not per TCS output vertex. So setting `gl_TessLevel*` to different values for different vertices of one TCS input patch seems to be nonsencial. – Sergey Aug 11 '16 at 10:47
  • @AndonM.Coleman: Can you please refer to the section of the spec which states that a provoking vertex even has meaning for the tessellation stages? After what I've discovered so far, there is no provoking vertex without a primitive and there are no primitives after before the TES is invoked. (Not even sure about if the provoking vertex is meaningful in a TES either.) BTW, the wiki states "Patches do not have a provoking vertex [..]". – thokra Aug 11 '16 at 11:17
  • It has no meaning, I merely mean to say this is analogous to provoking vertices for other primitives. One invocation defines this characteristic, but it's not an error to write it for other invocations; just meaningless if you try to assign a different value on each invocation. – Andon M. Coleman Aug 11 '16 at 11:21

1 Answers1

3

What you are seeing here is an attempt by the shader's author to establish a convention similar to provoking vertices used by other primitive types.

OpenGL Shading Language 4.50 - 2.2 Tessellation Control Processor - p. 7

Tessellation control shader invocations run mostly independently, with undefined relative execution order. However, the built-in function barrier() can be used to control execution order by synchronizing invocations, effectively dividing tessellation control shader execution into a set of phases.

Tessellation control shaders will get undefined results if one invocation reads a per-vertex or per-patch attribute written by another invocation at any point during the same phase, or if two invocations attempt to write different values to the same per-patch output in a single phase.

It is unclear given the shader psuedo-code whether foo and bar are uniform across all TCS invocations. If they are not, the second shader snippet invokes undefined behavior due to the undefined relative ordering.

Arbitrarily deciding that the first invocation is the only one that is allowed to write the per-patch attribute solves this problem and is analogous to a first-vertex provoking convention. A last-vertex convention could just as easily be implemented since the number of patch vertices is known to all invocations.

None of this is necessary if you know foo and bar are constant, however.

Andon M. Coleman
  • 42,359
  • 2
  • 81
  • 106
  • To expand on this, of course if you have different calculations for the different vertices, it will be more efficient to let each invocation do one of these calculations and write to one output variable instead of letting one invocation do all the work. – karyon Aug 11 '16 at 17:56