ルート署名の作成Creating a Root Signature

ルート署名は複雑なデータ構造であり、その中で構造体が入れ子になっています。Root signatures are a complex data structure containing nested structures. 後述するデータ構造定義を使用して、プログラムで定義することができます (メンバーの初期化に役立つメソッドについても説明しています)。These can be defined programmatically using the data structure definition below (which includes methods to help initialize members). 別の方法として、高水準シェーディング言語 (HLSL) で作成することもできます。この方法には、レイアウトがシェーダーに対応していることがコンパイラによって早い段階で検証されるという利点があります。Alternatively, they can be authored in High Level Shading Language (HLSL) – giving the advantage that the compiler will validate early that the layout is compatible with the shader.

ルート署名を作成するための API は、以下で説明するレイアウト記述をシリアル化したもの (自己完結型、ポインターなし) を受け取ります。The API for creating a root signature takes in a serialized (self contained, pointer free) version of the layout description described below. このシリアル化バージョンを C++ のデータ構造から生成するためのメソッドが用意されていますが、シリアル化されたルート署名定義を取得するもう 1 つの方法として、ルート署名付きでコンパイルされたシェーダーからルート署名を取り出すこともできます。A method is provided for generating this serialized version from the C++ data structure, but another way to obtain a serialized root signature definition is to retrieve it from a shader that has been compiled with a root signature.

ルート署名の記述子とデータに対するドライバー最適化を利用する場合は、「ルート署名バージョン 1.1」を参照してください。If you wish to take advantage of driver optimizations for root signature descriptors and data, refer to Root Signature Version 1.1

記述子テーブル バインドの種類Descriptor Table Bind Types

列挙型 D3D12_DESCRIPTOR_RANGE_TYPE では、記述子テーブルのレイアウト定義の一部として参照できる記述子の種類が定義されています。The enum D3D12_DESCRIPTOR_RANGE_TYPE defines the types of descriptors that can be referenced as part of a descriptor table layout definition.

範囲として定義されているので、たとえば記述子テーブルの中に 100 個の SRV が含まれる場合でも、100 個ではなく 1 個のエントリでその範囲を宣言できます。It is a range so that, for example if part of a descriptor table a descriptor table has 100 SRVs, that range can be declared in one entry rather than 100. したがって、記述子テーブル定義は範囲のコレクションです。So a descriptor table definition is a collection of ranges.

typedef enum D3D12_DESCRIPTOR_RANGE_TYPE
{
  D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
  D3D12_DESCRIPTOR_RANGE_TYPE_UAV,
  D3D12_DESCRIPTOR_RANGE_TYPE_CBV,
  D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER
} D3D12_DESCRIPTOR_RANGE_TYPE;

記述子の範囲Descriptor Range

D3D12_DESCRIPTOR_RANGE 構造体では、記述子テーブル内の特定の種類 (たとえば SRV) の記述子の範囲が定義されています。The D3D12_DESCRIPTOR_RANGE structure defines a range of descriptors of a given type (such as SRVs) within a descriptor table.

D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND #define が一般的に、D3D12_DESCRIPTOR_RANGEOffsetInDescriptorsFromTableStart パラメーターに使用されます。The D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND #define can typically be used for the OffsetInDescriptorsFromTableStart parameter of D3D12_DESCRIPTOR_RANGE. これは、定義しようとする記述子範囲を、記述子テーブル内の直前の範囲の後に追加することを意味します。This means append the descriptor range being defined after the previous one in the descriptor table. アプリケーションで記述子のエイリアスを使用する場合、または何らかの理由によりスロットをスキップする必要がある場合は、OffsetInDescriptorsFromTableStart を必要なオフセットに設定できます。If the application wants to alias descriptors or for some reason wants to skip slots, it can set OffsetInDescriptorsFromTableStart to whatever offset is desired. 異なる種類の範囲が重複するように定義することはできません。Defining overlapping ranges of different types is invalid.

RangeTypeNumDescriptorsBaseShaderRegisterRegisterSpace の組み合わせによって指定されるシェーダー レジスタのセットが、1 つのルート署名内の宣言のうち同じ D3D12_SHADER_VISIBILITY (後のシェーダー可視性のセクションを参照) を持つものの間で競合したり、互いに重複したりしてはなりません。The set of shader registers specified by the combination of RangeType, NumDescriptors, BaseShaderRegister, and RegisterSpace cannot conflict or overlap across any declarations in a root signature that have common D3D12_SHADER_VISIBILITY (refer to the shader visibility section below).

記述子テーブルのレイアウトDescriptor Table Layout

D3D12_ROOT_DESCRIPTOR_TABLE 構造体は、記述子テーブルのレイアウトを記述子範囲のコレクションとして宣言します。これらの範囲は、記述子ヒープ内に順に出現します。The D3D12_ROOT_DESCRIPTOR_TABLE structure declares the layout of a descriptor table as a collection of descriptor ranges that appear one after the other in a descriptor heap. サンプラーは、CBV/UAV/SRV と同じ記述子テーブル内に存在していてはなりません。Samplers are not allowed in the same descriptor table as CBV/UAV/SRVs.

この構造体は、ルート署名のスロットの種類を D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE に設定するときに使用されます。This struct is used when the root signature slot type is set to D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE.

グラフィックス (CBV、SRV、UAV、サンプラー) 記述子テーブルを設定するには、ID3D12GraphicsCommandList::SetGraphicsRootDescriptorTable を使います。To set a graphics (CBV, SRV, UAV, Sampler) descriptor table, use ID3D12GraphicsCommandList::SetGraphicsRootDescriptorTable.

計算記述子テーブルを設定するには、ID3D12GraphicsCommandList::SetComputeRootDescriptorTable を使います。To set a compute descriptor table, use ID3D12GraphicsCommandList::SetComputeRootDescriptorTable.

ルート定数Root Constants

D3D12_ROOT_CONSTANTS 構造体は、一連の定数をルート署名内でインラインで宣言します。これらの定数は、シェーダー内で 1 つの定数バッファーとして出現します。The D3D12_ROOT_CONSTANTS structure declares constants inline in the root signature that appear in shaders as one constant buffer.

この構造体は、ルート署名のスロットの種類を D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS に設定するときに使用されます。This struct is used when the root signature slot type is set to D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS.

ルート記述子Root Descriptor

D3D12_ROOT_DESCRIPTOR 構造体は、記述子 (シェーダー内に出現するもの) をルート署名内でインラインで宣言します。The D3D12_ROOT_DESCRIPTOR structure declares descriptors (that appear in shaders) inline in the root signature.

この構造体は、ルート署名のスロットの種類を D3D12_ROOT_PARAMETER_TYPE_CBVD3D12_ROOT_PARAMETER_TYPE_SRV、または D3D12_ROOT_PARAMETER_TYPE_UAV に設定するときに使用されます。This struct is used when the root signature slot type is set to D3D12_ROOT_PARAMETER_TYPE_CBV, D3D12_ROOT_PARAMETER_TYPE_SRV or D3D12_ROOT_PARAMETER_TYPE_UAV.

シェーダーの可視性Shader Visibility

D3D12_SHADER_VISIBILITY 列挙型のメンバーが D3D12_ROOT_PARAMETER のシェーダー可視性パラメーターの値として設定され、これによってどのシェーダーが特定のルート署名スロットの内容を参照するできるかが決まります。The member of D3D12_SHADER_VISIBILITY enum set into the shader visibility parameter of D3D12_ROOT_PARAMETER determines which shaders see the contents of a given root signature slot. 計算の場合は常に _ALL を使います (アクティブなステージが 1 つだけであるため)。Compute always uses _ALL (since there is only one active stage). グラフィックスの場合は選択可能ですが、_ALL を使用すると、そのルート署名スロットにおいてバインドされているものはどのシェーダー ステージでも参照できるようになります。Graphics can choose, but if it uses _ALL, all shader stages see whatever is bound at the root signature slot.

シェーダー可視性の用途の 1 つは、作成するシェーダーのシェーダー ステージごとに異なるバインドを期待するように、重複する名前空間を使ってプログラミングする場合です。One use of shader visibility is to help with shaders that are authored expecting different bindings per shader stage using an overlapping namespace. たとえば、頂点シェーダーで次のように宣言するとします。For example, a vertex shader may declare:

 

Texture2D foo : register(t0);"Texture2D foo : register(t0);"

 

ピクセル シェーダーでも次のように宣言できます。and the pixel shader may also declare:

 

Texture2D bar : register(t0);Texture2D bar : register(t0);

アプリケーションで t0 へのルート署名バインドを VISIBILITY_ALL に設定した場合は、両方のシェーダーが同じテクスチャを参照します。If the application makes a root signature binding to t0 VISIBILITY_ALL, both shaders see the same texture. 各シェーダーが別のテクスチャを参照するようにしたい場合は、ルート署名スロットを 2 つ定義して VISIBILITY_VERTEX と _PIXEL を指定します。If the shader defines actually wants each shader to see different textures, it can define 2 root signature slots with VISIBILITY_VERTEX and _PIXEL. ルート署名スロットに対してどの可視性が設定されているかにかかわらず、1 つの固定最大ルート署名サイズに対するコストは常に同じです (コストは SlotType のみに依存します)。No matter what the visibility is on a root signature slot, it always has the same cost (cost only depending on what the SlotType is) towards one fixed maximum root signature size.

ローエンドの D3D11 ハードウェアでは、ルート レイアウト内の記述子テーブルのサイズを検証するときに SHADER_VISIBILITY も考慮されます。D3D11 ハードウェアの中には、ステージごとにサポートできるバインド数の上限が設定されているものがあるからです。On low end D3D11 hardware, SHADER_VISIBILITY is also taken into account used when validating the sizes of descriptor tables in a root layout, since some D3D11 hardware can only support a maximum amount of bindings per-stage. このような制限が課せられるのは低階層のハードウェアで実行されるだけであり、現代的なハードウェアでは制限はありません。These restrictions are only imposed when running on low tier hardware and do not limit more modern hardware at all.

1 つのルート署名で複数の記述子テーブルが定義されていて、これらが同じ名前空間内で重なり合っているときに (シェーダーに対するレジスタ バインド)、そのうち 1 つでも可視性に _ALL が指定されている場合は、レイアウトは無効となります (作成は失敗します)。If a root signature has multiple descriptor tables defined that overlap each other in namespace (the register bindings to the shader) and any one of them specifies _ALL for visibility, the layout is invalid (creation will fail).

ルート署名の定義Root Signature Definition

D3D12_ROOT_SIGNATURE_DESC 構造体は、その中に記述子テーブルとインライン定数を持つことができ、各スロットの種類は D3D12_ROOT_PARAMETER 構造体と列挙型 D3D12_ROOT_PARAMETER_TYPE によって定義されます。The D3D12_ROOT_SIGNATURE_DESC structure can contain descriptor tables and inline constants, each slot type defined by the D3D12_ROOT_PARAMETER structure and the enum D3D12_ROOT_PARAMETER_TYPE.

ルート署名スロットを作成するには、ID3D12GraphicsCommandListSetComputeRoot*** および SetGraphicsRoot*** のメソッドを参照してください。To initiate a root signature slot, refer to the SetComputeRoot*** and SetGraphicsRoot*** methods of ID3D12GraphicsCommandList.

静的サンプラーをルート署名の中で記述するには、D3D12_STATIC_SAMPLER 構造体を使用します。Static samplers are described in the root signature using the D3D12_STATIC_SAMPLER structure.

多数のフラグによって、特定のシェーダーによるルート署名のアクセスが制限されます。D3D12_ROOT_SIGNATURE_FLAGS を参照してください。A number of flags limit the access of certain shaders to the root signature, refer to D3D12_ROOT_SIGNATURE_FLAGS.

ルート署名データ構造のシリアル化/逆シリアル化Root Signature Data Structure Serialization / Deserialization

このセクションで説明するメソッドは D3D12Core.dll によってエクスポートされるものであり、ルート署名のデータ構造をシリアル化および逆シリアル化するためのメソッドが用意されています。The methods described in this section are exported by D3D12Core.dll and provide methods for serializing and deserializing a root signature data structure.

シリアル化された形式は、ルート署名を作成するときに API に渡される形式です。The serialized form is what is passed into the API when creating a root signature. シェーダーをルート署名付きで作成した場合は (その機能が追加されているとき)、コンパイルされたシェーダーの中には既にシリアル化されたルート署名が存在することになります。If a shader has been authored with a root signature in it (when that capability is added), then the compiled shader will contain a serialized root signature in it already.

アプリケーションで手続き型で D3D12_ROOT_SIGNATURE_DESC データ構造を生成する場合は、D3D12SerializeRootSignature を使用してシリアル化された形式にする必要があります。If an application procedurally generates a D3D12_ROOT_SIGNATURE_DESC data structure, it must make the serialized form using D3D12SerializeRootSignature. その出力を ID3D12Device::CreateRootSignature に渡すことができます。The output of that can be passed into ID3D12Device::CreateRootSignature.

アプリケーションにシリアル化されたルート署名が既にある場合、またはコンパイル済みシェーダーの中にルート署名が存在していてそのレイアウト定義をプログラムで検出したい場合は ("リフレクション" と呼ばれます)、D3D12CreateRootSignatureDeserializer を呼び出すことができます。If an application has a serialized root signature already, or has a compiled shader that contains a root signature and wishes to programmatically discover the layout definition (known as "reflection"), D3D12CreateRootSignatureDeserializer can be called. これによって生成される ID3D12RootSignatureDeserializer インターフェイスには、逆シリアル化された D3D12_ROOT_SIGNATURE_DESC データ構造体を返すメソッドがあります。This generates an ID3D12RootSignatureDeserializer interface, which contains a method to return the deserialized D3D12_ROOT_SIGNATURE_DESC data structure. このインターフェイスが、逆シリアル化されたデータ構造体の有効期間全体の所有者となります。The interface owns the lifetime of the deserialized data structure.

ルート署名作成 APIRoot Signature Creation API

ID3D12Device::CreateRootSignature API は、シリアル化された形式のルート署名を受け取ります。The ID3D12Device::CreateRootSignature API takes in a serialized version of a root signature.

パイプライン状態オブジェクトでのルート署名Root Signature in Pipeline State Objects

パイプライン状態を作成するメソッド (ID3D12Device::CreateGraphicsPipelineState および ID3D12Device::CreateComputePipelineState) は、省略可能な ID3D12RootSignature インターフェイスを入力パラメーターとして受け取ります (D3D12_GRAPHICS_PIPELINE_STATE_DESC 構造体に格納されます)。The methods to create pipeline state (ID3D12Device::CreateGraphicsPipelineState and ID3D12Device::CreateComputePipelineState ) take an optional ID3D12RootSignature interface as an input parameter (stored in a D3D12_GRAPHICS_PIPELINE_STATE_DESC structure). これは、シェーダー内の既存のルート署名よりも優先されます。This will override any root signature already in the shaders.

ルート署名がパイプライン状態作成メソッドの 1 つに渡された場合は、そのルート署名は PSO 内のすべてのシェーダーに対応しているかどうかの検証が行われ、その後でドライバーに渡されてすべてのシェーダーとともに使用されます。If a root signature is passed into one of the create pipeline state methods, this root signature is validated against all the shaders in the PSO for compatibility and given to the driver to use with all the shaders. これらのシェーダーの中に、別のルート署名を持つものがある場合は、API に渡されたルート署名で置き換えられます。If any of the shaders has a different root signature in it, it gets replaced by the root signature passed in at the API. ルート署名が渡されない場合は、渡されるすべてのシェーダーが同じルート署名を持つ必要があり、これがドライバーに提供されます。If a root signature is not passed in, all shaders passed in must have a root signature and they must match – this will be given to the driver. PSO をコマンド リストまたはバンドルに対して設定しても、ルート署名が変更されることはありません。Setting a PSO on a command list or bundle does not change the root signature. このことを達成するには、メソッド SetGraphicsRootSignatureSetComputeRootSignature を使用します。That is accomplished by the methods SetGraphicsRootSignature and SetComputeRootSignature. 描画 (グラフィックス)/ディスパッチ (計算) が呼び出される時点までに、アプリケーションは現在の PSO が現在のルート署名と一致していることを保証する必要があります。このとおりでない場合は、動作は未定義となります。By the time draw(graphics)/dispatch(compute) is invoked, the application must ensure that the current PSO matches the current root signature; otherwise, the behavior is undefined.

バージョン 1.1 のルート署名を定義するためのコードCode for Defining a Version 1.1 Root Signature

以下の例では、次の形式のルート署名を作成する方法を示します。The example below shows how to create a root signature with the following format:

RootParameterIndexRootParameterIndex 内容Contents
[0][0] ルート定数: { b2 }Root constants: { b2 } (1 CBV)(1 CBV)
[1][1] 記述子テーブル: { t2-t7、u0-u3 }Descriptor table: { t2-t7, u0-u3 } (6 SRV + 4 UAV)(6 SRVs + 4 UAVs)
[2][2] ルート CBV: { b0 }Root CBV: { b0 } (1 CBV、静的データ)(1 CBV, static data)
[3][3] 記述子テーブル: { s0-s1 }Descriptor table: { s0-s1 } (2 サンプラー)(2 Samplers)
[4][4] 記述子テーブル: { t8 - 無制限 }Descriptor table: { t8 - unbounded } (SRV 数無制限、揮発性記述子)(unbounded # of SRVs, volatile descriptors)
[5][5] 記述子テーブル: { (t0、space1) - 無制限 }Descriptor table: { (t0, space1) - unbounded } (SRV 数無制限、揮発性記述子)(unbounded # of SRVs, volatile descriptors)
[6][6] 記述子テーブル: { b1 }Descriptor table: { b1 } (1 CBV、静的データ)(1 CBV, static data)

 

ルート署名のほとんどの部分をほとんどの時間使用する方が、ルート署名を頻繁に切り替えるよりも良い方法です。If most parts of the root signature get used most of the time it can be better than having to switch the root signature too frequently. アプリケーションでは、ルート署名内のエントリを、変更頻度が高い順に並べ替える必要があります。Applications should sort entries in the root signature from most frequently changing to least. アプリが、ルート署名のどの部分かを問わずバインドを変更したときは、ドライバーがルート署名の状態の一部または全部をコピーすることが必要になる場合があるため、このことは状態変更が多い場合にかなりのコストになる可能性があります。When an app changes the bindings to any part of the root signature, the driver may have to make a copy of some or all of root signature state, which can become a nontrivial cost when multiplied across many state changes.

さらに、ルート署名によって定義される静的サンプラーは、異方性テクスチャ フィルター処理をシェーダー レジスタ s3 で行います。In addition, the root signature will define a static sampler that does anisotropic texture filtering at shader register s3.

このルート署名がバインドされた後は、記述子テーブル、ルート CBV、および定数を [0..6] パラメーター空間に割り当てることができます。After this root signature is bound, descriptor tables, root CBV and constants can be assigned to the [0..6] parameter space. たとえば、記述子テーブル (記述子ヒープ内の範囲) を、ルート パラメーター [1] と [3..6] のそれぞれにバインドできます。e.g. descriptor tables (ranges in a descriptor heap) can be bound at each of root parameters [1] and [3..6].

CD3DX12_DESCRIPTOR_RANGE1 DescRange[6];

DescRange[0].Init(D3D12_DESCRIPTOR_RANGE_SRV,6,2); // t2-t7
DescRange[1].Init(D3D12_DESCRIPTOR_RANGE_UAV,4,0); // u0-u3
DescRange[2].Init(D3D12_DESCRIPTOR_RANGE_SAMPLER,2,0); // s0-s1
DescRange[3].Init(D3D12_DESCRIPTOR_RANGE_SRV,-1,8, 0,
                  D3D12_DESCRIPTOR_RANGE_FLAG_DESCRIPTORS_VOLATILE); // t8-unbounded
DescRange[4].Init(D3D12_DESCRIPTOR_RANGE_SRV,-1,0,1,
                  D3D12_DESCRIPTOR_RANGE_FLAG_DESCRIPTORS_VOLATILE); 
                                                            // (t0,space1)-unbounded
DescRange[5].Init(D3D12_DESCRIPTOR_RANGE_CBV,1,1,
                  D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC); // b1

CD3DX12_ROOT_PARAMETER1 RP[7];

RP[0].InitAsConstants(3,2); // 3 constants at b2
RP[1].InitAsDescriptorTable(2,&DescRange[0]); // 2 ranges t2-t7 and u0-u3
RP[2].InitAsConstantBufferView(0, 0, 
                               D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC); // b0
RP[3].InitAsDescriptorTable(1,&DescRange[2]); // s0-s1
RP[4].InitAsDescriptorTable(1,&DescRange[3]); // t8-unbounded
RP[5].InitAsDescriptorTable(1,&DescRange[4]); // (t0,space1)-unbounded
RP[6].InitAsDescriptorTable(1,&DescRange[5]); // b1

CD3DX12_STATIC_SAMPLER StaticSamplers[1];
StaticSamplers[0].Init(3, D3D12_FILTER_ANISOTROPIC); // s3
CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC RootSig(7,RP,1,StaticSamplers);
ID3DBlob* pSerializedRootSig;
CheckHR(D3D12SerializeVersionedRootSignature(&RootSig,pSerializedRootSig)); 

ID3D12RootSignature* pRootSignature;
hr = CheckHR(pDevice->CreateRootSignature(
    pSerializedRootSig->GetBufferPointer(),pSerializedRootSig->GetBufferSize(),
    __uuidof(ID3D12RootSignature),
    &pRootSignature));

次のコードでは、上記のルート署名をグラフィックス コマンド リストに対して使用する方法を示します。The following code illustrates how the above root signature might be used on a graphics command list.

InitializeMyDescriptorHeapContentsAheadOfTime(); // for simplicity of the 
                                                 // example
CreatePipelineStatesAhreadOfTime(pRootSignature); // The root signature is passed into 
                                     // shader / pipeline state creation
...

ID3D12DescriptorHeap* pHeaps[2] = {pCommonHeap, pSamplerHeap};
pGraphicsCommandList->SetDescriptorHeaps(pHeaps,2);
pGraphicsCommandList->SetGraphicsRootSignature(pRootSignature);
pGraphicsCommandList->SetGraphicsRootDescriptorTable(
                        6,heapOffsetForMoreData,DescRange[5].NumDescriptors);
pGraphicsCommandList->SetGraphicsRootDescriptorTable(5,heapOffsetForMisc,5000); 
pGraphicsCommandList->SetGraphicsRootDescriptorTable(4,heapOffsetForTerrain,20000);
pGraphicsCommandList->SetGraphicsRootDescriptorTable(
                        3,heapOffsetForSamplers,DescRange[2].NumDescriptors);
pGraphicsCommandList->SetComputeRootConstantBufferView(2,pDynamicCBHeap,&CBVDesc);

MY_PER_DRAW_STUFF stuff;
InitMyPerDrawStuff(&stuff);
pGraphicsCommandList->SetSetGraphicsRoot32BitConstants(
                        0,&stuff,0,RTSlot[0].Constants.Num32BitValues);

SetMyRTVAndOtherMiscBindings();

for(UINT i = 0; i < numObjects; i++)
{
    pGraphicsCommandList->SetPipelineState(PSO[i]);
    pGraphicsCommandList->SetGraphicsRootDescriptorTable(
                    1,heapOffsetForFooAndBar[i],DescRange[1].NumDescriptors);
    pGraphicsCommandList->SetGraphicsRoot32BitConstant(0,&i,1,drawIDOffset);
    SetMyIndexBuffers(i);
    pGraphicsCommandList->DrawIndexedInstanced(...);
}

ルート署名Root Signatures

HLSL でのルート署名の指定Specifying Root Signatures in HLSL

ルート署名の使用Using a Root Signature