我们有一个有点复杂的片段着色器,我们无法将其拆分为较小的着色器。
着色器的作用之一是计算片段/像素到其他7个点的距离,找到最近的两个点。
在iPad上,我们在这部分代码中遇到了巨大的性能问题,乐器告诉我们瓶颈是着色器代码导致动态分支 GPU。
我们牺牲了可读性并尝试了几次代码更改以避免这些动态分支:
这是我们提出的代码的“最简单”版本:
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周期。没有提到这条线导致动态分支导致巨大性能问题的事实。 我们如何调试此代码的任何其他想法,以便我们可以理解为什么它会导致这些分支?
答案 0 :(得分:2)
我们做到了。将bool
转换为float
(1.0
为true
,0.0
为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
这将计算最低距离的索引,而不使用动态分支。