マルチスレッドでコマンドを発行する仕組みを作ったけど、久しぶりに見たらところどころ忘れてしまってたので資料としてまとめておくことにした。
コマンド発行の仕組み
クラスはCommand、CommandList、ComanndAgent、CommandQueueの4つで構成されている。
CommandQueue
CommandQueueは以前はGraphics、Compute、Copyの3つ用意してそれぞれに発行できるようにしていたけど、フレームの途中で同期はしないようにすることを考えるとGraphicsのみで良いという考えに至り、1つのみ用意している。
またGPU処理完了でブロックしないように、Queue1つに付き、1スレッドが待機している。
Command
GPUに命令する最低単位。1命令文=1Commandで、要求するコマンドに必要なパラメータを管理している。
CommandList
DirectX12のID3D12CommandAllocatorとID3D12CommandListを内包するクラス。
CommandAgent
コマンドを内包し、実行タイミング要求で別スレッド経由でコマンドリストに追加する。
Agent1つに付き、1スレッドが待機している。
処理の流れ
①GetCommandAgent
動作スレッド:メイン
描画開始時、BeginRender内でプールからCommandAgentを取得する。
②AddCommand
動作スレッド:メイン
CommandAgentに対して、各種コマンドを追加していく。
③Execute
動作スレッド:メイン
CommandQueueにCommandAgentを移管して、CommandAgent内に眠っているスレッドを起こす。
同時にCommandQueue内に眠っているスレッドも起こす。
④GetCommandList
動作スレッド:CommandAgent
CommandAgent内のスレッドが、コマンド発行用のリストをプールから取得する。
⑤AddCommandList
動作スレッド:CommandAgent
ComanndAgent内に溜め込んだコマンドを1つずつコマンドリストに追加していく。
⑥ExecuteCommandLists
動作スレッド:CommandQueue
CommandQueue内のスレッドがCommandAgentを監視して、現在管理中のCommandAgentの処理が全て完了していたら、ExecuteCommandListsを呼び出し、GPUの処理を開始する。
⑦ReleaseCommandAgent
動作スレッド:CommandQueue
CommandAgentを返却する。
その際CommandAgentが抱えている各種リソースの開放を行う。
※厳密には、NextFrame内で呼び出される。
⑧NextFrame
動作スレッド:CommandQueue
切り替え先のフレームのGPU命令が完了しているかチェックし、終わっていなければ待つ。
終わっている場合はReleaseCommandAgentを呼び出す
次のフレームに切り替える。
課題
最初の頃はコマンドを追加するたびに、別スレッドでコマンドリストに追加して、全コマンドが追加できたらExecuteCommandListsを発行していた。
現在はCommandQueueにExecuteする単位でExecuteCommandListsする仕組みを用意はしているけど、実際にExecuteするタイミングは全コマンドを追加したらで変わっていない。
nVIDIAのドキュメントによると、1フレームで5~10回に分けてExecuteCommandListsを呼び出すと良いと書かれている。手動で分割するか、動的に判断するか今後考える。
またCommandAgent内のCommandListは複数持てるように設計しているが、現状利用しているのは1つのみになっている。更に、1フレームで複数CommandAgentを扱えるようにしているけど、現状利用しているのは1つのみ。全体で15~30くらいCommandListを保持して使い回すらしいので、複数のCommandListをまとめてExecuteするようにしたい。
※nVIDIAのドキュメントが以前「DX12 Do's And Don'ts」という題名のBlogだったけど、それぞれのトピックに分けて再構成されていた。最初なくなってしまったのかと思ったけど、1つ1つをよく見ると前と同じ内容だった。

0 件のコメント:
コメントを投稿