[Anagnostou 2018Anagnostou, K. 2018. GPU driven rendering experiments. Digital Dragons. https://interplayoflight.wordpress.com/2018/05/25/gpu-driven-rendering-experiments-at-the-digital-dragons-conference/.]
自己紹介#
- Unit 2 Gamesのリードグラフィクスプログラマ
- 以前はRadiant Worldsのリードグラフィクス/エンジンプログラマで、5年ほどSkysaga: Infinite Islesの開発に従事していた
- Blitz Games Studiosのシニアグラフィクスプログラマ
- Rareのレンダリングエンジニア
- コンピュータグラフィックスでのPhDに加えていくつかの学術的なグラフィクスを修め、地方の大学でコンピュータグラフィックスを教えていた
- 時々、https://interplayoflight.wordpress.comでブログを書いている
ジオメトリカリング: とは?#
- 最終的にレンダリングされる画像に(大幅に)寄与しないジオメトリをGPUパイプラインに流さないこと
- オクルージョンカリングはこのサブセットであり、他(ソリッドメッシュ)の背後に位置するジオメトリに関係する
- 錐台カリング、後面カリング、スクリーンサイズカリングはカリングのその他の形である
ジオメトリカリングを気にする理由#
- 長年ピクセルシェーダのコストを減らそうと奮闘してきた
- 後面カリング、Early-Z、Hi-Z、Zプリパス、ステンシル処理、ディファードレンダリング、動的解像度、マルチ解像度、など
- 頂点シェーディングにそこまで愛情を注いでこなかった
- ポリゴン数は同様に増加している
- Zプリパス、シャドウパス、ディファードパスはメモリ帯域幅や処理要件を増加させる
- 開発者の制御外のゲームコンテンツ(UGC)
- ナイーブ/ブルートフォースな頂点シェーディングはGPU処理を無駄にする可能性がある
- このことは、フレーム時間の削減を逃す可能性がある
- または、(おそらく)よりきれいなピクセルをシェーディングするのに使われるとか
- または、さらなる非同期コンピュートを行うとか
- または、GPUを高温にしないとか
- これは同様にCPUに利を成す可能性がある(API呼び出しが少なくなる)
小道具いっぱい!#
- ゲームはすぐにジオメトリに制限されるようになる
- 加えて、不十分なバッチングによる大量のドローコール
オクルージョンカリング#
- 元のオクルージョンカリングはボクセルチャンクの連結グラフに基づいていた[Checchi 2014Checchi, T. 2014. The Advanced Cave Culling Algorithm™, or, making Minecraft faster. Tommo's blog. https://tomcc.github.io/2014/08/31/visibility-1.html.]
- 建物内の大量の小道具やかなりのオープンスペースには十分に効率的でなかった
オクルージョンカリング: ハードウェアクエリ#
- GPUはオクルージョンクエリのオクルージョンカリングをサポートする
- メッシュインスタンシングを用いるとすると十分な粒度ではない
- 同じフレーム中にその結果を使用できない(ポッピング)
- 依然として、有用な選択肢であり、Unrealのデフォルトのオクルージョンカリングシステムである
オクルージョンカリング: CPUラスタライゼーション#
- CPUで遮蔽物を小さなZバッファにラスタライズする
- バウンディングボックスをラスタライズしてテストする
- 同じフレームに結果を使える!
- CPUコストを増加させる
- Frostbite[Collin 2011Collin, D. 2011. Culling the Battlefield: Data Oriented Design in Practice. Game Developers Conference. https://www.ea.com/frostbite/news/culling-the-battlefield-data-oriented-design-in-practice.]やKillzone3[Valient 2011Valient, M. 2011. Practical Occlusion Culling in Killzone 3. ACM SIGGRAPH. https://www.guerrilla-games.com/read/practical-occlusion-culling-in-killzone-3.]はこれを実装しており、Intel[Kuah 2016Kuah, K. 2016. Software Occlusion Culling. Intel Developer Zone. https://web.archive.org/web/20200503075853/https://software.intel.com/en-us/articles/software-occlusion-culling.]にはソフトウェアラスタライザのソリューションがある
オクルージョンカリング: GPU(DX10までの時代)#
- GPUは高速なラスタライザであり、そこで遮蔽物のZバッファを生み出す
- 階層的なミップチェインを生み出し、ピクセルシェーダでカリングを行う
- レンダリングを操るためにCPUで結果を読み戻す
- GPUオクルージョンパス -> CPUの小道具のカリング -> GPUレンダリングパス
- ラグがあり、同じフレームに結果が使えない
オクルージョンカリング: GPU(コンピュート、DX11+)#
- CPUに結果を送り戻さないとしたらどうする?
- オクルージョンパス -> GPUの小道具のカリング -> GPUレンダリングパス
- この分野での素晴らしい進歩
- [Haar and Aaltonen 2015Haar, U. and Aaltonen, S. 2015. GPU-Driven Rendering Pipelines. Advances in Real-Time Rendering in Games course. ACM SIGGRAPH. https://advances.realtimerendering.com/s2015/index.html.]や[Wihlidal 2016Wihlidal, G. 2016. Optimizing the Graphics Pipeline With Compute. Game Developers Conference. https://gdcvault.com/play/1023109/Optimizing-the-Graphics-Pipeline-With.]のプレゼンテーションを参照
- 均一なメッシュクラスタ
- メッシュクラスタのHi-Zカリング
- トライアングルフィルタリング(錐台、大きさ、向き、など)
- MultiDrawIndirectによるメッシュバッチングとレンダリング
オクルージョンカリング: GPU(コンピュート、DX11+)#
- そのゲームでは、追加できそうな簡単な何かを必要とした
- DX11、理想的には拡張に依存しない
- 小道具メッシュレベルで動作し、コンテンツパイプラインへの変更がない
- 譲歩して、階層的Zバッファのアイデアを再検討する[@Shopf2008MarchFroblinsSimulationa; Hill and Collin 2011Hill, S. and Collin, D. 2011. Practical, Dynamic Visibility for Games. GPU Pro 2 20. https://blog.selfshadow.com/publications/practical-visibility/.]
Hi-Zカリング#
- Zバッファに遮蔽物をレンダリングする
- ミップチェインを作るためにZバッファをダウンスケールする
- すべての小道具のバウンディングボックスを追加し、コンピュートシェーダに渡す
- Zバッファのミップチェインを用いてカリングを行う
- 可視の小道具をレンダリングするのにDrawIndexedInstancedIndirectを使う
- やったぜ。
Hi-Zカリング#
- ドローコール
- インデックス数
- インスタンス数
- インデックスバッファのオフセット
- 頂点バッファのオフセット
- インスタンスバッファのオフセット
- ドローコールリスト
- メッシュリスト
- インスタンスリスト
階層的Zバッファ#
- 生成は素直で、空のピクセルシェーダを用いて大きなメッシュをレンダリングする
- max()を用いてN回ダウンスケールするのにコンピュートまたはピクセルシェーダを使う
- 一度に4つの深度サンプルを得るためにGather4を使うことができる
- または、D3D11_FILTER_MAXIMUM_MIN_MAG_MIP_POINTテクスチャフィルタモードを使う(DX11.2が必要)
大きな遮蔽物メッシュ(壁、大きな小道具、地形)を選び、D16のZバッファにレンダリングする。16ビット精度が十分か否かは状況やカリング範囲などに依存するだろう。一般に、カメラに近いほぼ精度が良い。その範囲に渡って精度を改善するために反転Zバッファを試すことができるだろう。
max操作を用いてミップチェインを作るために続いてZバッファをダウンスケールする。これは低ミップで遮蔽物の”ジオメトリ”を大幅に変化させるだろうが、より正確なオクルージョンを提供する(見えている小道具をカリングしない)
すべての4サンプルを得るためにGatherを用いることができるだろうが、これはミップをサポートせず、ミップあたりSRV/RTVを生成する必要があるだろう。または、DX11.2を使えるなら、最大テクスチャフィルタモードを用いる。これはミップマップミップマップをサポートし、Gather4より使うVGPRが少ない(このtipについてSebastian Aaltonenに感謝)
Zバッファのレンダリングはレンダリングフレームへの追加コストである(頂点bound)。これは、低メッシュLODをレンダリングしたり、フルスクリーンをレンダリングしてGプリパスをレンダリングするときに使ってオーバーヘッドを軽減させる、といった軽減策がある。入力としてZバッファをバインドするとdecompressされて、後続のZ操作をより高価にすることを、いくつかのGPUでは気に留めておく価値がある(例えば、Gプリパスに影響を与えるかもしれない)。
小道具のカリング#
小道具のカリング#
小道具のカリング#
- ミップのサポートがないので、一度に4サンプルを得るためにGather4を使用できない
- DX11はSampleLevelCmpと同等のものを公開していない :-(
- (DX11.2でなら)D3D11_FILTER_MAXIMUM_MIN_MAG_MIP_POINTテクスチャフィルタモードを使う
- そうでなければ、4つのSampleLevel命令を使う
- [Wihlidal 2016Wihlidal, G. 2016. Optimizing the Graphics Pipeline With Compute. Game Developers Conference. https://gdcvault.com/play/1023109/Optimizing-the-Graphics-Pipeline-With.]や[Haar and Aaltonen 2015Haar, U. and Aaltonen, S. 2015. GPU-Driven Rendering Pipelines. Advances in Real-Time Rendering in Games course. ACM SIGGRAPH. https://advances.realtimerendering.com/s2015/index.html.]のようにトライアングルをフィルタできないが、錐台カリングやスクリーン上メッシュサイズカリングのようないくつかの追加の処理を行うことができる
小道具のカリング#
小道具のカリング#
- オクルージョンパスは遮蔽された小道具に対するインスタンスバッファにある”ギャップ”をそのままにするだろう
- それらのレンダリングを回避できないので、取り除く必要がある
小道具のカリング#
- ナイーブな最初の試み: AppendBufferを使う
- インスタンスがバッチされず、順番が保存されないときに上手く動作する
Stream compaction#
- 我々は我々自身の出力を取り扱う必要がある
- インスタンスデータを出力する代わりに、predicatesを出力する
Stream compaction#
- stream compactionを実装する必要があるだろう
- Parallel prefix sum (scan) [Blelloch 1990Blelloch, G. E. 1990. Prefix Sums and Their Applications. https://www.cs.cmu.edu/~scandal/papers/CMU-CS-90-190.html.]
Stream compaction#
Stream compaction#
Stream compaction#
Stream compaction#
- コンピュートシェーダで実装され、[Harris et al. 2005Harris, M., Sengupta, S. and Owens, J. D. 2005. Parallel Prefix Sum (Scan) with CUDA. GPU Gems 3.]を採用する
- 1024のスレッドグループひとつで最大2048のインスタンスを処理する
- インスタンスデータは定期的なメモリアクセスを避けるためにグループ共有メモリにコピーされる
- この方法は効率的な処理(O(n)の操作)だがグループ共有メモリバンクの衝突を引き起こす
GPU駆動レンダリング#
- stream compactionパスはstructured bufferに可視のインスタンスを書き込む
- インスタンス数とオフセットでarguments bufferも更新する
- 1回のDrawIndexedInstancedの呼び出しで可視の小道具を描画できる
Taking it further#
Batch all the things#
- 理想的には、パスあたりひとつのドローコールですべてを描画したい
- 大きなバッファにすべてのメッシュLODをバッチする
- マテリアル情報を定数/構造化バッファにバッチする
- テクスチャをテクスチャ配列にバッチする
- DirectX 11はMultiDrawIndexedInstancedIndirectをサポートしていない :-(
- AMDとNVIDIAはドライバ拡張を介してサポートしている(残念ながらIntelはサポートしていない)
Mesh batching#
Materials#
- DX11ではバインドレスにできないので、我々はテクスチャ配列にすべてのテクスチャを追加する必要がある
- 非標準のテクスチャサイズがこれを難しくするかもしれない
- 配列あたり最大2048のテクスチャ
- 定数バッファに格納される、または、頂点シェーダから渡されるあるIDでアクセスされる
Materials#
- ドローコールごとの個別の定数バッファを使う選択肢がない
- 単一の定数バッファにマテリアル情報をバッチする必要がある
Manual vertex fetch#
- ちょうどその頃、我々はSkySagaにおいてインターリーブでない頂点バッファに切り替えた
- いくつかのアーキテクチャでパフォーマンスが優れる
- 個別のストリームが再利用をしやすくする(シャドウパス、Gプリパス、など)
- 手動頂点フェッチが魅力的な選択肢だった
- 属性の頻度の変化
- 手動頂点フェッチをGPU駆動レンダリングスキームに要素に含めた
Fetching data in the vertex shader#
- 各ドローコールは(Multi)DrawIndexedInstancedIndirectを用いて正しくレンダリングするのに必要なバッチされたバッファにすべてのオフセットを含む
- SV_VertexIDやSV_InstanceIDには適用されない
- DX11にはgl_DrawIDがない
- 頂点シェーダでドローコールIDを読み出す方法が必要となる
Fetching data in the vertex shader#
- インデックスは手動でフェッチされない(オフセットが適用される)
- インデックスバッファにドローコールIDを格納するアイデアをなんとなく考えていた(16ビット)
- Skysagaの小道具はcubic meterあたり約1000のトライアングルがある
- 特に32ビットのインデックスバッファで、範囲全体を使わない場合、実行可能な選択肢となり得るだろう
- 最終的に、コンピュートシェーダで書き出したドローコールIDを持つper instanceストリームを追加した
- Input Assemblerにこれをバインドすると、インスタンスのオフセットが正しく適用される
Rendering more than 2048 instances#
- 元のstream compactionテクニックは2048個のインスタンスに制限される
- これを解決するために、我々は多数のスレッドグループに渡り処理を広げる必要がある
- segmented scansを用いる[Blelloch 1990Blelloch, G. E. 1990. Prefix Sums and Their Applications. https://www.cs.cmu.edu/~scandal/papers/CMU-CS-90-190.html.; Harris et al. 2005Harris, M., Sengupta, S. and Owens, J. D. 2005. Parallel Prefix Sum (Scan) with CUDA. GPU Gems 3.]
Culling and stream compaction#
- メッシュLODは通常のメッシュと見なされ、カリングやstream compactionの面では変更はない
- コンピュートシェーダは可視であるメッシュLODのインスタンス数とインスタンスバッファへのオフセットを埋める
- そうして、単一のドローコールですべてを描画する
Results for GTX 970#
Results for HD 4000#
Performance improvements#
- オクルージョンシステムはほぼ帯域幅で制限される
- 最も高価なパスはカリングパスとインスタンスデータのコピーパスである
- これらを改善するために、以下を必要とする
- 使用されるメモリ量を削減する
- レイテンシーを隠すためにGPUを活用する
Performance improvements#
- システムを改善するためにあなたのゲームについての知識を使う
- バウンディングボックスをfloat3 bboxMinとuint bboxSizeとして格納する(帯域幅+VGPR)
- インスタンスごとに4x3行列を格納する(帯域幅とVGPRを節約する)
- 使い方に応じてインスタンスデータストリームを分ける
- より小さなスレッドグループに対して可能な2パススキャン
- インスタンスバッファのオフセットを計算するための並列化された最終パス(GTX970で18倍の高速化)
- グループ共有メモリの衝突はこのインスタンス数では重大であるように見えない
- オクルージョンパスは0.5msから0.25msにまで落ちた(GTX 970)
Performance improvements#
Issues#
- MultiDrawIndirectはDX11においてNVIDIAとAMDによってのみサポートされ、Intelではサポートされない
- IntelのGPUでは、DrawIndexedInstancedIndirectがループ中で使われることができる
- オクルージョンはper viewであるため、技術的に、シャドウビューごとに返さなければならないだろう
- メインビューのオクルージョン結果を用いることができるだろう(可視でないオブジェクトにシャドウをキャストしない)
Occlusion solved then?#
- 残念ながら、解決してない
- 保守的Hi-Zバッファフィルタリングは遮蔽物のジオメトリを大きく”変える”
- 大きなオブジェクトのオクルージョンは十分に正確でない
- 大きな遮蔽物がないとしたら?前フレームのZバッファ&再投影[Haar and Aaltonen 2015Haar, U. and Aaltonen, S. 2015. GPU-Driven Rendering Pipelines. Advances in Real-Time Rendering in Games course. ACM SIGGRAPH. https://advances.realtimerendering.com/s2015/index.html.]
- 大きな遮蔽物、閉じた空間、小さな小道具があるシーンで良好。人によって違うだろうけど
- あなたのゲームに追加するのは比較的簡単