ルート署名バージョン 1.1

ルート署名バージョン 1.1 の目的は、記述子ヒープ内の記述子が変わらない場合またはデータ記述子のポイント先が変わらない場合にアプリケーションがドライバーを示すことができるようにすることです。 これにより、ドライバーのオプションを最適化して、ドライバーがポイントする記述子またはメモリが一定の時間静的であることを認識できる場合があります。

概要

ルート署名バージョン 1.0 を使用すると、記述子ヒープの内容とそれらが指すメモリをアプリケーションを、それらを参照しているコマンド リストやバンドルが潜在的に GPU 上で使用されていればいつでもアプリケーションが自由に変更できます。 ただし、非常に多くの場合、アプリケーションは、それらを参照するコマンドが記録された後に記述子やメモリを変更する柔軟性を実際には必要としません。

ほとんどの場合、アプリケーションは、自明に次の操作を行うことができます。

  • 記述子テーブルまたはルート記述子をコマンド リストまたはバンドルにバインドする前に、記述子 (および可能であればそれらが指すメモリ) を設定する。
  • これらの記述子を参照するコマンド リストやバンドルが最後の実行を終了するまで、その記述子が変更されないようにする。
  • 記述子が指すデータが同じ期間全体で変更されないようにする。

また、アプリケーションで尊重できるのは、データが変更されない期間を短縮することだけです。 特に、ルート パラメーター バインド (記述子テーブルまたはルート記述子) が現在データを指しているコマンド リストの実行中は、データがその期間にわたって静的になる可能性があります。 つまり、アプリケーションは、設定時にデータが静的になることがわかっているため、ルート パラメーターを介して設定されている期間と期間の間に一部のデータの更新を GPU タイムラインで実行しようとするかもしれません。

記述子または記述子が指すデータが変更されなければ、ドライバーが実行する可能性のある特定の最適化は、ハードウェア ベンダー固有になります。重要なことに、この操作では、パフォーマンスが改善される可能性はありますが、それ以外の動作が変更されることはありません。 アプリケーションの意図について可能な限り多く認識していても、アプリケーションに負担はかかりません。

最適化の 1 つとして、多くのドライバーは、記述子とデータの静的性をアプリケーションが保証できることを認識していればシェーダーによるより効率的なメモリ アクセスを生成できます。 たとえば、特定のハードウェアがルート引数のサイズに影響されにくい場合、ドライバーは、ヒープ内の記述子にアクセスするための間接的なレベルをルート記述子に変換することでこれを解消することができます。

バージョン 1.1 を使用する開発者にとっての追加のタスクは、可能な限りデータの揮発性と静的性について保証することであり、それによってドライバーは、意味のある最適化を行うことができます。 開発者は、静的性についていかなる保証もする必要はありません。

ルート署名バージョン 1.0 は引き続きそのまま機能しますが、ルート署名を再コンパイルするアプリケーションは、既定でルート署名 1.1 になっています (必要な場合にバージョン 1.0 を強制するオプションもあります)。

静的および揮発性のフラグ

次のフラグはルート署名に含まれています。これにより、ドライバーは個々のルート引数を設定時に最適に処理する方法を選択できるようになるほか、最初のコンパイル時に同じ仮定をパイプライン状態オブジェクト (PSO) に埋め込むこともできます (これは、ルート署名が PSO の一部であるためです)。

次のフラグは、アプリによって設定され、記述子またはデータに適用されます。

typedef enum D3D12_DESCRIPTOR_RANGE_FLAGS
{
    D3D12_DESCRIPTOR_RANGE_FLAG_NONE    = 0,
    D3D12_DESCRIPTOR_RANGE_FLAG_DESCRIPTORS_VOLATILE    = 0x1,
    D3D12_DESCRIPTOR_RANGE_FLAG_DATA_VOLATILE   = 0x2,
    D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC_WHILE_SET_AT_EXECUTE    = 0x4,
    D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC = 0x8
} D3D12_DESCRIPTOR_RANGE_FLAGS;

typedef enum D3D12_ROOT_DESCRIPTOR_FLAGS
{
    D3D12_ROOT_DESCRIPTOR_FLAG_NONE = 0,
    D3D12_ROOT_DESCRIPTOR_FLAG_DATA_VOLATILE    = 0x2,
    D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC_WHILE_SET_AT_EXECUTE = 0x4,
    D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC  = 0x8
} D3D12_ROOT_DESCRIPTOR_FLAGS;

DESCRIPTORS_VOLATILE

このフラグが設定されていると、アプリケーションは、ルート記述子テーブルが指している記述子ヒープ内の記述子をいつでも変更できます。ただし、記述子テーブルをバインドするコマンド リストまたはバンドルの送信が完了していても、実行が終了していない場合は除きます。 たとえば、実行のためにコマンド リストを送信する "前に"、コマンド リストを記録し、それが参照する記述子ヒープ内の記述子を引き続き変更します。 これは、ルート署名バージョン 1.0 でサポートされている唯一の動作です。

DESCRIPTORS_VOLATILE フラグが設定 されていない 場合、記述子は静的です。 このモードのフラグはありません。 静的な記述子とは、ルート記述子テーブルが指す記述子ヒープ内の記述子が、その記述子テーブルがコマンド リストまたはバンドルに (記録中に) 設定される前に初期化されていて、コマンド リストまたはバンドルが最後に実行を終了するまでその記述子を変更できないことを意味します。 ルート署名バージョン 1.1 では、静的記述子が既定の前提であり、必要に応じ、アプリケーションで DESCRIPTORS_VOLATILE フラグを指定する必要があります。

静的記述子と共に記述子テーブルを使用したバンドルの場合、記述子は、(バンドルの呼び出し時とは対照的に) バンドルの記録時に開始される準備ができている必要があり、バンドルが最後に実行を終了するまで変更されません。 静的記述子を指す記述子テーブルは、バンドルの記録中に設定して、バンドルに継承されないようにする必要があります。 バンドル内で設定され、コマンド リストに戻された、静的記述子を含む記述子テーブルをコマンド リストで使用することは有効です。

記述子が静的な場合は、DESCRIPTORS_VOLATILE フラグを設定する必要がある動作に別の変更があります。 (Texture1D/2D/3D/Cube ビューとは対照的に) バッファー ビューへの境界外アクセスは無効であり、読み取りに対して既定の値が返されたり書き込みがドロップされたりする代わりに、未定義の結果が生成されます (デバイス リセットの可能性も含まれます)。 ハードウェアの境界外アクセス チェックをアプリケーションで利用できないようにする目的は、より効果的であると考えられる場合、ドライバーが静的記述子アクセスをルート記述子アクセスに昇格させることを選択できるようにすることにあります。 ルート記述子は、境界外チェックをサポートしていません。

記述子にアクセスするときにアプリケーションが安全な範囲外のメモリ アクセス動作に依存している場合は、それらの記述子にアクセスする記述子範囲をDESCRIPTORS_VOLATILEとしてマークする必要があります。

DATA_VOLATILE

このフラグが設定されていると、記述子が指しているデータは CPU によっていつでも変更できます。ただし、記述子テーブルをバインドするコマンド リストまたはバンドルの送信が完了していても、実行が終了していない場合は除きます。 これは、ルート署名バージョン 1.0 でサポートされている唯一の動作です。

このフラグは、記述子範囲フラグとルート記述子フラグの両方で使用可能です。

DATA_STATIC_WHILE_SET_AT_EXECUTE

このフラグが設定されていると、記述子が指しているデータは、GPU タイムラインでの実行中に基になるルート記述子または記述子テーブルがコマンド リストまたはバンドルに設定された時点から、後続の描画またはディスパッチがそのデータを参照しなくなる時点まで変更できません。

GPU でルート記述子または記述子テーブルが設定される前であれば、このデータは同じコマンド リストまたはバンドルによって変更することも "できます"。 このデータは、データを参照する描画またはディスパッチが完了していれば、ルート記述子またはそれを指す記述子テーブルがまだコマンド リストまたはバンドルに設定されている間にも変更することができます。 ただし、そうするには、次回ルート記述子または記述子テーブルが逆参照される前に、記述子テーブルをコマンド リストに再バインドする必要があります。 こうすることで、ドライバーは、ルート記述子または記述子テーブルが指すデータが変更されたことを認識できるようになります。

DATA_STATIC_WHILE_SET_AT_EXECUTEとDATA_VOLATILEの重要な違いは、ドライバーがコマンド リスト内のデータ コピーによって記述子が指すデータが追加の状態追跡を行わずに変更されたかどうかをドライバーが判断できないDATA_VOLATILEです。 そのため、たとえば、ドライバーが任意の種類のデータプリフェッチ コマンドをコマンド リストに挿入できる場合 (たとえば、既知のデータへのシェーダー アクセスをより効率的にするために)、DATA_STATIC_WHILE_SET_AT_EXECUTEは 、SetGraphicsRootDescriptorTableSetComputeRootDescriptorTable 、または定数バッファー ビューを設定するメソッドのいずれかを使用して設定された時点でデータプリフェッチを実行するだけで済みます。 シェーダー リソース ビュー、または順序なしのアクセス ビュー。

バンドルの場合、実行時に設定されている間はデータが静的であるという保証は、バンドルの各実行に一意に適用されます。

このフラグは、記述子範囲フラグとルート記述子フラグの両方で使用可能です。

DATA_STATIC

このフラグが設定されていると、記述子が指すデータは、メモリを参照するルート記述子または記述子テーブルが記録中にコマンド リストまたはバンドルで設定されるまでに初期化されています。また、このデータは、コマンド リストまたはバンドルが最後の実行を終了するまで変更できません。

バンドルの場合、静的な期間は、呼び出し元コマンド リストの記録とは対照的に、バンドルの記録中にルート記述子または記述子テーブルを設定するタイミングで始まります。 さらに、静的データを指す記述子テーブルをバンドルに設定して、継承されないようにする必要があります。 バンドル内で設定され、コマンド リストに戻された、静的データを指す記述子テーブルをコマンド リストで使用することは有効です。

このフラグは、記述子範囲フラグとルート記述子フラグの両方で使用可能です。

フラグの組み合わせ

一度に指定できる DATA フラグは 1 つだけです。ただし、サンプラーはデータを指さないため、DATA フラグをまったくサポートしないサンプラー記述子範囲を除きます。

SRV および CBV 記述子範囲に DATA フラグが存在しない場合、既定のDATA_STATIC_WHILE_SET_AT_EXECUTE動作が想定されます。 この既定値がDATA_STATICではなく選択される理由は、ほとんどの場合、DATA_STATIC_WHILE_SET_AT_EXECUTEが安全な既定値になる可能性が非常に高く、DATA_VOLATILEに既定よりも最適化の機会が得られるということです。

UAV 記述子範囲に DATA フラグが存在しない場合は、通常 UAV が書き込まれる場合、既定のDATA_VOLATILE動作が想定されます。

DESCRIPTORS_VOLATILEをDATA_STATICと組み合わせることはできませんが、他の DATA フラグと組み合わせることができます。 DESCRIPTORS_VOLATILEをDATA_STATIC_WHILE_SET_AT_EXECUTEと組み合わせることができる理由は、揮発性記述子では、コマンド リスト/バンドルの実行中に記述子を準備する必要があり、DATA_STATIC_WHILE_SET_AT_EXECUTEは、コマンド リスト/バンドル実行のサブセット内の静的性についてのみ約束を行う必要があるということです。

フラグの概要

次の表は、使用できるフラグの組み合わせをまとめたものです。

有効なD3D12_DESCRIPTOR_RANGE_FLAGS設定 説明
フラグの設定なし 記述子は静的です (既定値)。 データの既定の前提条件: SRV/CBV: DATA_STATIC_WHILE_SET_AT_EXECUTE、UAV: DATA_VOLATILE。 SRV/CBV のこのような既定値は、大多数のルート署名の使用パターンに安全に適合します。
DATA_STATIC 記述子とデータのどちらも静的です。 これにより、ドライバーの最適化の可能性が最大になります。
DATA_VOLATILE 記述子は静的で、データは揮発性です。
DATA_STATIC_WHILE_SET_AT_EXECUTE 記述子は静的で、データは実行時に設定されている間は静的です。
DESCRIPTORS_VOLATILE 記述子は揮発性であり、SRV/CBV: DATA_STATIC_WHILE_SET_AT_EXECUTE、UAV: DATA_VOLATILEのデータに関する既定の想定が行われます。
DESCRIPTORS_VOLATILE |DATA_VOLATILE 記述子とデータはどちらも揮発性で、ルート署名 1.0 と同等です。
DESCRIPTORS_VOLATILE |DATA_STATIC_WHILE_SET_AT_EXECUTE 記述子は揮発性です。ただし、コマンド リストの実行中に変更することはできないことに注意してください。 したがって、実行中にルート記述子テーブルを介して設定されている間はデータが静的であるという追加の宣言を組み合わせることは有効です。基になる記述子は、データが静的であると保証されるよりも長い時間にわたって実質的に静的です。

 

有効なD3D12_ROOT_DESCRIPTOR_FLAGS設定 説明
フラグの設定なし データの既定の前提条件: SRV/CBV: DATA_STATIC_WHILE_SET_AT_EXECUTE、UAV: DATA_VOLATILE。 SRV/CBV のこのような既定値は、大多数のルート署名の使用パターンに安全に適合します。
DATA_STATIC データは静的で、ドライバーの最適化の可能性が最大になります。
DATA_STATIC_WHILE_SET_AT_EXECUTE データは実行時に設定されている間は静的です。
DATA_VOLATILE ルート署名 1.0 と同じです。

 

バージョン 1.1 API のまとめ

次の API 呼び出しにより、バージョン 1.1 が有効になります。

列挙型

これらの列挙型には、記述子とデータの揮発性を指定するための重要なフラグが含まれています。

構造

(バージョン 1.0) から更新された構造体には、揮発性または静的フラグへの参照が含まれます。

関数

次に示すメソッドは、ルート署名の任意のバージョンで動作するよう設計されているため、元の D3D12SerializeRootSignature および D3D12CreateRootSignatureDeserializer 関数と置き換えられます。 シリアル化された形式とは、CreateRootSignature API に渡される形式です。 ルート署名を含むシェーダーを作成した場合、コンパイルされたシェーダーには既にシリアル化されたルート署名が含まれることになります。

メソッド

ルート署名のデータ構造を逆シリアル化するための ID3D12VersionedRootSignatureDeserializer インターフェイスが作成されます。

ヘルパー構造体

バージョン 1.1 のいくつかの構造体の初期化を支援するために、ヘルパー構造体が追加されました。

  • CD3DX12_DESCRIPTOR_RANGE1
  • CD3DX12_ROOT_PARAMETER1
  • CD3DX12_STATIC_SAMPLER1
  • CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC

Helper Structures and Functions for D3D12 (D3D12 のヘルパー構造体および関数)」を参照してください。

静的フラグに違反した場合の結果

上記の記述子およびデータ フラグ (および特定のフラグが設定されないことによって暗示される既定値) は、アプリケーションがドライバーに保証する動作を定義します。 アプリケーションがこの保証に違反した場合、これは無効な動作です。結果は未定義であり、ドライバーやハードウェアによって異なる可能性があります。

デバッグ レイヤーには、フラグを設定せずにルート署名バージョン 1.1 を使用する際の既定値の保証を含め、アプリケーションが保証内容を保っていることを検証するためのオプションがあります。

バージョン管理

シェーダーにアタッチされたルート署名をコンパイルする場合、新しい HLSL コンパイラが既定でルート署名バージョン 1.1 をコンパイルする一方、古い HLSL コンパイラは 1.0 しかサポートしていません。 1.1 のルート署名は、ルート署名 1.1 をサポートしていない OS では動作しないことに注意してください。

シェーダーでコンパイルされたルート署名のバージョンは、/force_rootsig_ver <version> を使用して強制的に特定のバージョンに設定することができます。 バージョンの強制は、(たとえば最適化の目的でのみ機能するが動作に影響を与えないルート署名のサポートされていないフラグを削除することで) 強制されたバージョンでコンパイルされるルート署名の動作をコンパイラが維持できる場合に成功します。

このように、アプリケーションは、たとえばアプリケーションをビルドするときに 1.1 のルート署名を 1.0 と 1.1 の両方にコンパイルし、実行時に OS サポートのレベルに応じて適切なバージョンを選択できます。 ただし、アプリケーションがシェーダーとは別にルート署名を個別にコンパイルする方が最もスペース効率が高くなります (特に複数のバージョンが必要な場合)。 最初にルート署名がアタッチされた状態でシェーダーがコンパイルされていない場合でも、/verifyrootsignature コンパイラ オプションを使用することで、シェーダーとのルート署名の互換性に関するコンパイラ検証の利点が保たれます。 後で実行するときに、ルート署名を持たないシェーダーを使用し、必要なルート署名 (おそらく OS でサポートされている適切なバージョン) を別のパラメーターとして渡すことで、PSO を作成することができます。

ルート署名の作成

ルート署名

HLSL でのルート署名の指定