openglglslshadertesselation

Setting gl_TessLevel only for one output vertex?


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?

Solution

  • 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.