GPGPUプログラミング (CUDA / OpenGL GLSL)
GPGPU (汎用GPU) プログラミングは、グラフィックスプロセッサが持つ数千の並列コアを、グラフィックス以外の計算に利用する手法である。密なRGB-D SLAMはこの分野における「キラーアプリ」であった。KinectFusionのようなパイプラインの各段階——ピクセルごとの深度フィルタリング、ボクセルごとのTSDF融合、ピクセルごとのレイキャスティング——はいずれも、ピクセルまたはボクセルに対するembarrassingly parallel(自明に並列化可能)なマップ処理であり、まさにGPUが得意とする形である。CPU上で30 Hzのリアルタイム密SLAMを実現するのは事実上不可能であり、これらのループをGPUに移した瞬間に初めて可能になった。
CUDAの概要
CUDA (NVIDIAのGPGPUプラットフォーム) は、GPUを軽量スレッドの格子として提供し、それらすべてが同一のカーネル関数を実行する。
- スレッド階層: スレッドはブロックにまとめられ、ブロックはグリッドにまとめられる。ピクセルごとのカーネルは通常、1ピクセルにつき1スレッドとして起動され、各スレッドはブロック/スレッドのインデックスから自身の座標を計算する。
- メモリ階層: 大容量だが高レイテンシのグローバルメモリ、ブロック内でのデータ再利用のための高速なシェアードメモリ、そしてスレッドごとのレジスタが存在する。性能は**コアレスド(coalesced)**なグローバルメモリアクセス(隣接するスレッドが隣接するアドレスを読む——画像処理では自然に成立する)と、PCIeバスを経由するホスト-デバイス(CPU-GPU)間転送の最小化に大きく依存する。
- 実行モデル: スレッドはロックステップのグループ(ワープ)単位で実行される。ワープ内で分岐が発散すると直列化されるため、ピクセルごとのコードは重い分岐を避けるべきである。
典型的なSLAMカーネルには、深度マップのバイラテラルフィルタリング、深度から頂点マップ・法線マップを計算する処理、TSDF統合(カメラフラスタム内の各ボクセルに1スレッドを割り当て、そのボクセルを深度画像へ投影して加重移動平均を更新する)、そしてフレーム対モデルトラッキングのためのTSDFレイキャスティングがある。
並列リダクションはもう一つの重要なパターンである。射影データ対応ICPは、のGauss-Newton系を、数十万ピクセルにわたる項との総和として計算する。各スレッドが自分のローカルな積を計算し、シェアードメモリ内でのツリー構造のリダクションがブロックごとに総和を取り、最終パス(またはアトミック演算)でブロック間の結果を結合する。CPUに戻されるのは、小さなの系のみであり、そこで解かれる。
計算手段としてのOpenGL GLSL
CUDAが登場する以前、そしてCUDAと並行して、GPGPUはグラフィックスパイプラインを通じてGLSLシェーダーを用いて行われてきた。いくつかの影響力のあるRGB-Dシステム(特にElasticFusion)はこの方式で書かれている——CUDAと異なりベンダー中立であるという利点もある。
- データはテクスチャと頂点バッファに保持され、計算はレンダリングパスとして表現される。フラグメントシェーダーが出力ピクセルごとに1回実行され、結果をオフスクリーンのフレームバッファオブジェクト(“render-to-texture”)に書き込み、パスをまたいで2つのバッファ間でピンポンすることも多い。
- サーフェルマップはこのパイプラインに自然に適合する。各サーフェル(位置、法線、半径、色、重み)は頂点であり、マップをインデックス/モデル画像へスプラッティングすることは単なるレンダリングであり、サーフェルの融合とカリングはこれらのバッファに対するシェーダーパスとして実行される。
- 現代のOpenGLはコンピュートシェーダーも提供しており、GLエコシステムを離れずにCUDAとのギャップの大部分を埋めている。また、CUDA-OpenGL相互運用により、CUDAカーネルがGLバッファに直接書き込むことでゼロコピーの可視化が可能になる。
実践的な指針
- パイプライン全体をGPU上に常駐させ、フレームごとにダウンロードするのはポーズと小さな行列のみにとどめること。フレームごとに画像や体積全体をホスト-デバイス間でコピーすると、高速化の効果は消えてしまう。
- まずメモリトラフィックをプロファイルすること——ほとんどの画像/ボクセルカーネルは計算量ではなく帯域幅がボトルネックである。近傍ピクセルを再利用するステンシル型カーネル(バイラテラルフィルタ、法線計算)にはシェアードメモリを活用する。
- GPU上で画像ピラミッドを利用する。coarse-to-fineのICP/トラッキングは同じカーネルを複数解像度で実行することで、低コストで大きな収束域を得られる。
- 数値精度に注意すること。コンシューマー向けGPUは
floatの方がdoubleよりもはるかに高速であり、密SLAMパイプラインはほぼ全て単精度で実行され、重要な場面ではCPU側のソルバーに倍精度を予約する。 - 古典的な密SLAMを超えて、同じスキルはステレオ深度推定ネットワーク、ニューラルフィールドマッピング、あらゆる学習ベースのフロントエンドを支えている。CUDAはそのすべての基盤である。
SLAMにおける意義
- KinectFusion(CUDA)とElasticFusion(GLSL)はGPGPUの2つの経路を示している。両方を理解することで、事実上すべての密RGB-D SLAMコードベースを読み、修正できるようになる。
- GPU/CPUの分担はアーキテクチャ上の決定である。GPU上で密トラッキングとマッピングを行い、CPU上で疎な最適化(ポーズグラフ、BA)を行うのが標準的な役割分担である。
- ボクセルハッシング、TSDF統合、レイキャスティングは、GPUの並列性があってこそリアルタイムで実行できる。この分野では、アルゴリズム設計とGPGPUの実装は不可分である。