Skip to content
Go back

D3D12 & Vulkan Done Right

· Updated:

slides video

拙訳

バリア(Barriers)

  • いまだにハイレベルAPIでパフォーマンスが落ちる原因、第一位。
  • でも、バリアを正しくするのは難しい。

問題(Issues)

  • バリアは足りないとデータが壊れるcorruption
  • バリアが多すぎると一括処理や状態遷移に支障が出る。
  • 大抵のバリアの間違いはデバッグレイヤやバリデーションレイヤで補足できる。

解決策(Solutions)

  • 手動で置いていく。
    • シンプルなエンジンならうまくいく。
    • すぐにゴチャゴチャになってしまう。
  • シーンの背後behind the scenes”にバリアを自動生成する。
    • 追尾しているリソースごと。
    • 正しくするのが難しい。
    • 要求に応じてon demand”遷移させると、バッチ処理不足や時折の最適でないバリア設置を引き起こす。
  • D3D12でもレンダパスを導入する。
    • ポータビリティが向上する。
  • フレームグラフFrame graph
    • 依存関係を各パスごとに解析する。
    • 各リソースのスコープが定まるので、メモリのエイリアスを付けることができる。
    • ケーススタディ: TiagoやYuriyのトーク(GDC2017)

コピーキュー(Copy Queue)

  • PCIe上でコピーを行うのに特化した専用ハードウェア。
    • 他のキューとは独立して処理する。

ものは簡単で、システムメモリからローカル1にコピーするなら、コピーキューを使うべし

  • ストリーミングに最適。
  • mGPUはP2Pで転送する。
  • コピーキューを待たないようにするために、GPUへ十分な数の仕事を回せているかを確かめる。
    • ローカルメモリが必要になる前に、理想を言えば数フレーム前だが、できるだけ早めにコピーを開始する。
  • ローカルからローカルへのコピーにはコピーキューを使わない。
    • かわりに、グラフィックスキューやコンピュートキューを使う。
    • コピーキューはPCIeの速度で動作する。
    • (ただし、メモリのデフラグのような”background”で行うならコピーキューを使うことができる。)

メモリのデフラグ(Memory Defragging)

  • 例えば1フレームあたり1%の帯域幅を使うとしたら、かわりにコピーキューを使う。
    • グラフィックスキューをレンダリングに専念させるため。
    • コピーキューがビジーでないフレームでやる。

パイプラインシェーダ管理(Pipeline Shader Management)

  • 組み合わせ爆発を最小限に留めるようにする。
    • 使わないものは早めに取り除くprune
    • 適切なところでウーバーシェーダーを検討する。
    • D3D12ではroot constantsを使う。
    • Vulkanではspecialization constantsを使う。
  • その場でPSOを作る場合、十分に前もって行う。

リソースマネジメント(Resource Management)

  • あなたはリソース管理の全権を得ている。
    • どれだけのメモリがGPUの物理メモリにあるか。
    • どれだけのメモリがゲームに必要か。
    • ローカルメモリが容量を超過した割り当てoversubscribeをしていないことを保証するのは自己責任。

容量超過しそうなら対処する(Take action if you do end up oversubscribing)

  • 容量超過状態はパフォーマンス上の鋭い変動fluctuationsを引き起こす可能性がある。
  • 原因:
    • ブラウザなどのメモリ負荷の高いmemory intensive他のアプリケーションによるフォーカス奪取。
    • ユーザーによる解像度や品質の変更。
  • 1GBや2GBなどのハードウェアでは上限を設けることを検討する。

どれだけのメモリが使えるか

  • D3D12なら、利用可能なメモリ容量はIDXGIAdapter3::QueryVideoMemoryInfo()で取れる。
  • budgetが突然失われることもありうる。
  • 各フレームでポールするかコールバック登録する。

ローカルメモリを制限するにはどうすればよいか

  • パフォーマンスに与える影響が大きくないnon performance-criticalアセットをローカルメモリから移す。
    • システムメモリのオーバーフローヒープに入れる。
  • MIPレベル上位を削るDrop top mip levels
ローカルメモリ外へのアセットの移動
  • ローカルのコピーを解放する。
  • システムメモリに移す前にリソースのアクセスパターンを理解しておく。
    • 読み込みは一度だけ。
    • 高い局所性のある予測可能なアクセスパターンはgood。
上位のMIPを削る
  • 最大70%のメモリが節約される。
    • 独断的にdogmaticallyやれば、ビジュアルにいくらかの差異が生まれる。
    • 理知的にintelligentlyやれば、ビジュアルに差異は生まれない。
    • テクスチャがひとつのヒープにあるリソースに置かれているなら、実装するのはより簡単になる。

Try testing with two instances of your title

MakeResident

  • MakeResidentは失敗し得る。
    • きちんとハンドリングしよう。
  • MakeResidentは同期呼び出しである。
    • すべてのリソースが準備できるまで返ってこない。
    • バッチにまとめて、非同期に実行する。
    • 小さいバッチは非効率 -> 大量の小さなページング処理。
  • Evictは比較的安価。
    • そのコストは次のMakeResident呼び出しまで遅延されるだろう。

結論

  • 第一級市民first class citizensとして新しい概念を取り入れるembrace
    • マルチスレッディング
    • 複数のキュー
    • レンダパス + フレームグラフ
    • 明示的なリソース管理
  • 将来を見据えていないのなら、D3D12やVulkanを最大限活用することはないだろう。
    • キューやバリアを指揮するorchestrateため、高い視野で用いる。

Footnotes

  1. 訳注:ここでいう”ローカル”は、GPUから見て近場にあるということ。