我想知道着色器内的“If-statements”(顶点/片段/像素......)是否真的会降低着色器性能。例如:
使用它更好吗:
vec3 output;
output = input*enable + input2*(1-enable);
而不是使用这个:
vec3 output;
if(enable == 1)
{
output = input;
}
else
{
output = input2;
}
在另一个论坛上有一个关于那个的讨论(2013):http://answers.unity3d.com/questions/442688/shader-if-else-performance.html这里有人说,If语句对于着色器的性能真的很糟糕。
同样在这里他们谈论if / else语句(2012)中有多少:https://www.opengl.org/discussion_boards/showthread.php/177762-Performance-alternative-for-if-(-)
也许硬件或着色器编译器现在更好,他们修复了这个(可能不存在)性能问题。
编辑:
在这种情况下,这里假设enable是一个统一变量,它总是设置为0:
if(enable == 1) //never happens
{
output = vec4(0,0,0,0);
}
else //always happens
{
output = calcPhong(normal, lightDir);
}
我想在这里我们在着色器中有一个分支,它可以减慢着色器的速度。那是对的吗?
制作2个不同的阴影是否更有意义,比如一个用于其他阴影,另一个用于if部分?
什么是着色器甚至可能使if
声明性能问题?它与着色器的执行方式以及GPU从中获得巨大的计算性能有关。
单独的着色器调用通常并行执行,同时执行相同的指令。他们只是在不同的输入值集上执行它们;他们分享制服,但他们有不同的内部登记。所有执行相同操作序列的着色器组的一个术语是“波前”。
任何形式的条件分支的潜在问题是它可以搞砸所有这些。它导致波前的不同调用必须执行不同的代码序列。这是一个非常昂贵的过程,必须创建一个新的wavefront,将数据复制到它上面等。
除非......它没有。
例如,如果条件是wavefront中每次调用所采用的条件,则不需要运行时分歧。因此,if
的成本只是检查条件的成本。
所以,假设你有一个条件分支,让我们假设wavefront中的所有调用都将采用相同的分支。在这种情况下,表达式的性质有三种可能性:
uniform
值)。但是在编译时不会知道表达式的值。所以编译器可以静态地确定波前永远不会被这个if
破坏,但是编译器不知道将采用哪个分支。不同的硬件可以处理不同的分支类型而不会发散。
此外,即使不同波前采用条件,编译器也可以重新构造代码以不需要实际的分支。你给出了一个很好的例子:output = input*enable + input2*(1-enable);
在功能上等同于if
语句。编译器可以检测到正在使用if
来设置变量,从而执行双方。这通常是针对树枝体很小的动态条件的情况。
几乎所有的硬件都可以处理var = bool ? val1 : val2
而不必分歧。这可能是在2002年。
由于这非常依赖于硬件,因此......取决于硬件。然而,可以看到某些硬件时代:
在那里,它有点狂野的西部。 NVIDIA针对此类硬件的编译器因检测此类情况而臭名昭着,并且每当您更改影响此类情况的制服时,实际上都会重新编译着色器。
一般来说,这个时代约有80%的“从不使用if
声明”来自。但即使在这里,也不一定如此。
您可以期待静态分支的优化。您可以希望静态统一分支不会导致任何额外的减速(尽管NVIDIA认为重新编译比执行它更快的事实使得它至少对于他们的硬件来说不太可能)。但是动态分支会花费你一些东西,即使所有的调用都采用相同的分支。
这个时代的编译器尽力优化着色器,以便简单地执行简单的条件。例如,你的output = input*enable + input2*(1-enable);
是一个体面的编译器可以从你的等效if
语句生成的东西。
这个时代的硬件通常能够处理静态统一的分支语句而几乎没有减速。对于动态分支,您可能会或可能不会遇到减速。
这个时代的硬件几乎可以保证能够在几乎没有性能问题的情况下处理dynamically uniform条件。实际上,它甚至不必是动态统一的;只要同一波前的所有调用都采用相同的路径,您就不会看到任何重大的性能损失。
请注意,上一个时代的某些硬件也可能会这样做。但这是几乎肯定是真实的。
欢迎回到狂野的西部。虽然不像Pre-D3D10桌面,但这主要是由于ES 2.0口径硬件的巨大差异。有如此多的东西可以处理ES 2.0,它们的工作方式各不相同。
静态分支可能会得到优化。但是,从静态统一分支中获得良好性能是否与硬件有关。
这里的硬件比ES 2.0更加成熟和强大。因此,您可以期望静态统一分支执行得相当好。一些硬件可能像现代桌面硬件那样处理动态分支。
它高度依赖于硬件和条件。
如果你的条件是统一的:不要打扰,让编译器处理它。如果你的条件是动态的(比如从属性计算的值或从纹理或某物中获取的值),那么它就更复杂了。
对于后一种情况,您几乎必须进行测试和基准测试,因为它取决于每个分支中代码的复杂性以及分支决策的“一致性”。
例如,如果其中一个分支占据了99%的情况并丢弃了该片段,那么很可能您希望保留条件。但是在上面的简单例子中,如果enable
是一些动态条件的OTOH,算术选择可能会更好。
除非你有一个像上面这样的明确案例,或者除非你针对一个固定的已知架构进行优化,否则你可能会更好地完成编译器的数据。