我经常看到这样的示例 GLSL 代码:
vec4 sum = texture2D(texture, uv) * 4.0;
sum += texture2D(texture, uv - halfpixel.xy * offset);
sum += texture2D(texture, uv + halfpixel.xy * offset);
sum += texture2D(texture, uv + vec2(halfpixel.x, -halfpixel.y) * offset);
sum += texture2D(texture, uv - vec2(halfpixel.x, -halfpixel.y) * offset);
这不会在编译后的代码中引入依赖链吗?即使有也没关系吗?
编辑:
我希望我正确地总结了这一点。现代 CPU 和 GPU 具有调度程序,可以在同一时钟周期内同时运行某些指令(例如 Intel 上的
ADD
或 FADD
)。仅当操作不是要求它们分开的依赖链的一部分时,这才有可能。
这是一个依赖链的示例,在调度程序可以继续执行下一个语句之前,需要计算每个加法:
sum2 = a + b;
sum2 += c; // needs to know a+b
sum2 += d; // needs to know sum2+c
sum2 += e; // needs to know sum2+d
sum2 += f; // needs to know sum2+e
此代码是等效的(尽管它至少需要两个额外的累加器,并且使用浮点数可能会给出与上面代码不同的答案),但在我们到达最后一行之前不存在依赖链(有两个)。因此,在允许的 CPU/GPU 上,可以安排前三行同时执行:
a1 = a + b;
a2 = c + d;
a3 = e + f;
sum1 = a1 + a2 + a3;
就我问题顶部的代码而言,我不知道它是否重要。如果纹理查找不能并行完成,那么可能就不能。如果可以的话,那么也许?这就是我问的原因。
让我们考虑一下您的添加示例:
sum2 = a + b;
sum2 += c; // needs to know a+b
现在,应该问一个问题:什么“需要知道a+b”?因为这个表达式包含了几个步骤。是的,它与
+=
进行 sum2
运算,但在此之前,它必须首先计算表达式 c
和 sum2
。
从硬件的角度来看,评估 c
基本上是无操作的,但这并不重要。毕竟,语句
c += sum2;
同样依赖于
a+b
。这是因为计算
sum2
需要首先计算程序中该点用于生成
sum2
的所有表达式。正是对
sum2
的评估使得语句具有依赖性,而不是对
c
的评估。从技术上讲,如果您使用
sum2
而不是
+=
进行纯赋值操作,它就不会具有依赖性(因为它会覆盖以前的值),因此单独求值不会产生依赖性,但现在不用介意。重点是,当我们查看所有这些纹理表达式时,我们可以看到
texture2d
调用独立于求和操作且彼此独立。因此,实现可以按照自己想要的任何顺序自由地评估它们。这有关系吗?不;纹理获取并不便宜,并且重新排序这些东西并不会让它们变得更便宜。四个连续向量相加的成本是您最不需要担心的。