OpenCL(GPU)で波の表現

English version is ready: 英語

どうも、吉村です。

今回はGPUのパワーを体感してみたいと思います。
お題としては波で、最終結果はこのようになります。
メッシュにはなっておらず、全て点で出来ています。
また、頂点の高さでカラーリングしています。

wave-gpu from wowdev on Vimeo.

波はセルベースの計算方法で、セル同士をバネで接続することで波のように見せます。

・セルはそれぞれ高さを持つ
・隣のセルが離れていればお互いに引っ張り合う
・0の高さに引っ張る力がある

これらの規則で毎フレーム処理すれば波っぽくなります。
波動方程式は難しくて今はよくわからないので、ひとまず無視します。

この処理をするにあたって、一番の壁になるのは処理速度です。
というのも、セルは2次元の広がりがあるため、あっという間に計算量が膨れ上がります。
例えば、100×100でも1万、500×500なら25万、1000×1000なら100万のセルになり、
1フレームに100万のセルをCPUにやらせるのは非常に無理があります。

しかし! こういう”単純な計算を大量にやる”というのはGPUの超絶並列化コアを使用すれば爆速で実行できるはずです。
なので今回は1000×1000(100万)のセル計算ををGPUにやらせて、60fpsをキープすることにチャレンジします。

アプローチとしては、今回はOpenCLを使っていこうと思います。

OpenCLは生のAPIを使っても良いのですが、APIはそれなりに複雑なので、これは結構骨が折れます。
Xcodeが使える環境であれば、実はもっと簡単手軽にOpenCLを使う方法があります。
というのは、Appleは並列化のための技術であるGCD(Grand Central Dispatch)とOpenCLを見事に統合しているからです。(素晴らしい!最高!)
またさらにOpenGLのメモリとも直結してOpenCLを動作させることも比較的簡単に可能なのです。

簡単に計算のステップをまとめると、

  1. gcl_gl_set_sharegroup()でOpenGLとのコネククションを確立させる
  2. OpenCLのカーネルコードとして、プロジェクトに~~~.clのファイルを作成する
  3. ~~~.cl.hをインクルードする(ヘッダーはビルド時に自動生成されるため、これだけで使えるようになる)
  4. gcl_create_dispatch_queue(CL_DEVICE_TYPE_GPU, NULL);でOpenCLの並列キューを作成する
  5. VBO(Vertex Buffer Object, GPUに確保した頂点データ)を作成する
  6. gcl_gl_create_ptr_from_buffer()でVBOをOpenCLのバッファとして使用することを指示する
  7. gcl_malloc(), gcl_map_ptr(), gcl_unmap等を使って、作業用の加速度データのために、OpenCLバッファを用意する
  8. メインループで、計算と描画を繰り返す

という手順になります。

メインループは以下のようになります。(疑似コード)
以前の記事

http://wowdev.jp/?p=321

と非常に構造が似ています。

これで目標だった「1000×1000(100万)のセル計算ををGPUにやらせて、60fpsをキープする」を達成することができます。

ちなみに実際動作するソースコードはこちらです。

https://github.com/wowdevjp/GPUWave

【環境】
・MacOSX mountain lion
・xcode 4.6.2
・cinder

この構造で最も特筆すべき点は、
「計算がほとんどGPUで完結している」
という点です。
VBOを直接OpenCLで書き換えることが可能なおかげで、
メインループでのGPUCPU間のメモリの転送が最小限に押さえられているのです。
これによりGPUの速度を最大限に引き出すことが可能になります。
また、このような高度なブリッジが非常にシンプルなAPIで操ることが出来る
という点も大きなポイントになってくるとおもいます。

是非皆さんもGPUのスーパーパワーをAppleの素敵なAPIとともに体験してみませんか?
きっと良い旅になることでしょう。
ではでは