If语句会减慢我的着色器吗?

时间:2016-06-15 05:59:35

标签: opengl glsl shader direct3d hlsl

我想知道" 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部分?

2 个答案:

答案 0 :(得分:93)

哪些着色器甚至可能使if语句出现性能问题?它与着色器的执行方式以及GPU从中获得巨大的计算性能有关。

单独的着色器调用通常并行执行,同时执行相同的指令。他们只是在不同的输入值集上执行它们;他们分享制服,但他们有不同的内部登记。所有执行相同操作序列的着色器组的一个术语是" wavefront"。

任何形式的条件分支的潜在问题是它可以搞砸所有这些。它导致波前的不同调用必须执行不同的代码序列。这是一个非常昂贵的过程,必须创建一个新的wavefront,将数据复制到它上面等等。

除非......它没有。

例如,如果条件是wavefront中每次调用所采用的条件,则不需要运行时分歧。因此,if的成本只是检查条件的成本。

所以,让我们说你有一个条件分支,让我们假设wavefront中的所有调用都采用相同的分支。在这种情况下,表达的性质有三种可能性:

  • 编译时静态。条件表达式完全基于编译时常量。这样,你知道从查看代码并知道将采取哪些分支。几乎所有编译器都将此作为基本优化的一部分来处理。
  • 静态均匀分支。该条件基于表达式,这些表达式涉及在编译时已知的常量(具体地,常量和uniform值)。但是在编译时不会知道表达式的。所以编译器可以静态地确定波前永远不会被这个if打破,但编译器无法知道将采用哪个分支。
  • 动态分支。条件表达式包含常量和制服以外的术语。在这里,编译器无法判断波前是否会被分解。是否需要发生取决于条件表达式的运行时评估。

不同的硬件可以处理不同的分支类型而不会发散。

此外,即使不同的波前采用了条件,编译器也可以重新构造代码,而不需要实际的分支。您给出了一个很好的示例:output = input*enable + input2*(1-enable);在功能上等同于if语句。编译器可以检测到if正用于设置变量,从而执行双方。这种情况经常发生在树枝体很小的动态条件下。

几乎所有硬件都可以处理var = bool ? val1 : val2而不必分歧。这可能是在2002年。

由于这与硬件有关,因此......取决于硬件。然而,可以看到某些硬件时代:

桌面,预先D3D10

在那里,它有点像狂野的西部。用于此类硬件的NVIDIA编译器因检测此类情况而臭名昭着,并且每当您更改影响此类情况的制服时,实际上重新编译着色器

一般来说,这个时代约有80%的人从不使用if语句"来自。但即使在这里,也不一定如此。

您可以期待静态分支的优化。您可以希望静态统一分支不会导致任何额外的减速(尽管NVIDIA认为重新编译比执行它更快的事实使得它至少对于他们的硬件来说不太可能)。但是动态分支将花费你一些成本,即使所有的调用都采用相同的分支。

这个时代的编译器尽力优化着色器,以便简单地执行简单的条件。例如,您的output = input*enable + input2*(1-enable);是一个体面的编译器可以从您的等效if语句生成的内容。

桌面,后D3D10

这个时代的硬件通常能够处理静态统一的分支语句而几乎没有减速。对于动态分支,您可能会或可能不会遇到减速。

桌面,D3D11 +

这个时代的硬件几乎可以保证能够在几乎没有性能问题的情况下处理dynamically uniform条件。实际上,它甚至不必动态均匀;只要同一波前的所有调用都采用相同的路径,您就不会发现任何重大的性能损失。

请注意,上一个时代的某些硬件也可能会这样做。但是,这几乎肯定是真实的。

Mobile,ES 2.0

欢迎回到狂野的西部。虽然不像Pre-D3D10桌面,但这主要是由于ES 2.0口径硬件的巨大差异。有如此众多的东西可以处理ES 2.0,它们的工作方式也各不相同。

静态分支可能会得到优化。但是,从静态统一分支中获得良好性能是否与硬件有关。

Mobile,ES 3.0 +

这里的硬件比ES 2.0更加成熟和强大。因此,您可以期望静态统一分支执行得相当好。有些硬件可能像现代桌面硬件那样处理动态分支。

答案 1 :(得分:6)

它高度依赖于硬件和条件。

如果你的情况是统一的:不要打扰,让编译器处理它。 如果您的条件是动态的(如从属性计算的值或从纹理或某物中获取的值),那么它就更复杂了。

对于后一种情况,您几乎必须进行测试和基准测试,因为它取决于每个分支中代码的复杂性以及如何“一致”。分支决定是。

例如,如果其中一个分支占99%的情况并丢弃该片段,那么很可能你想要保留条件。但是如果enable是{动态条件},上面的简单示例中的OTOH可能会更好。

除非您有如上所述的明确案例,或者除非您针对一个固定的已知架构进行优化,否则您可能会更好地摆脱出于您的编译器数据。