2023年4月30日日曜日

Enhanced Barriers

新しいバリアが使えるようになったので、ライブラリを書き換えてみた。


D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS


新しいバリアの説明を読んで、このフラグの存在を知った。
勝手に状態遷移をして、その条件が複雑で分かりづらいと古いバリアの欠点として書かれていた。

今まではこのフラグに頼らず、自前でリソースの読み込みと書き込みが切り替わるポイントで、自動的にリソースバリアを挿入するようにしていた。
だけどこのフラグを使うと一部のリソースを除いて、自動的に遷移してくれるし便利そう。

新しいバリアでも使えるみたいなのでこのフラグを立ててすべてのバリア処理を消してみた。


バッファ


まずリソースの生成関数が変わり、CreatePlacedResource2になった。
以前すべてのリソースをCreateCommittedResourceから、CreatePlacedResourceに変えたけど、今回はCreatePlacedResource2に変わる。
引数の変更点は、D3D12_RESOURCE_DESCからD3D12_RESOURCE_DESC1に変わる(中身はSamplerFeedbackMipRegionが増えた)のと、初期のリソースステータスを渡していたのを、初期のD3D12_BARRIER_LAYOUTを渡すようになったこと。

D3D12_BARRIER_LAYOUTでいうと、バッファに使うのはD3D12_BARRIER_LAYOUT_UNDEFINEDでいいらしい。
前の場合は、UploadとReadbackの場合は、D3D12_RESOURCE_USAGE_GENERIC_READで、それ以外はD3D12_RESOURCE_USAGE_COPY_DESTで始めていた。

そして、D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESSを指定することによって、バッファの場合は遷移を何もする必要がない。
書き込む時にD3D12_RESOURCE_STATE_GENERIC_READにして、コピーする時D3D12_RESOURCE_STATE_COPY_SOURCEにして、使うときにそれぞれのステータスにわざわざ遷移させていたけど、新しいバリアでは?それともD3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESSのフラグのお陰で?何もしなくて良くなった。

というわけでバッファについてはバリア処理が0になった。

テクスチャ


テクスチャの方はバッファのようにバリア0にはならなかった。
ただ、単純なファイルから読み込んだテクスチャはバッファ同様バリア0で行ける。
初期のレイアウトはD3D12_BARRIER_LAYOUT_COMMONで、後は勝手に遷移してくれる。
自分で制御しないといけないのは、D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGETとD3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCILを指定する場合で、このフラグを付ける場合はD3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESSが付けられない。なので、主にこのリソースについて、書き込み時とテクスチャ参照時にバリアを使うことになる。


グローバル


新しいバリアの3つ目のグローバルバリアについてはよくわからない。

Global barriers control cache flush and synchronization for all indicated resource access types in a single command queue. Global Barriers have no effect on texture layout. Global Barriers are needed to provide functionality similar to legacy NULL UAV barriers and NULL/NULL aliasing barriers.

説明にはこう書かれているが、全く理解できない。とりあえずは置いておく。


中間レンダーターゲット


中間レンダーターゲットはD3D12_BARRIER_LAYOUT_RENDER_TARGETスタートで、テクスチャとして使う場合に、D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCEに変える。


深度バッファ


深度バッファはD3D12_BARRIER_LAYOUT_DEPTH_STENCIL_WRITEスタートで、テクスチャとして使う場合に、D3D12_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCEに変える。


レンダーターゲット


レンダーターゲットは直接リソースを作らないけど、LayoutBeforeを設定するために初回のレイアウトはD3D12_BARRIER_LAYOUT_PRESENTを設定しておく。
書き込む際はD3D12_BARRIER_LAYOUT_RENDER_TARGETに変える。

新しくAccessBefore/AfterとSyncBefore/Afterが増えた。
この指定がいまいち理解できていないが1つわかった事がある。


実際には良くない例だけど、Presentを呼ぶ前にレンダーターゲットの遷移のみのExecuteCommandListsを実行していた。
この時、下記のように指定。
 AccessBefore=D3D12_BARRIER_ACCESS_RENDER_TARGET
 AccessAfter=D3D12_BARRIER_ACCESS_COMMON
 SyncBefore=D3D12_BARRIER_SYNC_RENDER_TARGET
 SyncAfter=D3D12_BARRIER_SYNC_ALL
特にSyncAfterは何を指定していいのか全く検討がつかない。

実行してみると、デバッグレイヤーにはこんなメッセージが出ていた。

D3D12 WARNING: ID3D12CommandQueue::ExecuteCommandLists: ExecuteCommandLists references command lists that have recorded only Barrier commands. Since there is no other GPU work to synchronize against, all barriers should use AccessAfter / AccessBefore = D3D12_BARRIER_ACCESS_NO_ACCESS and SyncBefore / SyncAfter = D3D12_BARRIER_SYNC_NONE. This information can be used as an optimization hint by some drivers. [ EXECUTION WARNING #1356: NON_OPTIMAL_BARRIER_ONLY_EXECUTE_COMMAND_LISTS]

バリアのみのコマンドリストは何も同期するものも無いからD3D12_BARRIER_ACCESS_NO_ACCESSとD3D12_BARRIER_SYNC_NONEをBefore/Afterに設定すればいいとのこと。
なるほど、今までExecuteCommandListsの枠を超えて、その前のコマンドも含めて考えないといけないのかと思っていたけど、今回の実行単位の中だけで考えればいいということがわかった。

コマンドリストの先頭で遷移させる場合はBeforeにD3D12_BARRIER_ACCESS_NO_ACCESSとD3D12_BARRIER_SYNC_NONEを設定出来るし、次の為にコマンドリストの最後でLayoutだけ変えておくような場合、AfterにD3D12_BARRIER_ACCESS_NO_ACCESSとD3D12_BARRIER_SYNC_NONEを設定する事が出来る。


UAV


テクスチャをロードした後、ミップマップを自動でつくるときにミップマップの階層分UAVを用意して、1つ上の階層を参照して、下の階層を作るというのを順番に行う。
この時PIXのヒントに出ていたのは、「UAVバリアが要らなさそうだから消してみれば?」という提案。
実際に消すと、同期が取れていないのでテクスチャがきちんと書き込まれなくなって真っ黒のテクスチャが出来上がる。
新しいバリアではUAVバリアというものが無くなったので、どうするのかと思ったらドキュメントには下記のように書かれていた。
 LayoutBefore = D3D12_BARRIER_LAYOUT_UNORDERED_ACCESS
 LayoutAfter = D3D12_BARRIER_LAYOUT_UNORDERED_ACCESS
 AccessBefore = D3D12_BARRIER_ACCESS_UNORDERED_ACCESS
 AccessAfter = D3D12_BARRIER_ACCESS_UNORDERED_ACCESS
 SyncBefore = D3D12_BARRIER_SYNC_COMPUTE_SHADING
 SyncAfter = D3D12_BARRIER_SYNC_COMPUTE_SHADING
実際にやってみると自分が使っている場面ではエラーがでて、レイアウトを両方ともCOMMONに変えたらうまくいった。
PIXで確認してみたところ、古いバリアの場合各階層ごとに待ちが発生して結構時間がかかっていたけど、新しいバリアではその待ちが消えていた。結果全体で200usぐらいかかっていた処理が150usぐらいに短縮された。新しいバリア恐るべし。


今回の修正でリソースバリアをライブラリで挿入していた処理を一切なくして、今のところ必要な場面で自分で指定する形に変えた。
もう少し新しいバリアを理解して、このまま行くのか、自動で挿入するかを見極める。


0 件のコメント:

コメントを投稿