在片段着色器中对float数组进行排序,而不在OpenGL ES(iPad)中进行动态分支

时间:2013-04-07 17:00:36

标签: opengl-es-2.0 glsl gpu

我们有一个有点复杂的片段着色器,我们无法将其拆分为较小的着色器。

着色器的作用之一是计算片段/像素到其他7个点的距离,找到最近的两个点。

在iPad上,我们在这部分代码中遇到了巨大的性能问题,乐器告诉我们瓶颈是着色器代码导致动态分支 GPU。

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

  • 更改循环,使其不是一个循环,而是重复的一系列指令。
  • 简化了代码,使其仅包含非常简单的 if-like 说明。

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

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上正常工作,而且仪器不会报告动态分支。但是使用这条线,它不会超过8 FPS。

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

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

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

1 个答案:

答案 0 :(得分:2)

我们做到了。将bool转换为float1.0true0.0false},这有助于:

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

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