GLSL IF 速度与乘数

GLSL IF speed vs multiply factor

我知道这个问题通常被问到,但答案总是 "depends",所以我提出了一个具体的问题,希望能得到具体的答案。

我知道 IF 在 GLSL 上的缺点,它们可能非常昂贵,甚至在某些硬件中执行所有代码。

所以,我有一个来自示例(双抛物面阴影贴图)的片段着色器,它使用 if's 来确定要使用哪个贴图并计算深度,但我知道用乘数替换那些 if's 非常容易,问题是片段着色器中有一个纹理采样,使用 if 或使用乘法器来过滤未使用的数据会更快吗?

这些是建议的代码:

IF 版本:

//Alpha is a variable computed on the fly, cannot be replaced

float depth = 0;
float mydepth = 0;

if(alpha >= 0.5f)
{
    depth = texture2D(ShadowFrontS, P0.xy).x;
    mydepth = P0.z;
}
else
{
    depth = texture2D(ShadowBackS, P1.xy).x;
    mydepth = P1.z;
}

过滤器版本:

float mlt = ceiling(alpha - 0.5f);

float depth = 0;
float mydepth = 0;

depth = texture2D(ShadowFrontS, P0.xy).x * mlt;
mydepth = P0.z * mlt;
mlt = 1.0f - mlt;
depth = depth + (texture2D(ShadowFrontS, P1.xy).x * mlt);
mydepth = P1.z * mlt;

P.D.: 我的目标是桌面和移动设备,所以低端硬件上的性能是必须的。

在大规模 SIMD 架构上,分支本身并不是 "evil"。如果 "bunch"(NVidia 称它们为 Warps)中的所有线程都遵循相同的代码路径,即采用所有相同的分支,那么一切都很好。

仅当一个分支被部分采用(在该束内)而另一部分未被采用时,两个分支都必须执行,随后丢弃与当前线程无关的计算和数据提取。

现在,在您的情况下,需要进行一些仔细的分析才能看到哪种变体对您的 GPU 更有利。但我的直觉告诉我,它实际上是分支版本。为什么?因为:通常,您决定分支的值取决于屏幕 space 位置,并且片段的大连续区域通常共享相同的代码路径和分支;所以性能惩罚只发生在那些覆盖边界区域的"bunches"。这些束的大小通常只有几个像素²(8×8 或 16×16)。

您拥有的着色器不受 GPU 限制(即受 GPU 的计算能力限制),但内存带宽受限,即受 GPU 内存 link 提供的吞吐量限制;那是因为 texture2D 获取操作。在这种情况下,减少实际的提取次数并因此减少所需的内存带宽可能比减少计算次数更能使您的程序受益。

您的着色器的无分支混合多路复用变体将始终获取两个纹理,分支的变体将仅在边界区域内执行此操作。因此,根据我的启发式猜测,您的分支变体实际上是更好的选择。

但要确保您必须 分析 它。