我知道这个问题已被普遍提出,但答案是“依赖”,所以我正在创建一个具体的问题,希望得到一个具体的答案。
我知道IF在GLSL上的邪恶,它们可能非常昂贵,甚至在某些硬件中执行所有代码。
所以,我有一个示例的碎片着色器(双抛物面阴影贴图),它使用if来确定使用哪个贴图并计算深度,但我知道用乘数替换那些是非常容易的,问题在片段着色器中是否有纹理采样,使用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。:我的目标是桌面和移动设备,因此必须在低端硬件上实现性能。
答案 0 :(得分:7)
分支在大规模SIMD架构上并非“邪恶”。如果“束”中的所有线程(NVidia称它们为Warps)遵循相同的代码路径,即采用所有相同的分支,一切都很好。
只有部分分支(在该分支内)而另一部分不分支,必须执行两个分支,然后在计算和数据提取中丢弃与当前线程无关的分支。
现在,在您的情况下,需要仔细分析才能看到,哪种变体有利于您的GPU。但我的直觉告诉我,它实际上是分支版本。为什么?因为:通常您决定分支的值取决于屏幕空间位置,并且通常大的连续片段区域共享相同的代码路径和分支;因此,性能惩罚只发生在那些覆盖边界地区的“束”中。这些束通常只有几个像素大小(8×8或16×16)。
你在那里使用的着色器不受GPU限制(即受GPU的计算能力限制),但是内存带宽受限,即GPU的内存链接提供的吞吐量;这是因为texture2D获取操作。在这种情况下,减少实际的读取次数,从而减少所需的内存带宽,可能会使计划受益,而不是减少计算次数。
着色器的无分支mix-multiplex变体将始终获取两个纹理,分支将仅在边界区域内执行。所以,从那个启发式算法我猜,你的分支变体实际上是更好的选择。
但是要确保你必须个人资料它。