Skip to content
Go back

拙訳「D3D12 & Vulkan Done Right」

· Updated:

[Thomas 2017Thomas, G. 2017. D3D12 & Vulkan Done Right. Game Developers Conference. https://gdcvault.com/play/1024732/Advanced-Graphics-Tech-D3D12-and.]

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から見て近場にあるということ。