DirectStorage APIの予想
はじめに
NVIDIA に GPUで直接ストレージのIO操作を行う cuFile API の仕様が公開されているので、これをベースにDirectStorage APIを予想してみます。
cuFile API Reference Guide :: NVIDIA GPUDirect Storage Documentation
cuFile API
NVIDIA が提供するGPUメモリとストレージ間でDMA転送するためのAPIです。
Driver API
ドライバーAPIは cuFile システムを初期化するためのAPIです。
API | 説明 |
---|---|
cuFileDriverOpen | cuFile システムの初期化 |
cuFileDriverClose | cuFile システムの解放 |
cuFileDriverSetPollMode | IO操作のポーリング利用有無の設定 |
cuFileDriverSetMaxDirectIOSize | ドライバと通信する際の最大 IO サイズ(KB)の設定 |
cuFileDriverSetMaxCacheSize | 内部バッファリング用の最大GPUメモリサイズの設定 |
IO API
基本的な入出力APIです。
API | 説明 |
---|---|
cuFileHandleRegister | OS固有のファイルでスクリプタをcuFile固有ハンドルに変換 |
cuFileRead | GPUアドレスにデータを読み込む |
cuFileWrite | GPUアドレスの内容をファイルに書き込む |
Stream API
非同期な入出力APIです。Queueに積まれ順次処理するようです。
Operations that are enqueued with cuFile Stream APIs are FIFO ordered with respect to other work on the stream and must be completed before continuing with the next action in the stream.
API | 説明 |
---|---|
cuFileReadAsync | 非同期でGPUアドレスにデータを読み込む |
cuFileWriteAsync | 非同期でGPUアドレスの内容をファイルに書き込む |
cuStreamSynchronize | ストリーム同期待ち |
Batch API
複数のファイルや同じファイルの任意の場所をどこに入出力するかをまとめて定義し一括で非同期IOを発行するAPIです。こちらもQueueに積まれ順次処理しますが、バッチ内の順序は入れ替わる場合があるようです。
cuFile Batch APIs enable an arbitrary mix of read and write transactions, multiple files, and multiple locations in a file in one dispatch. Operations enqueued with cuFile Batch APIs are FIFO ordered with respect to other work on the stream, and must be completed before continuing to the next action in the stream. Individual operations in each batch might be reordered with respect to each other.
API | 説明 |
---|---|
cuFileBatchIOSubmit | IO操作バッチを送信する |
cuFileBatchIOGetStatus | IO操作バッチの状態を取得する |
cuFileBatchIOCancel | IO操作バッチをキャンセルする |
cuFileBatchIODestroy | IO操作バッチ破棄する |
DirectStorage API の予想
ストレージ処理を行うのは GPU自身なので D3D12Device を QueryInterface して IDirectStorageDevice みたいなものを取得するのが考えられます。
pD3D12Device->QueryInterface(IID_PPV_ARGS(&pStorageDevice));
IO操作に関してはゲーム用途を考えた場合、ストリーミング処理が主な用途になるので Batch API をベースとした非同期なIO操作で実装されると思います。
なにより Microsoft が推進している Sampler Feedback Streaming を利用した仮想テクスチャ(Tiled Resource Texture)ストリーミングはタイル単位で大量のIO発行する必要があるので Batch API ベースじゃないと厳しいのが大きいです。
pStorageDevice->CreateStorageQueue(IID_PPV_ARGS(&pStorageQueue));
auto handle = CreateFile(...);
pStorageDevice->CreateFile(handle, IID_PPV_ARGS(&pStorageFile));
batches[0].File = pStorageFile;
batches[0].Heap = pHeap;
batches[0].Size = 65536;
batches[0].FileOffset = 0;
batches[0].WriteOffset = 0;
pStorageDevice->CreateBatch(numBatches, &batches, IID_PPV_ARGS(&pBatch));
pStorageQueue->Submit(pBatch);
DirectStorage の出力先は ID3D12Resource ではなく ID3D12Heap になると思います。ID3D12Resource だと Reserved リソースの場合、実メモリが割り当てられていない可能性がありますし仮想テクスチャの場合、実メモリの内容が確定した後に TileMapping したほうが楽そうだしIO発行の回数を減らすにも効果的だからです。
仮想テクスチャストリーミングとDirectStorage
現在のテクスチャストリーミングがMIPレベルストリーミングが主流なのは仮想テクスチャストリーミングでは1タイル64KiB単位という小さなデータを色々な場所から大量に読み込む必要がありHDD だとランダムアクセス過多でIOパフォーマンスが最悪なのも一つの要因です。
現在のSSDであれば64KiB以上のデータであればランダムアクセスによるパフォーマンス低下はほとんど無視できるレベルなのでBatch APIベースを前提とすれば一気に実用レベルになります。
タイルデータの最適化について
現在はマッピングするテクスチャが1枚なんてことはほぼ無くてカラー/法線/スペキュラー等と複数のテクスチャが使用されています。
タイル単位で読み込む場合も当然全てのテクスチャを読み込む必要がありますが各テクスチャのタイル毎にIO発行するのは流石に無駄が多いです。
IO発行回数を減らすためには各テクスチャのタイル単位でまとめて連続したデータにする事で1回のIO発行で各テクスチャのタイルを読み込むことが可能になります。
タイル番号 | カラー | 法線 | スペキュラー | その他 |
---|---|---|---|---|
0 | カラー#0 | 法線#0 | スペキュラー#0 | その他#0 |
1 | カラー#1 | 法線#1 | スペキュラー#1 | その他#1 |
2 | カラー#2 | 法線#2 | スペキュラー#2 | その他#2 |
N | カラー#N | 法線#N | スペキュラー#N | その他#N |
まとめ
DirectStorage の予想のはずが仮想テクスチャストリーミングなどまとまりがないない内容になりましたがこの二つは切っても切れない関係なのでとりあえず良しとします。
参考資料
cuFile API Reference Guide :: NVIDIA GPUDirect Storage Documentation
GPUDirect Storage: A Direct Path Between Storage and GPU Memory | NVIDIA Developer Blog
Discussion