リソースバリアの3つの役割
- リソースのステートの変更(例:RT=レンダーターゲットからSRVへの変更等)
- キャッシュコヒーレンシの確保
- パイプラインストール(例:書き込みの後に読み込みをする場合、順序が前後しないことを保証)
パフォーマンスの為の3つのルール
- D3D12_RESOURCE_STATE_COMMONとD3D12_RESOURCE_STATE_GENERIC_READステートは避けます。
GENERIC_READは、D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER、D3D12_RESOURCE_STATE_INDEX_BUFFER、D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE、D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE、D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT、D3D12_RESOURCE_STATE_COPY_SOURCEというフラグを全て含みます。これはリソースがあらゆる場所で使われる可能性があると見なされて多くのパイプラインストールを発生させます。GENERIC_READを使うのは"Upload heap"、すなわち、CPUからGPU側のバッファを更新する時に限るべきです。
D3D12_RESOURCE_STATE_COMMONを使うべき場面はCPUがテクスチャにアクセスする場合と、"Copy engine"※に渡すCopy queueにリソースを渡す場合に限ります。
SRVはD3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCEとD3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCEの二種類あります。通常は本当に使われるもの一つを指定したほうがよいですが、両方で使われることがわかっていればor演算子で両方指定します。1つだけ指定するのがよいと思ってRT→PS→NON PSのようにステートを渡り歩くのは良くないです。 - 不必要なトランジションは避ける。例えば実装の都合で任意に中間ステートなどを置いたりしないことです。高くつく可能性があります。
- 複数リソースのトランジションを纏めて発行してパフォーマンスアップ ID3D12GraphicsCommandList::ResourceBarrierには複数のバリアを引数として渡せます。例えば DS=depth stencilとRTをそれぞれSRVにするなら2つ同時に指定するべきです。また、複数リソースをSRVからD3D12_RESOURCE_STATE_COPY_DESTに変更して書き換える場合も、個別にResourceBarrierを呼ぶのではなくResourceBarrierの一度のコールで複数のリソースを指定すべきです。
Depth bufferはハードウェア内部で圧縮されていて、SRVに変更をするとその時点から解凍作業が始まってすぐに使えません。
そこで、D3D12_RESOURCE_BARRIER_FLAG_NONEの替わりに、D3D12_RESOURCE_BARRIER_FLAG_BEGIN_ONLYとD3D12_RESOURCE_BARRIER_FLAG_END_ONLY
の2つに分けて発行して、BEGINとENDの間にGPUに別の計算をさせると、計算と解凍を並行に走らせることができます。
※ "Copy engine"が何なのかわからなくて調べてみたのですが、どうやらここが解説ページです。
https://msdn.microsoft.com/en-us/library/windows/desktop/dn899217(v=vs.85).aspx
モダンなGPUハードウェア内に実装された3つのエンジン"Copy engine"、"3D engine"、"Compute engine"のうちの1つであり、D3D12_RESOURCE_STATE_COMMONステートはその"Copy engine"内部で行われる全ての内部的なステートを内包している、ということのようです。
No comments:
Post a Comment