通过制服打破 GLSL 循环

Breaking out of GLSL loops via a uniform

我想在浏览器中计算 GLSL 中浮点矩阵的每行最小值,大约 1000 行,4000 列。

基于之前的答案(参见 ),我使用了 for 循环。但是我想对上限使用统一的,这在 WebGL GLSL ES 1.0 中是不可能的。这是因为行的长度是在片段着色器之后定义的,我想避免弄乱 #DEFINEs.

所以我发现这个解决方法 - 固定周期长度和由制服定义的 if/break - 工作正常:

#define MAX_INT 65536
  
void main(void) {
  float m = 0.0;
  float k = -1.0;
  int   r = 40;

  for(int i = 0; i < MAX_INT; ++i){
    float ndx = floor(gl_FragCoord.y) * float(r) + float(i); 
    float a = getPoint(values, dimensions, ndx).x;
    m = m > a ? m : a;
    if (i >= r) { break; }
  };
}

现在的问题是:这有很大的缺点吗?是不是我在做一些奇怪的事情并且遗漏了什么?

我相信,但我不完全确定,唯一的风险是 driver/gpu 仍然会进行长循环。

举个例子想象这个循环

uniform int limit;

void main() {
  float sum = 0;
  for (int i = 0; i < 3; ++i) {
    sum += texture2D(tex, vec2(float(i) / 3, 0)).r;
    if (i >= limit) {
      break;
    }
  }
  gl_FragColor = vec4(sum);
}

可以re-written被driver这样

uniform int limit;

void main() {
  float sum = 0;
  for (int i = 0; i < 3; ++i) {
    float temp = texture2D(tex, vec2(float(i) / 3, 0)).r;
    sum += temp * step(float(i), float(limit));
  }
  gl_FragColor = vec4(sum);
}

没有分支机构。我不知道这样的 drivers/gpus 是否仍然存在没有条件,但是循环需要 const 整数表达式的想法是这样可以删除分支 and/or 循环 un-rolled在编译时,如果 driver/GPU 决定做任何一个。

uniform int limit;

void main() {
  float sum = 0;
  sum += step(float(0), float(limit)) * texture2D(tex, vec2(float(0) / 3, 0)).r;
  sum += step(float(1), float(limit)) * texture2D(tex, vec2(float(1) / 3, 0)).r;
  sum += step(float(2), float(limit)) * texture2D(tex, vec2(float(2) / 3, 0)).r;
  gl_FragColor = vec4(sum);
}

此外,顺便说一句,上面的具体示例不会输出任何内容,因此大多数 driver 会将整个着色器变成 no-op。