60.3K Views
July 15, 22
スライド概要
Game Creators Conference 2016の講演で使用したスライドです。
『GPU駆動レンダリングへの取り組み』 - 三嶋 仁 / 清水 昭尋
株式会社カプコンが誇るゲームエンジン「RE ENGINE」を開発している技術研究統括によるカプコン公式アカウントです。 これまでの技術カンファレンスなどで行った講演資料を公開しています。 【CAPCOM オープンカンファレンス プロフェッショナル RE:2023】 https://www.capcom-games.com/coc/2023/ 【CAPCOM オープンカンファレンス RE:2022】 https://www.capcom.co.jp/RE2022/ 【CAPCOM オープンカンファレンス RE:2019】 http://www.capcom.co.jp/RE2019/
GPU駆動レンダリングへの 取り組み 株式会社カプコン 三嶋仁/ 清水昭尋
GCC 2016はオープンなイベントです。 このセッションは 写真撮影とSNS投稿を歓迎します。
今回の内容 • レンダリングパイプライン • メッシュレンダラ • ライトカリング
レンダリングパイプライン • マルチプラットフォーム向け • 中間描画命令 • ネイティブ描画命令 • リソース管理
レンダリングパイプライン 中間描画命令 描画命令の ネイティブ ソート 描画命令
中間描画命令 • プログラマが使用 • 内製の描画命令 • マルチスレッド • 描画コンテキストで構築 • 命令の再利用
中間描画命令 • 基本の命令はDirectX11風 • DX12世代の機能 • Multi DrawやAsync Compute… • 32Bit定数の低コスト利用 • 一般命令 • Copy, Update Buffer, • Draw, Dispatchなどの • 優先順位の指定 Header 96 bit Attribute(32 bit) Type Id(8bit) Command Size(24bit) Command Priority(64bit) Reserve(8bit) Layer(10bit) Segment(6bit) Priority(32bit) Thread Id(8bit)
描画コンテキスト • 描画命令を作るインターフェイス • 各スレッドごとに存在 • ユニークなスレッドIDを保持 • 優先順位の設定 • 各種の描画リソースの設定 • シェーダとリソースの整合性のチェック • 中間描画命令を作成
描画命令のリソースの種類 • グラフィックスに必要な要素 • Texture, • ShaderBinary, • ShaderResourceView, • UnordredAccessView, • VertexBuffer, • IndexBuffer… Draw VertexBuffer IndexBuffer Texture… Sampler… ConstantBuffer… RenderTargetView DepthStencilView
描画命令のリソース管理 • オブジェクトの参照状態は参照カウントで管理 • リソースの生存管理 • 描画が完了するまで生存を保証しなければならない • 参照カウンタとは異なる別の生存保障の管理が必要 • 生存保障の管理は、保障フレームを使用 • 保障フレームは、参照時に現在フレーム+nを代入 • 保障フレームが現在のフレームを超えて、かつ、参照カウント が0のものが初めてメモリ上から解放 • 生存保障の管理は描画命令を発行時
描画命令のリソース構成 • 複数のリソースから構成 • セットアップコストが高い • リソースの収集 • 収集したリソースに保障フレームの更新 • インゲーム中は大量の描画命令 • セットアップコストは無視できない Draw VertexBuffer IndexBuffer Texture… Sampler… ConstantBuffer… RenderTargetView DepthStencilView
描画命令のリソースの再利用 • 経験的に描画命令は、前回の描画時と大差がない • メッシュの描画 • 毎回ほとんど同じ組み合わせのリソースを送る • 更新されるのはリソースの中身で、ハンドルは固定 • パーティクルの描画 • 使用するリソースは事前に決まっている • 前回と同じとして再利用 • CPUのパフォーマンスを向上
リソースの拡張 • リソースを意味のある単位に集約 • 初期化時に作成 • 複数フレームで再利用 • 内部のリソースの管理 TargetState GeometryState Viewports VertexBuffer RTVs IndexBuffer • 作成時 DSV 所持するリソースの参照カウンタのみを 作成時に更新 • 解放時 PipelineState PipelineResourceTable 保障を所持するリソースに反映 Shader Binary Constant Buffers • 更新チェックは劇手に低下 GPU States ShaderResourceViews • 実行時作成 • PipelineResourceTable • 実行時に必要なものが決定されるため • 再利用で描画コストを削減 UnorderedAccessViews Samplers
送信する描画リソース • Draw命令なら、4つのリソース Draw VertexBuffer IndexBuffer Texture… Sampler… ConstantBuffer… RenderTargetView DepthStencilView Draw TargetState PipelineState PipelineResourceTable GeometryState 頂点数など
中間描画命令のマルチスレッド • ネイティブ描画命令は中間描画命令の優先順に実行 • 中間描画命令の発行は、実際の描画と分離が可能 • 半透明と不透明の命令の実行 中間描画命令 スレッド0 不透明 不透明 半透明 スレッド1 半透明 半透明 不透明 ソート後の命令 不透明 不透明 不透明 半透明 半透明 半透明
中間描画命令のマルチスレッド • 優先順位の応用例 • 描画順番の最適化 • EarlyZを効かせるための、手前順の描画 • 半透明の整合性のための、奥順番の描画 • データのコピータイミングの制御 • コンピュートシェーダの依存リソースの調整 • チェック処理の遅延
コンピュートシェーダの依存リソースの調整 • CSでUAVとSRVがピンポンする箇所はストール • GPUソートや、シミュレーションなどで問題 • 依存したリソースが連続処理しないように優先順位を調整 • 回避するために、他のCSと混ぜて動かす 中間描画命令 スレッド0 Priority 0 BufA 書き出し Priority 1 BufA読み込み スレッド1 Priority 0 BufB 読み込み Priority 1 BufB書き込み ソート後の命令 BufA 書き出し BufB 読み込み BufA 読み込み BufB 書き込み
マルチスレッド • 実際のエンジンの動作 ライトやメッ シュの情報更新 • 3段階の構成 描画パスの構築 描画処理
ライトやメッシュの情報更新 • マルチスレッドで実際の描画を行う前の前処理 • ライト • 影を落とすかの判定し、描画パスの登録/解除 • 影のキャッシュの更新の可否 • メッシュ • 詳しくは後半に!
描画パスの構築 • 現在のフレームで利用する描画パスの収集 • • • • • GBuffer, DeferredLighting ShadowCast PostProcess Etc…. • 描画パスの構築は、シングルで動作 • 優先順位の最上位の値(LayerId)を作成 • 後続の描画処理は、パスの優先順位を利用することで、正確な描画タ イミングを作り出せる • 描画パスが発行する描画命令は、マルチスレッド • 描画パス先頭で行う画面のクリアやコピーなど
描画処理 • マルチスレッドで処理 • 個々の描画処理は相互依存しない 描画が依存するのは、描画リソースと描画パス(優先順位) • 各描画処理が収集した描画パスから中間描画命令を作成
命令のキャプチャとプレイ • 一度作った描画命令を再利用 メモリの設定なども省けるため最速 • 使用例 シャドウキャストメッシュ、ポストプロセス • キャプチャとプレイの制約 描画パスに依存したリソースは、別の描画パスで使用不可能 • ローカルなコンスタントバッファをエンジンが持つグローバル化コン スタントバッファに変えるなど • 例 CameraConstantBufferはシーンに一つしかない など・・・
レンダリングパイプライン 中間描画命令 描画命令の ネイティブ描 ソート 画命令
ソート • 正しい命令順番に並び替える • 安定したソートを使用 • 同一優先順位、同一スレッドの描画命令は発行順番を維持 • 並び替えはマルチスレッド 中間描画命令 スレッド0 不透明 不透明 半透明 スレッド1 半透明 半透明 不透明 ソート後の命令 不透明 不透明 不透明 半透明 半透明 半透明
レンダリングパイプライン 中間描画命令 描画命令の ネイティブ描 ソート 画命令
ネイティブ描画命令 • 中間描画命令をグラフィックスAPIに渡す • DirectX11,DirectX12, Mantle, etc… • マルチスレッドで作成 メインスレッド ゲーム処理 レンダリングスレッド0 レンダリングスレッドN セットアップ ネイティブ描画コマンド0 ネイティブ描画コマンドn
リソースからネイティブ描画命令を支援 • 再利用可能な処理は事前に最適化 • 中間描画命令を作成する段階で必要な情報を収集 プラットフォームによって実装が異なる • DX11ならリソースの単純な配列化 • DX12ならdescriptor tableの構築 • リソースを使いまわすことを前提に設計
リソースからネイティブ描画命令を支援 • 更新頻度でリソースの処理を分離 • 更新される可能性があるリソース • ConstantBuffer • 位置情報やカメラ情報など細かな単位の更新に使用されており、ハンド ルが可変なため • プラットフォームによりけり実装が異なる • Texture • ストリーミングテクスチャなどで、ハンドルが可変なため • 更新されないリソース • RWTexture,Buffer,RWBuffer,Sampler • ネイティブ描画命令の作成の際、CB,Textureのみ収集
ここまでのまとめ • マルチスレッドの描画命令の作成 • リソースの集約と再利用 • よりパフォーマンスアップするためには?
メッシュレンダラの目標 • 従来よりも多くのジオメトリを高速に処理する • 描画コール数の削減 • 効果的なカリング • GPUによるCPU負荷の肩代わり • CPU SIMDの活用 • CPU,GPUキャッシュフレンドリ
新しいエンジンとしての課題 • 汎用設計でも高い性能 • 拡張性を閉ざさない • マルチプラットフォーム • 最新のAPIに対応できるキャパシティ • Mantle, DirectX12, etc… • 下はDirectX11世代までを考慮 • ゼロドライバオーバーヘッド[1] への挑戦
メッシュレンダラの取り組み • 描画API の Indirect 化 • すべてのメッシュが Indirect 命令による描画 • 描画コールの削減 • Multi-Draw, Instancing • マージメッシュ • GPUカリング
描画API の Indirect 化
Indirect 命令とは • 描画 API 引数をGPUのバッファで指定 • GPUから引数制御が可能 • 引数データがCPU側の場合GPUへの転送が必要 • バッファのオフセットはCPUから指定 void DrawIndexedInstanced( uint IndexCountPerInstance, uint InstanceCount, uint StartIndexLocation, uint BaseVertexLocation, uint StartInstanceLocation); void DrawIndexedInstancedIndirect( ID3D11Buffer* pBufferForArgs, uint AlignedByteOffsetForArgs);
Indirect 命令のための引数 • ジオメトリ描画のためのデータ(20byte) • このデータの管理に注力 struct DrawIndexedInstancedArgs { uint IndexCountPerInstance; uint InstanceCount; uint StartIndexLocation; uint BaseVertexLocation; uint StartInstanceLocation; };
Indirect 引数の管理 • VRAMの一部領域を割り当て • 割り当てた領域をさらにアロケーターで管理 • 単一リソースとすることでCSアクセスを容易に • バッファ使用戦略 • メッシュ単位でLOD、影モデル含めた情報を転送 • LOD切替時はバッファオフセットを変更 • メッシュ状態(パーツ等)の切替時は再転送
Indirect 引数のメモリ使用図 DrawIndexedInstancedArgs LOD0 Mat A VRAM Mat B Mesh 0 Mat B LOD1 Mat C Mat A Mat B Mesh 3 LOD2 Mat B Mat A Mesh 2 影LOD0 Mat D Mat E Mesh 4
Indirect 命令は使えるか • CPU負荷に大きな変化は無い? • PCの場合はドライバ実装による(遅くなることも?) • バッファ管理のオーバーヘッド • ポテンシャル • Multi-Draw の使用 • GPU駆動のメッシュ描画パイプライン[2]
描画コールの削減
Multi-Draw API とは • 複数の Indirect 描画を一度の描画コールで実行 • DirectX12, OpenGL4.4, 拡張DirectX11[4]は 同様の機能が使用可能 void DrawIndexedInstancedIndirect( ID3D11Buffer* pBufferForArgs, uint AlignedByteOffsetForArgs); void MultiDrawIndexedInstancedIndirect ( ID3D11Buffer* pBufferForArgs, uint AlignedByteOffsetForArgs, uint ArgsCount); ※こんなAPIは存在しません
メッシュ描画の素朴な実装 Mesh 1 Material A Material B 描画コール 描画コール Material B 描画コール Material C 描画コール Mesh 2 描画コール Material B 描画コール Material B 描画コール Material B 描画コール Material C Material D Material D 描画コール 描画コール
Multi-Draw を使用した実装 Mesh 1 Material A DrawIndexedInstancedArgs Material B Material B Material C Mesh 2 Material B Material B Material B Material C Material D Material D 描画コール 描画コール 描画コール 描画コール 描画コール 描画コール
Multi-Draw は使えるか • 対応プラットフォームの問題 • DirectX11では複数のIndirect命令に置き換え • Indirect命令を使用するのであれば必ず考えるべき • 描画コールをシンプルに削減可能
Instancing に関して • 同一描画可能インスタンスをハッシュマップ管理 • 自動 Instancing • CPUカリング後のインスタンス数をGPUへ • Indirect バッファの InstanceCount を CS で制御 • インスタンス情報のバッファ化 • CBからStructuredBufferへ変更 インスタンス情報 World Matrix Previous World Matrix Joint Offset Previous Joint Offset Shader Parameters
インスタンス情報の管理 • レンダラが全インスタンス情報を管理 • メッシュ固有オフセットと SV_InstanceID で引く オフセット インスタンス情報 へのインデックス インスタンス情報 SV_InstanceID 2 8 0 1 2 3 1 0 5 3 12 7 13 15 11 …
マージメッシュの検証 • Multi-Draw によりジオメトリ描画が柔軟に • メッシュを跨いだ描画コールのマージも可能では? • 20byteの Indirect 引数を連結していく • 必要なリソースの結合 • StartIndexLocation, BaseVertexLocation の再計算 • StartInstanceLocation で何番目のバッファかを指定
マージメッシュの実装? Mesh 1 Material A DrawIndexedInstancedArgs Material B Material B Material C Mesh 2 Material B Material B Material B Material C Material D Material D 描画コール 描画コール 描画コール 描画コール
Instancing, マージメッシュの課題 • 描画コールは減るがオーバードロー負荷が目立つ • ピクセル負荷の高いものは致命的 • CPUとGPU負荷のトレードオフ • シーンによって効果的かどうかの選択が必要 • 実装コストに結果が見合うのか? • 特にマージメッシュはリソースの結合が必要 • VB, IB, CB, Texture, etc… • Bindless Texture の使用
マージメッシュは使えるか • マテリアルがユーザーメイドのエンジンでは大変 • リソースの制約等の要件を満たすメッシュは… →シャドウマップ描画! • 描画不具合時のデバッグが難しくなる?
GPU カリング
新しいエンジン初期のカリングの流れ • カリングはすべてCPU処理 アプリケーション層 疎粒度な シーンカリング エンジン層 フラスタムカリング & 微小面積カリング 中間描画命令
カリング処理の再考 • フラスタムカリング • それなりのCPU負荷 • GPUの方が得意そうな処理? • オクルージョンカリング • 高速なCPU処理の実装難度が高い • GPUの方が得意そうな処理?
GPU駆動のカリングへ • 理想 • CPUのコストをGPUへオフロード • メッシュはすべてGPUへ素通し、GPUでカリング • 現実 • 中間描画命令の肥大化、大量の描画コール • データのGPUへのコピーコスト増
オクルージョンカリングの導入 • アルゴリズムはHiZを利用したものを使用[3] • GBufferへの描画対象メッシュのみ • ライトのPreZ処理は無い • Gbufferパスの前までにCSで実行 • 同一フレームのGPUで処理するため遅延無し
GPUオクルージョンカリングの流れ • PreZでHiZを作成 • これはライトカリング等でも利用 • GBufferパスの頂点、ピクセル負荷を下げる PreZ Pass Occlusion Culling Pass GBuffer Pass 1. 遮蔽物による Z の作成 2. Z から HiZ を作成 HiZ と対象オブジェクトAABBで 遮蔽テスト、Indirect 引数のイ ンスタンス数をデクリメント インスタンス数 0 であれば 描画されない GPU処理フロー
GPUオクルージョンカリングは使えるか • カリング処理によるGPU負荷増 • 主にジオメトリの頂点負荷とのトレードオフ • GPUへのデータ転送負荷は最小限に • 描画コールの数は減らない • CPUで遮蔽情報をリードする必要がある
GPUフラスタムカリングの検証 • CPUでカリングしないため中間描画命令が増加 • マージメッシュを使ったシャドウマップ描画への適用 DrawIndexedInstancedArgs フラスタム0 フラスタム1 Frustum Culling Pass Shadow Cast Pass フラスタム2 ライトフラスタムと対象オブ ジェクトAABBで遮蔽テスト Indirect 引数のインスタンス数 をインクリメント インスタンス数 0 であれば 描画されない フラスタム3 GPU処理フロー
GPUカリングは使えるか • CPU, GPUでのカリング処理のオーバーヘッドが 大きくなってしまうと意味がない • CSカリングによるGPU負荷の増加 • 非同期コンピュートで負荷を隠蔽できるか?
GPUカリングの展望 • フレーム遅延無しでのCPUオクルージョンクエリ • 非同期コンピュートラスタライザ • CPU, GPU負荷双方の削減可能性
メッシュレンダラのまとめ • エンジンで搭載されているもの • Multi-Draw, Instancing • GPU オクルージョンカリング • エンジン搭載を検証中のもの • マージメッシュ(シャドウマップ描画) • GPU フラスタムカリング(シャドウマップ描画)
ライトカリング
2段階のアプローチ • GPU上でのオクルージョンカリング • PreZで作られた深度を利用 • フレーム遅延 • 描画時のライトカリング • GBufferで作られた深度からHiZを作成 • CSで実行
よりよいスポットライトのカリング
ライトカリング
実装 • 従来の視線をFrustumからView空間のAABBへ • ConeをAABBからView空間のFrustumへ • 対象物を逆にすることで適切なカリングが可能
まとめと課題 • 利点 • ライトの照明範囲を任意の凸包体で表現可能 • 問題点 • 視推台をAABBにすることで誤った判定が発生
最後のまとめ • レンダリングパイプラインの紹介 • メッシュ • ライトカリング
Thank you! ご清聴ありがとうございました 株式会社カプコンのセッション • 『STREET FIGHTER V Art Direction』 • 17:00 – 18:00 @ E705 亀井 敏征
References 1. Approaching Zero Driver Overhead in OpenGL http://gdcvault.com/play/1020791/ 2. GPU-Driven Rendering Pipelines http://advances.realtimerendering.com/s2015/aaltonenhaar_siggraph2015_com bined_final_footer_220dpi.pdf 3. Practical, Dynamic Visibility for Games http://blog.selfshadow.com/publications/practical-visibility/ 4. AMD GPU Services (AGS) Library http://gpuopen.com/gaming-product/amd-gpu-services-ags-library/ 5. Practical Clustered Shading http://www.humus.name/Articles/PracticalClusteredShading.pdf