ひきぷろのプログラミング日記

プログラミングの日記です。

(Unity) HLSL で Compute Shader を書くのは難しい

Unity 上で HLSL という仕組みを使って Compute Shader の処理を書いていたんですが、 Visual Studio のような環境で CPU 向けにコードを書くのと違うところが沢山あって、いろいろと困難を感じてしまいました。

感想文のような感じになりますが、発生した問題についてメモしておきます。

配列の扱い方が難しい

まずは普通に配列を宣言してみます。

// 配列の変数を宣言しているつもり
float test[];

このコードは、次のようなエラーが出ます。

  • implicit array missing initial value

初期値がないのがいけないっぽいです。

// これは OK。初期値がある
float test[] = {0, 0, 0};

// これも OK。サイズ指定がある
float test[10];

// これは NG。変数でサイズ指定してはいけないらしい
int size = 10;
float test[size];

3 つ目は、次のようなエラーが出ます。

  • array dimensions must be literal scalar expressions

整数の定数でないとダメっぽいですね。
変数でサイズを指定するとエラーになるようです。

ということは、動的にサイズを変えるような処理が作れないということになりますね。メディアンフィルタを移植しようとしたときに、中央値を出す処理で、今扱っている値のリストを保持する必要が出てきたんですが、動的に配列が作れないとなると、カーネルサイズ固定の処理しか書けないことになります。

色々試行錯誤してみたところ、 ComputeBuffer という仕組みを使って HLSL の外側から変数をセットすることで、動的な配列は作れないことはなさそうでした。ただ、 ComputeBuffer を使った場合は、 GPU のコアがどのように処理を分散しているかを想像してプログラムを書く必要がありそうです。なんとなくで処理を書いてみたら、今使っている最中の配列を別のコアが使おうとして、画像が乱れてしまう というような問題が出てきました。これは、同時に使われることがなさそうなほど大量にメモリを確保することで回避できましたが、かなり大きめにメモリを確保する必要がありそうでした。また、メモリの使用量を抑えようにも、どの程度隙間を空ければ処理が重なってしまわないかを明確にするのが難しそうです。きっちりこのラインを空ければ大丈夫 というのを見極めるのに時間がかかりそうでした。

重い処理を書くとグラフィックカードのドライバごと落ちる

重い処理だったのか問題がある処理だったのか、今のところはっきりしていないところもありますが、負荷が明らかに高そうな設定で実行してみると、ドライバごと落ちてしまって、 Unity がそのまま動かなくなることがありました。 Windows の通知領域に、「ドライバが落ちましたが復旧しました」 というようなメッセージが出てきます。 HLSL は、描画のための処理を書くものなので、もしかすると一定時間経っても応答がなければ、プログラムを停止させる処理が入っているのかもしれません。ブラウザでも、 JavaScript で無限ループしていると停止させるか確認するダイアログが出てきますが、それと同じような機能が入っているのかな と想像しています。さきほど試した感じでは、数百ミリ秒くらい経てば完了するであろうというような処理でも落ちてしまっていたので、時間がかかる処理が全く書けなさそうでした。 Unity の再起動が必要になるので、試行錯誤するのも時間がかかりそうです。

インターネット上にドキュメントが少ない

ググってもあんまり資料が出てきませんでした。 HLSL を書いている人自体が少ないのかもしれません。

エラーの内容が分かりにくい

Unity の Compute Shader では、エラーの行番号が出てきても場所が間違っていたりとか、エラーが発生して落ちた場合でも、原因がはっきりとは分からないとか、追跡する道が途絶えてしまうようなことがわりとありました。

まとめ

CPU を扱っている時と同じような感覚で HLSL のプログラムを書くのは難しそうです。 HLSL 用に発想を切り替えるとか、処理内容を考え直すとか、そういう手間がかかってしまいます。また、ドライバが落ちてしまう問題は結構深刻で、重い処理なら落ちる ということであれば、もしかすると HLSL を使うこと自体がちょっと間違っているのかもしれません。ゆくゆく、画像の描画のように十数ミリ秒で完了するような処理だけではなくて、長時間かかるような処理も書きたいので、他の方法も検討した方が良いのかもです。今のところ、 CUDA を試してみようかなと思っています。

追記

Unity 上ではなくて、 Visual Studio 単体でも SharpDX や SlimDX というライブラリを使うことで、 HLSL を書き始めるのは楽にできそうでした。 Visual Studio に組み込まれている NuGet というパッケージマネージャで簡単にダウンロードすることができます。 Visual Studio 2013 以降の環境では、 GPU のパフォーマンスチェックツールが付いているようですので、場合によっては Unity より簡単にコードが書けるかもしれません。無料版にも付属してたら良いですが、もしかすると Professional 以上だけの機能かもしれません。

実装の参考にさせて頂いた URL

memeplex.blog.shinobi.jp