1

我们有一个有点复杂的片段着色器,我们不能将它拆分成更小的着色器。

着色器所做的一件事是计算片段/像素到其他 7 个点的距离,并找到两个最近的点。

在 iPad 上,这部分代码存在巨大的性能问题,Instruments告诉我们瓶颈是着色器代码,它会导致GPU 中的动态分支

我们牺牲了可读性并尝试了几处代码更改以避免这些动态分支:

  • 改变了循环,使它不是真正的循环,而是一系列重复的指令。
  • 简化代码,使其仅包含非常简单的类似 if 的指令。

这是我们提出的代码的“最简单”版本:

bool isLowest;
float currentDistance, lowestDistance, newLowest;
lowestDistance = distances[1];
int indexOfLowest = 1, newIndexOfLowest;

// 'loop' for index 2
currentDistance = distances[2];
isLowest = currentDistance < lowestDistance;
newLowest = isLowest ? currentDistance : lowestDistance;
lowestDistance = newLowest;
newIndexOfLowest = isLowest ? 2 : indexOfLowest;    // BAD LINE
indexOfLowest = newIndexOfLowest;

// 'loop' for index 3
currentDistance = distances[3];
isLowest = currentDistance < lowestDistance;
newLowest = isLowest ? currentDistance : lowestDistance;
lowestDistance = newLowest;
newIndexOfLowest = isLowest ? 3 : indexOfLowest;    // BAD LINE
indexOfLowest = newIndexOfLowest;

// etc. until index 8

如您所见,代码找到了最低距离的索引。在我们看来,这段代码可以在没有动态分支的情况下执行。一个“循环”的最后四行只是简单的计算,它们不是真正的分支。

问题是'循环中的倒数第二行,其中indexOfLowest获得了一个新值:

newIndexOfLowest = isLowest ? 2 : indexOfLowest;

如果我们注释掉该行,那么在 60 FPS 上一切正常,并且 Instruments 不会报告动态分支。但是有了这条线,它不会超过 8 FPS。

我们如何重写这段代码,使其不会在 GPU 中引起动态分支?

编辑:刚刚将代码更新为更简单的版本,它有同样的问题。旧代码有一个具有固定限制的for循环。但问题仍然存在。

编辑 2:我们刚刚使用 PowerVR 的 PVRShaderEditor(以前的 PVRUniSCoEditor)分析了代码,它显示了每个着色器源代码行的预期 GPU 周期。有问题的行(上面代码中的 BAD LINE)仅显示 1-2 个 GPU 周期。没有提到这条线导致动态分支导致巨大的性能问题的事实。我们有什么其他想法可以调试此代码,以便我们了解它为什么会导致这些分支?

4

1 回答 1

3

我们做到了。在这里投射boolto float( 1.0for true, 0.0for false) 很有帮助:

bool isLowest;
float currentDistance, lowestDistance, newLowest;
lowestDistance = distances[1];
int indexOfLowest = 1, newIndexOfLowest;

// 'loop' for index 2
currentDistance = distances[2];
isLowest = currentDistance < lowestDistance;
indexOfLowest = float(isLowest) * float(2 - indexOfLowest);
lowestDistance = isLowest ? currentDistance : lowestDistance;

// 'loop' for index 3
currentDistance = distances[3];
isLowest = currentDistance < lowestDistance;
indexOfLowest = float(isLowest) * float(3 - indexOfLowest);
lowestDistance = isLowest ? currentDistance : lowestDistance;

// etc. until index 8

这将计算最低距离的索引,而不使用动态分支。

于 2013-04-10T09:29:13.960 回答