拙訳
バリア(Barriers)
- いまだにハイレベルAPIでパフォーマンスが落ちる原因、第一位。
- でも、バリアを正しくするのは難しい。
問題(Issues)
- バリアは足りないとデータが壊れる。
- バリアが多すぎると一括処理や状態遷移に支障が出る。
- 大抵のバリアの間違いはデバッグレイヤやバリデーションレイヤで補足できる。
解決策(Solutions)
- 手動で置いていく。
- シンプルなエンジンならうまくいく。
- すぐにゴチャゴチャになってしまう。
- “シーンの背後”にバリアを自動生成する。
- 追尾しているリソースごと。
- 正しくするのが難しい。
- “要求に応じて”遷移させると、バッチ処理不足や時折の最適でないバリア設置を引き起こす。
- D3D12でもレンダパスを導入する。
- ポータビリティが向上する。
- フレームグラフ
- 依存関係を各パスごとに解析する。
- 各リソースのスコープが定まるので、メモリのエイリアスを付けることができる。
- ケーススタディ: TiagoやYuriyのトーク(GDC2017)
コピーキュー(Copy Queue)
- PCIe上でコピーを行うのに特化した専用ハードウェア。
- 他のキューとは独立して処理する。
ものは簡単で、システムメモリからローカル1にコピーするなら、コピーキューを使うべし
- ストリーミングに最適。
- mGPUはP2Pで転送する。
- コピーキューを待たないようにするために、GPUへ十分な数の仕事を回せているかを確かめる。
- ローカルメモリが必要になる前に、理想を言えば数フレーム前だが、できるだけ早めにコピーを開始する。
- ローカルからローカルへのコピーにはコピーキューを使わない。
- かわりに、グラフィックスキューやコンピュートキューを使う。
- コピーキューはPCIeの速度で動作する。
- (ただし、メモリのデフラグのような”裏”で行うならコピーキューを使うことができる。)
メモリのデフラグ(Memory Defragging)
- 例えば1フレームあたり1%の帯域幅を使うとしたら、かわりにコピーキューを使う。
- グラフィックスキューをレンダリングに専念させるため。
- コピーキューがビジーでないフレームでやる。
パイプラインシェーダ管理(Pipeline Shader Management)
- 組み合わせ爆発を最小限に留めるようにする。
- 使わないものは早めに取り除く。
- 適切なところでウーバーシェーダーを検討する。
- D3D12ではroot constantsを使う。
- Vulkanではspecialization constantsを使う。
- その場でPSOを作る場合、十分に前もって行う。
リソースマネジメント(Resource Management)
- あなたはリソース管理の全権を得ている。
- どれだけのメモリがGPUの物理メモリにあるか。
- どれだけのメモリがゲームに必要か。
- ローカルメモリが容量を超過した割り当てをしていないことを保証するのは自己責任。
容量超過しそうなら対処する(Take action if you do end up oversubscribing)
- 容量超過状態はパフォーマンス上の鋭い変動を引き起こす可能性がある。
- 原因:
- ブラウザなどのメモリ負荷の高い他のアプリケーションによるフォーカス奪取。
- ユーザーによる解像度や品質の変更。
- 1GBや2GBなどのハードウェアでは上限を設けることを検討する。
どれだけのメモリが使えるか
- D3D12なら、利用可能なメモリ容量は
IDXGIAdapter3::QueryVideoMemoryInfo()で取れる。 - budgetが突然失われることもありうる。
- 各フレームでポールするかコールバック登録する。
ローカルメモリを制限するにはどうすればよいか
- パフォーマンスに与える影響が大きくないアセットをローカルメモリから移す。
- システムメモリのオーバーフローヒープに入れる。
- MIPレベル上位を削る
ローカルメモリ外へのアセットの移動
- ローカルのコピーを解放する。
- システムメモリに移す前にリソースのアクセスパターンを理解しておく。
- 読み込みは一度だけ。
- 高い局所性のある予測可能なアクセスパターンはgood。
上位のMIPを削る
- 最大70%のメモリが節約される。
- 独断的にやれば、ビジュアルにいくらかの差異が生まれる。
- 理知的にやれば、ビジュアルに差異は生まれない。
- テクスチャがひとつのヒープにあるリソースに置かれているなら、実装するのはより簡単になる。
Try testing with two instances of your title
MakeResident
- MakeResidentは失敗し得る。
- きちんとハンドリングしよう。
- MakeResidentは同期呼び出しである。
- すべてのリソースが準備できるまで返ってこない。
- バッチにまとめて、非同期に実行する。
- 小さいバッチは非効率 -> 大量の小さなページング処理。
- Evictは比較的安価。
- そのコストは次のMakeResident呼び出しまで遅延されるだろう。
結論
- 第一級市民として新しい概念を取り入れる。
- マルチスレッディング
- 複数のキュー
- レンダパス + フレームグラフ
- 明示的なリソース管理
- 将来を見据えていないのなら、D3D12やVulkanを最大限活用することはないだろう。
- キューやバリアを指揮するため、高い視野で用いる。
Footnotes
-
訳注:ここでいう”ローカル”は、GPUから見て近場にあるということ。 ↩