D3D12 拡張バリア
強化されたバリア用の DDI インターフェイスは、Windows 11、バージョン 22H2 WDK (WDDM 3.0) で使用できます。 22H2 (またはそれ以前の OS リリース) で拡張バリアを使用するには、1.706.4-preview Agility SDK をインストールする必要があります。
D3D12 拡張バリアにより、開発者は GPU 作業の同期、テクスチャ レイアウトの遷移、キャッシュ フラッシュ (リソース メモリ アクセス) を個別に制御できます。 この機能は、開発者が GPU 作業の同期、テクスチャ レイアウトの切り替え、キャッシュ フラッシュ (リソース メモリ アクセス) を個別に制御できる一連の Direct3D API と DDI を提供します。
強化されたバリアは、従来のリソース バリアを、より表現力豊かなバリア タイプに置き換えます。 これらには次の機能があります。
- 同期の待機時間が短縮されます。
- 過剰なキャッシュフラッシュの削減。
- 不思議な昇格ルールやディケイ ルールはありません。
- 高速で柔軟なリソース エイリアシング (多様なエイリアシング トポロジ)。
- バリア切り替え中に破棄します。
- 同一リソース・コピー (セルフ・コピー) を含む、同時読み取り/書き込みのサポート。
- 非同期の破棄、コピー、解決、クリア コマンドのサポート。
拡張バリアは、従来のリソース バリアほど単純ではありませんが、あいまいさが少ないため、開発者にとって使いやすいものとなっています。
強化されたバリアサポートの報告
拡張バリア機能は、現在、ハードウェアまたはドライバーの要件ではありません。 ドライバーは、D3D12DDI_D3D12_OPTIONS_DATA_0089 のメンバー EnhancedBarriersSupported を TRUE に設定することで、サポートを示します。
- D3D12DDI_FEATURE_VERSION_VIDEO_0088_0 は、Windows 11で導入された D3D12 拡張バリア マイルストーンの予備実装を定義するバージョン番号です。
D3D12 拡張バリア コールバック関数
拡張バリアのサポートを示すドライバーは、次のコールバック関数を実装します。
- PFND3D12DDI_BARRIER_0088
- PFND3D12DDI_CREATEHEAPANDRESOURCE_0088
- PFND3D12DDI_CALCPRIVATEHEAPANDRESOURCESIZES_0088
- PFND3D12DDI_CHECKRESOURCEALLOCATIONINFO_0088
設計の詳細
ドライバーは、通常、次の 3 つの個別の操作を使用してレガシ リソース バリアを処理します。
- GPU の作業を同期します。
- 必要なキャッシュ フラッシュ操作を実行します。
- 必要なレイアウト変更を実行します。
強化されたバリアを使用すると、開発者に、これらの各操作を個別に制御できます。
強化されたバリアの種類
強化された バリアには、3つのタイプがあります。
従来のリソース バリアは、範囲リソース バリアと置き換えられます。 範囲バリアは、レガシー リソース バリアを、目立ったパフォーマンスの低下なしに、完全に実装できるように提供されています。
すべてのバリア タイプは、バリアの前後に、GPU 作業の同期と、読み取りまたは書き込みアクセスの種類を制御します。
テクスチャ バリアは、テクスチャ サブリソースのレイアウトを追加的に管理します。 サブリソースの選択は、従来のリソース バリアで使用される使い慣れた 1 つまたはすべてのオプションに加えて、ミップ、配列、および平面スライスの範囲として表すことができます。
バッファー バリアとグローバル バリアは、同期とリソース アクセスのみを制御し、リソース レイアウトには影響しません (バッファーにはレイアウトがありません)。 グローバル バリアは、キャッシュされたすべてのメモリに影響するため、コストが高くなる可能性があり、よりスコープの広いバリアでは、不十分な場合にのみ使用する必要があります。
テクスチャ バリア
- キャッシュのフラッシュ、メモリ レイアウト、およびテクスチャ サブリソースの同期を制御します。
- テクスチャ リソースでのみ使用する必要があります。
- 単一のサブリソース、すべてのサブリソース、またはサブリソースの一貫した範囲 (したがって、ミップ範囲と配列範囲) を選択できます。
- 有効な NULL 以外のリソース ポインターを指定する必要があります。
バッファー バリア
- バッファー リソースのキャッシュ フラッシュと同期を制御します。
- バッファー リソースでのみ使用する必要があります。
- テクスチャとは異なり、バッファーにはサブリソースが 1 つのみあり、切り替え可能なレイアウトはありません。
- 有効な NULL 以外のリソース ポインターを指定する必要があります。
グローバル バリア
- 指定されたすべてのリソース アクセス タイプのキャッシュ 同期を 1 つのコマンド キューで制御します。
- テクスチャ レイアウトには影響しません。
- 従来の NULL UAV バリア、および NULL/NULL エイリアス バリアと同様の機能を提供するために、必要となります。
グローバル バリアはテクスチャ レイアウトを切り替えず、それ以外の、レイアウトの変更が必要な画面切り替えの場合は、グローバル バリアを使用できません。 たとえば、グローバル バリアを使用して、同時アクセスではないテクスチャを D3D12DDI_BARRIER_ACCESS_RENDER_TARGET から D3D12DDI_BARRIER_LAYOUT_SHADER_RESOURCE D3D12DDI_BARRIER_LAYOUT_RENDER_TARGET D3D12DDI_BARRIER_ACCESS_SHADER_RESOURCE に切り替えさせることはできません。
Synchronization
グラフィックス プロセッサは、できるだけ多くの作業を並列に実行できるように設計されています。 以前の GPU 作業に依存する GPU 作業は、依存データにアクセスする前に同期する必要があります。
拡張バリア インターフェイスでは、明示的な SyncBefore 値と SyncAfter 値が論理ビット フィールド マスクとして使用します。 バリアを実行する前に、バリアは、上記のすべてのコマンド SyncBefore スコープが完了するのを待つ必要があります。 同様に、バリアは、バリアが完了するまで、後続のすべての SyncAfter スコープをブロックする必要があります。 D3D12DDI_BARRIER_SYNCは、バリアに関する GPU 作業の同期スコープを指定します。
詳細については、「拡張バリア仕様」を参照してください。
レイアウトの切り替え
テクスチャ サブリソースでは、さまざまなアクセス方法に異なるレイアウトを使用できます。 たとえば、テクスチャーは、レンダー ターゲットまたは深度ステンシルとして使用すると圧縮されることが多く、シェーダーの読み取りまたはコピー コマンドでは圧縮されないことがよくあります。 テクスチャ バリアでは、LayoutBefore と LayoutAfter の D3D12DDI_BARRIER_LAYOUT 値を使用して、レイアウトの遷移を記述します。
レイアウトの切り替えはテクスチャにのみ必要であるため、D3D12DDI_TEXTURE_BARRIER データ構造でのみ表現されます。
LayoutBefore と LayoutAfter はどちらも、バリアを実行するキューのタイプと互換性がある必要があります。 たとえば、コンピューティング キューは、サブリソースを D3D12DDI_BARRIER_LAYOUT_RENDER_TARGET に切り替えたり、サブリソースから移行したりできません。
バリアの順序を明確に定義するために、一連のバリアを完了した後のサブリソースのレイアウトは、シーケンス内の最終的な LayoutAfter となります。
アクセスの切り替え
多くの GPU 書き込み操作がキャッシュされるため、書き込みアクセスから別の書き込みアクセスへのバリア、または読み取り専用アクセスには、キャッシュ フラッシュが必要になる場合があります 拡張バリア API では、アクセス遷移を使用して、特定の新しいアクセスの種類に対してサブリソースのメモリを可視化する必要があることを示します。 レイアウトの切り替えと同様に、関連するサブリソースのメモリが目的の用途で既にアクセス可能であることがわかっている場合は、一部のアクセス切り替えは必要ない場合があります。
アクセスの切り替えは次のように表されます。
- テクスチャの場合は、D3D12DDI_TEXTURE_BARRIER 構造の一部として。
- バッファーの場合は、D3D12DDI_BUFFER_BARRIER 構造体の一部として。
アクセスの切り替えでは同期は実行されません。 依存アクセス間の同期は、バリアの適切な SyncBefore と SyncAfter 値を使用して処理されることが想定されています。
指定した AccessBefore に表示される AccessAfter は、リソース メモリが別のアクセスの種類でも表示されることを保証しません。 次に例を示します。
MyTexBarrier.AccessBefore=D3D12DDI_BARRIER_ACCESS_UNORDERED_ACCESS;
MyTexBarrier.AccessAfter=D3D12DDI_BARRIER_ACCESS_SHADER_RESOURCE;
このアクセス切り替えは、後続のシェーダー読み取りアクセスが、先行する順序付けされていないアクセス/書き込みに依存することを示します。 ただし、ハードウェアが UAV キャッシュからシェーダー リソースを直接読み取ることができる場合、ドライバーは実際には UAV キャッシュをフラッシュしない可能性があります。
D3D12DDI_BARRIER_ACCESS_COMMON
D3D12DDI_BARRIER_ACCESS_COMMON は、レイアウトと互換性のあるアクセスを示す特殊なアクセスの種類です。 D3D12DDI_BARRIER_ACCESS_COMMON に切り替えるということは、バリアの後にレイアウト互換のアクセスにサブリソース データを使用できる必要があることを意味します。 バッファーにはレイアウトがないため、 D3D12DDI_BARRIER_ACCESS_COMMON は、単に、バッファーと互換性のあるアクセスを意味します。
バリアで D3D12DDI_BARRIER_ACCESS_COMMON を AccessBefore として指定すると、すべての書き込みアクセスの種類のセットが暗黙的に指定されます。 D3D12DDI_BARRIER_ACCESS_COMMON を AccessBeforeとして使用することはお勧めしません。コストがかかり、意図しないキャッシュ フラッシュが発生する可能性があるためです。 代わりに、バリア オーバーヘッドを適切に制限するために、最も狭く必要な書き込みアクセス ビットのみを使用することを開発者にはお勧めします。 AccessBefore が D3D12DDI_BARRIER_ACCESS_COMMON に設定されている場合、デバッグ レイヤーの警告が発行されます。
単一キュー同時アクセス
強化されたバリアにより、同じコマンド キュー内の同じバッファー、または同時アクセス テクスチャに対する同時読み取り/書き込み操作が可能になります。
バッファーと同時アクセス リソースは、常に 1 つのキューからの書き込みアクセスと、1 つ以上の他のキューからの同時実行、非依存、読み取りアクセスをサポートしていました。 このサポートは、このようなリソースが常に COMMON レイアウトを使用し、読み取り/書き込みの危険がないためです (読み取りは同時書き込みに依存してはならないため)。 (従来のリソース バリア ルールでは、書き込み状態ビットと他の状態ビットの組み合わせが禁止されています。したがって、従来のリソース バリアを使用して、同じキュー内のリソースを同時に読み取り、および書き込みできません)。
一見重複していない 2 つの書き込みリージョンにキャッシュ ラインが重複している可能性があるため、ワンライター同時ポリシーは、引き続き適用されます。
サブリソース範囲
開発者がさまざまなサブリソースを遷移したいと考えるのは一般的です。 たとえば、特定のテクスチャ配列に対して完全なミップチェーンを遷移したり、すべての配列スライスに対して単一のミップレベルを遷移したりする場合です。 強化されたバリアにより、開発者は D3D12DDI_BARRIER_SUBRESOURCE_RANGE 構造を使用して、論理的に隣接するサブリソースの範囲を移行できます。 (レガシ リソース状態切り替えバリアでは、開発者は、すべてのサブリソース状態または単一のサブリソース状態をアトミックに移行するオプションのみが提供されます)。
コンピューティングとダイレクト キューのレイアウト
次の強化されたバリア レイアウトは、ダイレクト キューとコンピューティング キューの両方で同じであることが保証されています。
- D3D12DDI_BARRIER_LAYOUT_GENERIC_READ
- D3D12DDI_BARRIER_LAYOUT_UNORDERED_ACCESS
- D3D12DDI_BARRIER_LAYOUT_SHADER_RESOURCE
- D3D12DDI_BARRIER_LAYOUT_COPY_SOURCE
- D3D12DDI_BARRIER_LAYOUT_COPY_DEST
これらのレイアウトの 1 つのサブリソースは、直接キューまたはコンピューティング キューで、レイアウト切り替えなしで使用できます。
一部のハードウェアでは、先行または後続の両方のアクセスが直接キューにある場合、直接キューのレイアウト切り替えバリアが大幅に高速化される可能性があります。 直接キューのリソースにアクセスする場合は、次のレイアウトを使用することを強くお勧めします。
- D3D12DDI_BARRIER_LAYOUT_DIRECT_QUEUE_GENERIC_READ
- D3D12DDI_BARRIER_LAYOUT_DIRECT_QUEUE_UNORDERED_ACCESS
- D3D12DDI_BARRIER_LAYOUT_DIRECT_QUEUE_SHADER_RESOURCE
- D3D12DDI_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_SOURCE
- D3D12DDI_BARRIER_LAYOUT_DIRECT_QUEUE_COPY_DEST
DIRECT_QUEUE レイアウト バリアントはコンピューティング キューと互換性がなく、コンピューティング コマンド リスト バリアでは使用できません。 ただし、直接キューでのコンピューティング操作と互換性があります。
バリアフリー アクセス
ExecuteCommandLists の境界間に保留中のコマンドやキャッシュ フラッシュ操作は必要ないため、バッファーはバリアなしで ExecuteCommandLists スコープで最初にアクセスされる可能性があります。 同様に、テクスチャ サブリソースは、次の条件下でバリアなしで、最初にアクセスされる場合もあります。
- サブリソース レイアウトは、アクセス タイプとの互換性があります。
- 必要な圧縮メタデータが初期化されています。
未処理の読み取りまたは書き込み操作のない可能性があるレイアウト D3D12DDI_BARRIER_LAYOUT_COMMON (または D3D12DDI_BARRIER_LAYOUT_DIRECT_QUEUE_COMMON キュー固有の一般的なレイアウトなど) のテクスチャ サブリソースは、次のいずれかのアクセスの種類を使用して、バリアなしで ExecuteCommandLists コマンド ストリームでアクセスできます。
- D3D12DDI_BARRIER_ACCESS_SHADER_RESOURCE
- D3D12DDI_BARRIER_ACCESS_COPY_SOURCE
- D3D12DDI_BARRIER_ACCESS_COPY_DEST
さらに、キュー固有の共通レイアウトを使用するバッファーまたはテクスチャでは、バリアなしで D3D12DDI_BARRIER_ACCESS_UNORDERED_ACCESS を使用できます。
バッファーと同時アクセス テクスチャ (D3D12DDI_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS フラグを使用して作成されたテクスチャ) は、次のいずれかのアクセスの種類を使用して、バリアなしで ExecuteCommandLists コマンド ストリームで最初にアクセスできます。
- D3D12DDI_BARRIER_ACCESS_VERTEX_BUFFER
- D3D12DDI_BARRIER_ACCESS_CONSTANT_BUFFER
- D3D12DDI_BARRIER_ACCESS_INDEX_BUFFER
- D3D12DDI_BARRIER_ACCESS_RENDER_TARGET
- D3D12DDI_BARRIER_ACCESS_UNORDERED_ACCESS
- D3D12DDI_BARRIER_ACCESS_SHADER_RESOURCE
- D3D12DDI_BARRIER_ACCESS_STREAM_OUTPUT
- D3D12DDI_BARRIER_ACCESS_INDIRECT_ARGUMENT
- D3D12DDI_BARRIER_ACCESS_COPY_DEST
- D3D12DDI_BARRIER_ACCESS_COPY_SOURCE
- D3D12DDI_BARRIER_ACCESS_RESOLVE_DEST
- D3D12DDI_BARRIER_ACCESS_RESOLVE_SOURCE
- D3D12DDI_BARRIER_ACCESS_PREDICATION
後続のアクセスは、書き込みアクセスの種類が 1 つしかないバリアなしで行うこともできます。 ただし、D3D12DDI_BARRIER_ACCESS_RENDER_TARGET を除き、バリアを使用して、同じリソースへのシーケンシャル書き込みをフラッシュする必要があります。
セルフ リソース コピー
拡張バリアとは排他的には関係ありませんが、サブリソースのある領域から別の非交差リージョンへのコピーを許可する機能は、非常に要求の高い機能です。 強化されたバリアを使用すると、共通のレイアウトを持つサブリソースを、同じ CopyBufferRegion または CopyTextureRegion 呼び出しでソースと宛先の両方として使用できます。 交差するソースメモリ領域とコピー先メモリ領域でコピーを行うと、未定義の結果が生成されます。 デバッグ レイヤーは、このような結果に対して検証する必要があります。 (従来のリソース バリアの設計では、サブリソースが同時に D3D12DDI_RESOURCE_STATE_COPY_SOURCE 状態と D3D12DDI_RESOURCE_STATE_COPY_DEST 状態の両方になることは許可されないため、それ自体にコピーすることはできません。
配置されたリソース メタデータの初期化
従来のリソース バリア設計では、新しく配置およびアクティブ化されたエイリアス テクスチャ リソースをレンダー ターゲットまたは深度ステンシル リソースとして使用する前に、Clear、Copy、または Discard で初期化する必要があります。 この要件は、通常、レンダー ターゲット リソースと深度ステンシル リソースが、データを有効にするために初期化する必要がある圧縮メタデータを使用するためです。 同じことが、新しく更新されたタイルマッピングを持つ予約済みテクスチャにも当てはまります。
強化されたバリアは、バリアの一部として破棄するオプションをサポートします。 D3D12DDI_BARRIER_LAYOUT_UNDEFINEDから圧縮される可能性のあるレイアウト(D3D12DDI_BARRIER_LAYOUT_RENDER_TARGET、D3D12DDI_BARRIER_LAYOUT_DEPTH_STENCIL、D3D12DDI_BARRIER_LAYOUT_UNORDERED_ACCESSなど)へのバリアレイアウトの切り替えは、D3D12DDI_TEXTURE_BARRIER_FLAG_DISCARD が D3D12DDI_TEXTURE_BARRIER::Flags メンバーに存在する場合、圧縮メタデータを初期化しなければなりません。
レンダー ターゲットリソースと深度/ステンシル リソースに加えて、レガシ バリア モデルでサポートされなかった同様の UAV テクスチャ圧縮の最適化があります。
バリアの順序付け
バリアは順方向 (API 呼び出し順序、barrier-group-index、barrier-array-index) でキューに入れられます。 同じサブリソース上の複数のバリアは、バリアがキューに登録された順序で完了するかのように機能する必要があります。
同じメモリに書き込む可能性がある SyncAfter スコープが一致するキューに登録されたバリアは、キューに入った順序ですべての書き込みを完了する必要があります。 この要件により、リソースのエイリアシングをサポートするバリアでのデータ競合が回避されます。 たとえば、リソースを非アクティブ化するバリアは、同じメモリ上の別のリソースをアクティブ化し、メタデータをクリアする可能性のある別のバリアの前に、キャッシュをフラッシュする必要があります。