インスタンス描画

GPUVoxelizationのテストプログラムを書いている過程で、voxelの状態確認のためにインスタンス描画を行いました。今回はインスタンス描画について考えてみたいと思います。

一番時間がかかるvoxelの表示

GPUを用いてPolygonMeshをvoxelizeするテストプログラムを書いています。voxelの状態を確認するために、voxelが存在する場合はその該当領域をwireframeのcubeを描画するようにしました。
ところが、総レンダリング時間に対して、voxelのwireframe描画が非常に時間がかかることがわかりました。voxelの構築自体は数msで終わるのですが、その後の表示に数十~数百msかかってしまいます。これはvoxelの解像度を上げると顕著になっていきます。

考えられる2つの方法

3Dテクスチャに記録されたvoxelの値に応じてwireframeのcubeを描画する場合に、考えられる方法は大きく分けて2つあると思います。

Geometry Shaderを用いる方法

  • VS – 該当位置のvoxelの値を読み出して、voxelを描画する必要があるかないかを判断
  • GS – VSからの入力を元に、voxel描画の必要がない場合はプリミティブを生成せずに終了。描画の必要がある場合は、LineStripを用いてwireframe のcubeを出力
  • PS – 単純にLineを描画

この方法では、voxelの個数分だけPointプリミティブをインスタンス描画する必要があります。そのほかには、GSで頂点を多数生成しなければならないのが気がかりです。

Geometry Shaderを用いない方法

  • VS – 該当位置のvoxelの値を読み出して、voxelを描画する必要があるかないかを判断。voxel描画しない場合は頂点位置がクリップ座標系の外になるように出力する
  • PS – 単純にLineを描画

この方法では、voxelの個数分だけwiere cubeのプリミティブをインスタンス描画する必要があります。GSを用いませんがvoxel情報の参照は頂点ごとに行うので、計8回3Dテクスチャに格納されたvoxel情報が参照することになります。ただし参照する3Dテクスチャ座標は8回とも同一のものになるので、サンプリング時のキャッシュヒットは期待できます。
シェーダーコードはいずれも簡単なものなので割愛します。細かい最適化も行っていません。

結果

2563のvoxelにStanfordBunnyをvoxelizeしました。161716個のvoxelがfillされました。充填率にすると0.9%です。voxelのoctree化はしていないので単純な3Dテクスチャに値が格納されています。
比較対象にvoxelのfillを判定しないで、単純に2563のvoxelグリッドを描画したものを計測しました。これらの処理時間をGTX480を用いて計りました。(現在代替機で作業しています。折を見てGTX680で数字を取り直します)

voxel充填率 0.9% 100%
VS-PS 267ms 300ms
VS-GS-PS 267ms 267ms

このケースはvoxelの充填率が処理時間に影響していません。また、GSを用いた場合と用いない場合で処理時間がほとんど変わりません。描画のボトルネックになっている部分は、GSでもなければ3Dテクスチャのサンプリングでもないようです。

この場合のボトルネックはDrawCall

上記のケースでボトルネックになっていると思われるのはDrawCallと思われます。インスタンス描画を用いると、GPU内で多数のDrawCallがマージして実行されるイメージがありますが、実際には個々のインスタンスごとに別のオブジェクトとして処理されます。インスタンス描画は個々にDrawCallを呼び出して描画するより遥かに高速ですが、オブジェクトの頂点数が少ない場合には、少数頂点のオブジェクトを描画した時と同様の非効率性があります。具体的には、GeForceの場合は個々のインスタンスの頂点数が32頂点を下回る場合には考慮の余地があります。VS-PSのシェーダーでは、1インスタンスあたり8頂点なので、4個のvoxelをマージして32頂点になるようにDrawCallとシェーダーを変更します。VS-GS-PSのシェーダーでは1インスタンスあたり1頂点しか投入してないので、32個のvoxelをマージして、32頂点になるようにDrawCallとシェーダーを変更します。

voxel充填率 0.9% 100%
VS-PS(4cube by 1 instance) 93.7ms(280%) 292ms(102%)
VS-GS-PS(32cube by 1 instance) 30ms(890%) 228ms(117%)

DrawCallをマージする前と比較した場合の処理時間の比をあわせて記しました。voxelの充填率が低い場合の速度向上が顕著です。特にGSを使った場合では、8.9倍ものパフォーマンスになっています。この処理時間はレンダリング全体で計測していますので、voxelのインスタンス描画の部分のみを計測すればさらに高いパフォーマンスであると思われます。また、GSを使うケースと使わないケース2つを作成してみましたが、GSを使ったケースがvoxelの充填率が低い場合では特に処理が速く、充填率が100%になった場合でもなお速いという結果になりました。GSの使用はなんとなく禁忌のように扱われますが、このようなケースでは一番速い処理となり得るということが分かりました。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中