拙訳
メモリの種類(物理)
- GPUメモリ:
MEMORY_POOL_L1(DX) /DEVICE_LOCAL(VK) - CPUメモリ:
MEMORY_POOL_L0(DX)
メモリの種類(論理)
- GPUメモリ:
DEFAULT(DX) /DEVICE_LOCAL(VK)- GPUから高速に直接アクセスできる
- CPUから直接アクセスできない
- GPUで頻繁に読み書きするもの(RTV/DSV/UAV)や1回書き込んだら何度も読み込むもの(変更しないIB/VB/Tex)に使う
- CPUメモリ
- CPU→GPU:
UPLOAD(DX) /VISIBLE | COHERENT(VK)- CPUから直接アクセスでき、キャッシュされず、書き込みはまとめて行われる
- 連続的な書き込みは高速🐰
- 読み込みやトビトビな書き込みは低速🐢
- CPUで書き込み、GPUにコピーされるものに使う
- CPUで書き込み、GPUで直接読み出すものに使う
- Pro tip: データの先頭は64Bに揃える
- CPUから直接アクセスでき、キャッシュされず、書き込みはまとめて行われる
- GPU→CPU:
READBACK(DX) /VISIBLE | COHERENT | CACHED(VK)- CPUから直接アクセスでき、キャッシュされる
- GPUで書き込み、CPUで読み込むものに使う
- CPU→GPU:
データのアップロード方法
- 方法1:UPLOADにCPUで書き込み、DEFAULTにコピーしてからGPUで読み出す
- 方法2:UPLOADにCPUで書き込み、そのままGPUで読み出す
- バッファでは大丈夫
- テクスチャではGPU固有の最適なフォーマットで格納したほうが良い
TEXTURE_LAYOUT_UNKNOWN(DX) /IMAGE_TILING_OPTIMAL(VK)
- 方法3:BAR(Base Address Register)にCPUで書き込み、そのままGPUで読み出す
- BARはGPU側にあるが、CPUで直接書アクセスでき、キャッシュされず、書き込みはまとめて行われる
- Vulkanでは、
LOCAL | VISIBLE | COHERENT - DirectXでは、現時点では確認する術はない
Resizable BAR (ReBAR)
- 通常のBARは256MBで固定
- ReBARを有効にするとVRAM全体にCPUでアクセスできるようになる
- Vulkanでは、
LOCAL | VISIBLEメモリが256MB以上かどうかで分かる - DirectXでは、現時点では直接使用する術はない
- ReBARでもUPLOADと同様のTipsが有効
- 連続的な書き込みが高速🐰
- PCIe 4.0ならVRAMへの書き込みもCPUメモリと同等に高速なはず
パフォーマンスTips
- 個々のバッファ/テクスチャにはオーバーヘッドが存在する
- 大きなバッファ/テクスチャをいくつか用意してsub-allocateする
- バッファ1つに有意義なデータが64KB以上あるとなお良い
- 個々のSubmit/Executeやセマフォ/イベントによるキュー間同期にはオーバーヘッドが存在する
- 並列処理でレイテンシーを隠蔽する
- データを使うのと同じキューでコピーを行う、または、データを直接読み出してコピーを行わないようにする
- Map/Unmapにはいくらかのオーバーヘッドが存在する
- バッファをマップしたままにしておく
- UPLOADリソースはVRAMに割り当てられることがあるので:
- VRAMを取りすぎない、すなわち、UPLOADメモリを割り当てしすぎない
- 生成時に「好ましいヒープ」が決められる、すなわち、VRAMに割り当てられる可能性を高めるために最重要リソースから割り当てを行う
- “Residency”はヒープ全体で管理される、すなわち、巨大で重要なリソースを別個の割当で生成する(committed/dedicatedを使う)
- コピーするのはどのキューが良い?
- PCIeを介してUPLOADからコピーするときは、コピーキューのほうが若干早い🐰
- コピー元がVRAMにあるときは、コピーキューのほうが何倍か遅い🐢🐢🐢
- コピーキューを使うのは、レンダリングの裏で非同期に行う場合(テクスチャストリーミングとか)
- グラフィクス/コンピュートキューを使うのは、結果が即座に欲しい場合(現フレームの動的データとか)
- 非同期コンピュートが裏でのコピーに使えるかも?
- UPLOADやBARを使ってコピーをしないことも一考すると良い
- 少量のデータとか、一度しか読み出さないデータとか