やや複雑なフラグメント シェーダーがあり、これを小さなシェーダーに分割することはできません。
シェーダーが行うことの 1 つは、フラグメント/ピクセルから他の 7 つのポイントまでの距離を計算し、最も近い 2 つのポイントを見つけることです。
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
ご覧のとおり、コードは最短距離のインデックスを見つけます。私たちの意見では、このコードは動的分岐なしで実行できます。1 つの「ループ」の最後の 4 行は単なる計算であり、実際の分岐ではありません。
indexOfLowest
問題は、新しい値を取得する「ループ」の最後から 2 番目の行です。
newIndexOfLowest = isLowest ? 2 : indexOfLowest;
その行をコメントアウトすると、すべてが 60 FPS で正常に動作し、Instruments は動的ブランチを報告しません。しかし、このラインでは、8 FPS 以下です。
GPU で動的分岐が発生しないように、このコードをどのように書き直すことができるでしょうか?
編集:コードをさらに単純なバージョンに更新しましたが、同じ問題があります。古いコードには、制限が固定されたforループがありました。しかし、問題はまだ残っています。
編集 2: PowerVR の PVRShaderEditor (以前の PVRUniSCoEditor) を使用してコードを分析したところ、シェーダー ソース コード行ごとに予想される GPU サイクルが表示されます。問題の行 (上記のコードの BAD LINE) は、1 ~ 2 GPU サイクルのみで示されています。この行が動的分岐を引き起こし、それが大きなパフォーマンスの問題を引き起こすという事実は言及されていません。このコードをデバッグして、なぜこれらの分岐を引き起こしているのかを理解できるようにするための他のアイデアはありますか?