Apple 在他们的着色器最佳实践中表示,尽可能避免分支,尤其是在着色器中计算的值上进行分支。所以我用if
内置clamp()
函数替换了一些语句。我的问题是 , clamp()
,min()
和max()
可能更有效,还是它们只是简单地扩展为if
块的便利(即宏)函数?
我意识到答案可能取决于实现。在任何情况下,这些函数显然更干净,并且明确了编译器可以使用的意图。
Apple 在他们的着色器最佳实践中表示,尽可能避免分支,尤其是在着色器中计算的值上进行分支。所以我用if
内置clamp()
函数替换了一些语句。我的问题是 , clamp()
,min()
和max()
可能更有效,还是它们只是简单地扩展为if
块的便利(即宏)函数?
我意识到答案可能取决于实现。在任何情况下,这些函数显然更干净,并且明确了编译器可以使用的意图。
从历史上看,GPU 支持每片段指令MIN
的MAX
时间比支持任意条件分支的时间要长得多。桌面 OpenGL 中的一个例子是GL_ARB_fragment_program
扩展(现在被 GLSL 取代),它明确声明它不支持分支,但它确实提供了指令MIN
以及MAX
一些其他条件指令。
min()
考虑到这些操作的普遍性,max()
并且clamp()
在着色器中,我非常有信心所有 GPU 仍将具有用于这些操作的专用硬件。规范不能保证这一点,因为实现可以优化代码,但它认为合适,但在现实世界中,您应该使用 GLSL 的内置函数而不是滚动自己的函数。
唯一的例外是如果您的条件被用于避免大量额外的片段处理。在某些时候,分支的成本将低于在分支中运行所有代码的成本,但这里的平衡将非常依赖于硬件,您必须进行基准测试以查看它是否真的有助于您的应用程序目标硬件。这就是我的意思:
void main() {
vec3 N = ...;
vec3 L = ...;
float NDotL = dot(N, L);
if (NDotL > 0.0)
{
// Lots of very intensive code for an awesome shadowing algorithm that we
// want to avoid wasting time on if the fragment is facing away from the light
}
}
仅仅钳制NDotL
到 0-1,然后总是处理每个片段上的影子代码,只是为了乘以你的最终影子项,如果最初 <= 0,NDotL
那么会浪费很多精力,理论上我们可以通过分支来避免这种开销。NDotL
这种事情并不总是能赢得性能的原因是它非常依赖于硬件如何实现着色器分支。