7

我有一个着色器,我想在顶点着色器中移动一半的顶点。我试图从性能的角度来决定最好的方法,因为我们要处理超过 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()函数在?:内部使用 a ,因此它可能不比我的第 3 个解决方案好,而且可能更糟,因为<complex formula>每次肯定都会调用它,而它可能只在一半时间被直接调用?:。(还没有人回答这部分问题。)虽然避免两者并使用:

x += (1.0 - y) * <complex formula>;

可能比他们中的任何一个都好,因为在任何地方都没有进行比较。(并且y始终为 0 或 1。)仍然执行<complex formula>不必要的一半时间,但完全避免分支可能是值得的。

4

2 回答 2

8

或许看看这个答案

我的猜测(这是一个性能问题:测量它!)是您最好不要保留该if声明。

step原因一:理论上(如果调用正确),着色器编译器应该足够聪明,以便在编译if语句时在分支指令和类似于函数的指令之间做出最佳选择。改进它的唯一方法是分析[1]。请注意,在此粒度级别上,它可能取决于硬件。

[1] 或者,如果您对数据的布局方式有特定的了解,请继续阅读...

原因二是着色器单元的工作方式:如果单元中的一个片段或顶点与其他的分支采用不同的分支,那么着色器单元必须采用两个分支。但如果他们都采用相同的分支 - 另一个分支将被忽略。因此,虽然它是按单元而不是按顶点的 - 仍然有可能跳过昂贵的分支。

对于片段,着色器单元具有屏幕上的局部性 - 这意味着您可以获得最佳性能,附近的像素组都采用相同的分支(请参阅我的链接答案中的插图)。老实说,我不知道如何将顶点分组为单元 - 但如果您的数据分组适当 - 您应该获得所需的性能优势。

最后:值得指出的是,<complex formula>如果您说可以手动将其从 HLSL 中提升出来,那么它很可能会被提升到基于 CPU 的预着色器中(至少在 PC 上,从内存中 Xbox 360 没有'不支持这个,不知道PS3)。您可以通过反编译着色器来检查这一点。如果您只需要每次绘制(而不是每个顶点/片段)计算一次,那么在 CPU 上执行它可能最好的性能。

于 2012-09-18T11:38:30.913 回答
0

我厌倦了我的条件被忽略,所以我只是制作了另一个内核并在 c 执行中进行了覆盖。如果您需要它始终准确,我建议您进行此修复。

于 2015-08-14T17:05:15.550 回答