GPUクラッシュが激減した…

こんばんわ、Pocolです。

このところ,仕事でずっとGPUクラッシュを追っていたのですが,なんとなくノウハウが溜まってきました。
ほぼ、毎日のようにGPUクラッシュが何十件もあり,色々な人に手伝ってもらいながら,調査していたんですが,ようやくそれがパッタリ収まりました。

いくつか対策を入れていたのですが,結局どれが効いたのかは正直分からないのですが…
groupsharedで,UINT32_MAXでアクセスして,GPU上でメモリ破壊を発生させるコードがあったので,修正した所,謎に発生していたGPUクラッシュが治まりました。
多分,数年レベルで放置されていたバグなんじゃないかと思います。

NVIDIA AftermathとかでGPUクラッシュダンプを調べても,PageFault,さらにシェーダ情報もでない,挙句の果てにはResourceBarrierを実行するとGPUクラッシュする,定数バッファが壊れて無限ループして,TDRで落ちるなど,過去のクラッシュログを見ても,まったく共通性もなく,しかも結構頻発する。でも全然再現性が無い,手元で全く発生しない…という困ったちゃんで,困りまくった挙句何かないのか?と調べてみたら,NVIDIA Aftermathに,GFSDK_Aftermath_FeatureFlags_EnableShaderErrorReportingというフラグがあるのですが,これを有効にしたところ即クラッシュするようになりました。
何で無効だったのか聞いたんですが,こちらはヘッダファイルにも書かれているようにパフォーマンスペナルティがあるということで,ゲームプレイに支障があるとのことで無効化していたとのことでした。
結局,このフラグを有効にしたところ,先ほど述べたgroupsharedのバグを発見できたのと,他にもクラッシュする原因が見つかって,大いに役立ちました。

今後は,確定で発生しないものはGPU上のメモリ破壊を疑った方が良いという知見が得られました。
基本的にはUAVとかSRVとかのLoad()とかoperator []あたりミスっているんじゃないかって思いがちなんですが,これらはAPIドキュメントを見ると、安全に良しなにしてくれそうなことがあるので,メモリ破壊が発生するのは,CPU上での書き換えか,groupsharedのアクセスミスによる2パターンしかないように思えます(他のパターンがあったら教えてください)。CPU上でのメモリ破壊の可能性がほとんど低いことが分かったなら,groupsharedでの破壊が無いかどうかを調べましょう。
今回のバグは,

groupshared g_Variable[XXX][XXX];
みたいなのが定義されていて,
float temp0 = saturate(XXX);
uint temp1 = temp0 * MAX_VALUE – 1;
uint index = min(temp, MAX_VALUE);
g_Variable[index][XXX] = ….;
っぽいような,謎の計算がされていて(uintをマイナス1する時点でぞわぞわしちゃんですが…)
temp0がゼロになったときに,アボンするみたいケースでした。
よくよく見ると「馬鹿か!」って怒鳴りたくなったっちゃうような,不具合なんですが,変数がごちゃごちゃ定義されていたりとか,ジュニアレベルのエンジニアだとこういうチェックがおざなりになりがちなんで,気を付けた方が良いよ!…という良い事例になりました。

…というわけで,社内でも共有したんですが,この場でも共有してみました。
謎バグに困っている方は,groupsharedで変なことしていないかどうか確認してみると良いかもです。
(※ちなみ1個あったら,他にも絶対あるだろうと思って全シェーダをチェックしてみたのですが,確認した所発生しているのは該当シェーダの1個だけでした。)