【发布时间】:2012-09-17 13:47:54
【问题描述】:
我有一个着色器,我想在其中移动顶点着色器中的一半顶点。我正在尝试从性能的角度来确定执行此操作的最佳方法,因为我们正在处理超过 100,000 个顶点,因此速度至关重要。我查看了 3 种不同的方法:(伪代码,但足以给你这个想法。<complex formula> 我不能给出,但我可以说它涉及一个 sin() 函数,以及一个函数调用(只是返回一个数字,但仍然是函数调用),以及一堆关于浮点数的基本算术)。
if (y < 0.5)
{
x += <complex formula>;
}
这样做的好处是<complex formula>只执行了一半,但坏处是肯定会导致分支,实际上可能比公式慢。它是最易读的,但在这种情况下,我们更关心速度而不是可读性。
x += step(y, 0.5) * <complex formula>;
使用 HLSL 的 step() 函数(如果第一个参数大于则返回 0,如果小于则返回 1),您可以消除分支,但现在每次都调用 <complex formula>,其结果乘以0(因此浪费了精力)一半的时间。
x += (y < 0.5) ? <complex formula> : 0;
这个我不知道。 ?: 是否会导致分支?如果没有,是对等式的两边进行评估,还是只对相关的一侧进行评估?
最后一种可能性是<complex formula> 可能会被卸载回 CPU 而不是 GPU,但我担心它在计算 sin() 和其他操作时会变慢,这可能会导致净损失。此外,这意味着必须将另一个数字传递给着色器,这也可能导致开销。任何人都知道哪个是最好的行动方案?
附录:
根据http://msdn.microsoft.com/en-us/library/windows/desktop/bb509665%28v=vs.85%29.aspx
step() 函数在内部使用?:,因此它可能并不比我的第三种解决方案好,而且可能更糟,因为每次肯定都会调用<complex formula>,而它可能只是有一半时间用直号?: 调用。 (还没有人回答这部分问题。)虽然避免两者并使用:
x += (1.0 - y) * <complex formula>;
可能比他们中的任何一个都好,因为在任何地方都没有进行比较。 (并且y 始终为 0 或 1。)仍有一半时间不必要地执行 <complex formula>,但完全避免分支可能是值得的。
【问题讨论】:
-
该方法取决于目标硬件。您可以比较这些变体的汇编代码(例如,
RenderMonkey可以分析 Radeon 卡的性能)。另外,顶点着色器是瓶颈吗?也许所有变体都会给出相同的结果:) -
某些内核上的着色器必须同步执行,所以无论如何
complex formula都会被评估。 -
另外,如果
y可以计算为网格的函数,也许您只需拆分网格或场景几何体并运行两个不同的着色器。 -
目标平台是 XBox360、PS3 和 PC。 (PC 版本仅用于测试,因此不太重要。通常,它使用与 PS3 相同的代码。)知道
y将始终恰好为 0 或 1,两者之间没有任何关系,这可能会有所帮助。 (这是四边形的纹理坐标。)我想step()函数可以简单地替换为(1.0 - y)并具有相同的效果。仍然导致公式计算出严格必要的两倍... -
哦,如上所述,两个不同的着色器不是一个选项,因为每个多边形都会跨越阈值。它只是渲染了很多四边形。把它想象成粒子效果。不完全是这样,但这是我认为在不违反 NDA 的情况下可以安全达到的接近程度。
标签: optimization branch shader hlsl