2

在互联网上,我找到了一些 TCS 代码示例,其中gl_TessLevel*仅为一个输出补丁顶点设置变量

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

而不仅仅是

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

无论有无条件检查,它的工作原理都相似,但我在 OpenGL wiki 上没有找到任何关于这种用法的信息。

如果从逻辑上思考,只在一次 TCS 调用中设置这些变量应该是可以的,而将它们设置为基于gl_InvocationID. 所以我的问题是:

  1. 这种设置方式gl_TessLevel*是否正确,是否会在某些平台上导致错误或崩溃?
  2. 如果它是正确的,它应该一直使用吗?是地道的吗?
  3. 最后,这两个片段如何影响性能?由于分支,第一个片段可能会降低性能吗?第二个片段是否会导致后续管道阶段的冗余和/或空闲调用,也会降低性能?
4

1 回答 1

3

您在这里看到的是着色器作者尝试建立一个类似于其他原始类型使用的激发顶点的约定。

OpenGL 着色语言 4.50  -  2.2 曲面细分控制处理器 - p。7

镶嵌控制着色器调用大多独立运行,具有未定义的相对执行顺序。 但是,可以使用内置函数barrier()通过同步调用来控制执行顺序,从而有效地将曲面细分控制着色器执行划分为一组阶段。

如果一个调用在同一阶段的任何时候读取另一个调用写入的逐顶点或逐补丁属性,或者如果两个调用尝试将不同的值写入同一阶段中的同一逐补丁输出,则镶嵌控制着色器将获得未定义的结果。单相。

目前尚不清楚着色器伪代码是否foo以及bar在所有 TCS 调用中是否一致。如果不是,则由于未定义的相对顺序,第二个着色器片段会调用未定义的行为。

任意决定第一个调用是唯一允许写入每个补丁属性的调用解决了这个问题,并且类似于第一个顶点激发约定。由于所有调用都知道补丁顶点的数量,因此可以很容易地实现最后一个顶点约定。

foo但是,如果您知道并且bar保持不变,那么这些都不是必需的。

于 2016-08-11T12:55:05.280 回答