そこで、GPUがどこまで実行したのか調べるのに使うフェンスですが、ID3D12Fence、HANDLE、ID3D12CommandQueue、それにUINT64型の変数と登場するオブジェクトが多くて分かり難いです。
結局、大雑把に何をするか書きだすと以下の2点に絞られます。
- GPUが実行位置に合わせてフェンスの値をインクリメント
- CPUはフェンスが期待した値になるまで待機する
GPUがフェンスの値をインクリメントと書きましたが、コマンドをGPUに送るのはCPUなのでその値もCPUから指定します。以下のようにコマンドリストの実行に混じって呼び出しているSignalがそれです。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
commandQueue->ExecuteCommandLists(1, &A); | |
commandQueue->Signal(fence, 1); | |
commandQueue->ExecuteCommandLists(1, &B); | |
commandQueue->Signal(fence, 2); | |
commandQueue->ExecuteCommandLists(1, &C); | |
commandQueue->Signal(fence, 3); |
GPUが設定したフェンスの値が指定値以上になるまで待つ関数はこんな感じです。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void WaitFenceValue(ComPtr<ID3D12Fence> fence, UINT64 value) | |
{ | |
HANDLE fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); | |
fence->SetEventOnCompletion(value, fenceEvent); | |
WaitForSingleObject(fenceEvent, INFINITE); | |
CloseHandle(fenceEvent); | |
} |
また、Signal呼び出しは常にvalueをインクリメントしながら行うのが定石で、その値を管理するためUINT64型の変数を用意することになります。
同期に使うHANDLEは使い捨てにしています。生成と破棄のオーバーヘッドが気になるなら保持しておく方法もありますが、何か意味のある状態をグローバルに保持するかのように見えるため可読性が落ちる気がします。個人的には使い捨てがおすすめです。
DirectX-Graphics-Samplesでは保持するように書かれています。
上のWaitFenceValue関数は必要最低限のコードでしたが、通常はGetCompletedValueを組み合わせて使うようです。待機する必要が無い場合を切り分けると少しパフォーマンスが上がる為と思われます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void WaitFenceValue(ComPtr<ID3D12Fence> fence, UINT64 value) | |
{ | |
if (fence->GetCompletedValue() >= value) { | |
return; | |
} | |
HANDLE fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); | |
fence->SetEventOnCompletion(value, fenceEvent); | |
WaitForSingleObject(fenceEvent, INFINITE); | |
CloseHandle(fenceEvent); | |
} |
No comments:
Post a Comment