Création d’une signature racine

Les signatures racines sont une structure de données complexe contenant des structures imbriquées. Ceux-ci peuvent être définis par programmation à l’aide de la définition de structure de données ci-dessous (qui inclut des méthodes pour faciliter l’initialisation des membres). Elles peuvent également être créées en HLSL (High Level Shading Language), ce qui offre l’avantage que le compilateur vérifie rapidement que la disposition est compatible avec le nuanceur.

L’API de création d’une signature racine prend une version sérialisée (autonome, sans pointeur) de la description de la disposition décrite ci-dessous. Une méthode est fournie pour générer cette version sérialisée à partir de la structure de données C++, mais une autre façon d’obtenir une définition de signature racine sérialisée consiste à la récupérer à partir d’un nuanceur compilé avec une signature racine.

Si vous souhaitez tirer parti des optimisations des pilotes pour les données et les descripteurs de signature racine, consultez Signature racine version 1.1

Types de liaison de table de descripteur

L’énumération D3D12_DESCRIPTOR_RANGE_TYPE définit les types de descripteurs qui peuvent être référencés dans le cadre d’une définition de disposition de table de descripteur.

Il s’agit d’une plage de sorte que, par exemple, si une partie d’une table de descripteur a 100 VSR, cette plage peut être déclarée dans une seule entrée au lieu de 100. Ainsi, une définition de table de descripteur est une collection de plages.

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;

Plage de descripteurs

La structure D3D12_DESCRIPTOR_RANGE définit une plage de descripteurs d’un type donné (par exemple, les SDR) au sein d’une table de descripteur.

La D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND macro peut généralement être utilisée pour le OffsetInDescriptorsFromTableStart paramètre de D3D12_DESCRIPTOR_RANGE. Cela signifie ajouter la plage de descripteur définie après la précédente dans la table de descripteur. Si l’application souhaite alias des descripteurs ou, pour une raison quelconque, souhaite ignorer les emplacements, elle peut définir OffsetInDescriptorsFromTableStart sur le décalage souhaité. La définition de plages qui se chevauchent de différents types n’est pas valide.

L’ensemble de registres de nuanceur spécifié par la combinaison de RangeType, NumDescriptors, BaseShaderRegisteret RegisterSpace ne peut pas entrer en conflit ou se chevaucher entre les déclarations d’une signature racine qui ont des D3D12_SHADER_VISIBILITY communes (reportez-vous à la section de visibilité du nuanceur ci-dessous).

Disposition du tableau de descripteur

La structure D3D12_ROOT_DESCRIPTOR_TABLE déclare la disposition d’une table de descripteur en tant que collection de plages de descripteurs qui commencent à un décalage donné d’un tas de descripteurs. Les échantillonneurs ne sont pas autorisés dans la même table de descripteur que cbV/UAV/SRV.

Ce struct est utilisé lorsque le type d’emplacement de signature racine est défini sur D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE.

Pour définir une table de descripteur graphique (CBV, SRV, UAV, Sampler), utilisez ID3D12GraphicsCommandList::SetGraphicsRootDescriptorTable.

Pour définir une table de descripteur de calcul, utilisez ID3D12GraphicsCommandList::SetComputeRootDescriptorTable.

Constantes racines

La structure D3D12_ROOT_CONSTANTS déclare les constantes inline dans la signature racine qui apparaissent dans les nuanceurs sous la forme d’une mémoire tampon constante.

Ce struct est utilisé lorsque le type d’emplacement de signature racine est défini sur D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS.

Descripteur racine

La structure D3D12_ROOT_DESCRIPTOR déclare les descripteurs (qui apparaissent dans les nuanceurs) inline dans la signature racine.

Ce struct est utilisé lorsque le type d’emplacement de signature racine est défini sur D3D12_ROOT_PARAMETER_TYPE_CBV, D3D12_ROOT_PARAMETER_TYPE_SRV ou D3D12_ROOT_PARAMETER_TYPE_UAV.

Visibilité du nuanceur

Le membre de D3D12_SHADER_VISIBILITY enum défini dans le paramètre de visibilité du nuanceur de D3D12_ROOT_PARAMETER détermine quels nuanceurs voient le contenu d’un emplacement de signature racine donné. Le calcul utilise toujours _ALL (car il n’y a qu’une seule étape active). Les graphiques peuvent choisir, mais s’ils utilisent _ALL, toutes les étapes du nuanceur voient tout ce qui est lié à l’emplacement de signature racine.

L’une des utilisations de la visibilité du nuanceur est d’aider les nuanceurs qui sont créés en attendant différentes liaisons par étape de nuanceur à l’aide d’un espace de noms qui se chevauche. Par exemple, un nuanceur de vertex peut déclarer :

Texture2D foo : register(t0);

et le nuanceur de pixels peut également déclarer :

Texture2D bar : register(t0);

Si l’application établit une liaison de signature racine à t0 VISIBILITY_ALL, les deux nuanceurs voient la même texture. Si le nuanceur définit souhaite réellement que chaque nuanceur voit des textures différentes, il peut définir 2 emplacements de signature racine avec VISIBILITY_VERTEX et _PIXEL. Quelle que soit la visibilité sur un emplacement de signature racine, il a toujours le même coût (en fonction de ce qu’est slotType) par rapport à une taille de signature racine maximale fixe.

Sur le matériel D3D11 de bas de gamme, SHADER_VISIBILITY est également pris en compte lors de la validation des tailles des tables de descripteurs dans une disposition racine, car certains matériels D3D11 ne peuvent prendre en charge qu’une quantité maximale de liaisons par étape. Ces restrictions sont imposées uniquement lors de l’exécution sur du matériel de bas niveau et ne limitent pas du tout le matériel plus moderne.

Si une signature racine a plusieurs tables de descripteurs définies qui se chevauchent dans l’espace de noms (les liaisons de registre au nuanceur) et que l’une d’elles spécifie _ALL pour la visibilité, la disposition n’est pas valide (la création échoue).

Définition de signature racine

La structure D3D12_ROOT_SIGNATURE_DESC peut contenir des tables de descripteurs et des constantes inline, chaque type d’emplacement défini par la structure D3D12_ROOT_PARAMETER et l’énumération D3D12_ROOT_PARAMETER_TYPE.

Pour lancer un emplacement de signature racine, reportez-vous aux méthodes SetComputeRoot*** et SetGraphicsRoot*** de ID3D12GraphicsCommandList.

Les échantillonneurs statiques sont décrits dans la signature racine à l’aide de la structure D3D12_STATIC_SAMPLER .

Un certain nombre d’indicateurs limitent l’accès de certains nuanceurs à la signature racine. Reportez-vous à D3D12_ROOT_SIGNATURE_FLAGS.

Sérialisation/désérialisation de la structure de données de signature racine

Les méthodes décrites dans cette section sont exportées par D3D12Core.dll et fournissent des méthodes pour sérialiser et désérialiser une structure de données de signature racine.

Le formulaire sérialisé est ce qui est passé dans l’API lors de la création d’une signature racine. Si un nuanceur a été créé avec une signature racine (lorsque cette fonctionnalité est ajoutée), le nuanceur compilé contient déjà une signature racine sérialisée.

Si une application génère de manière procédurale une structure de données D3D12_ROOT_SIGNATURE_DESC , elle doit créer le formulaire sérialisé à l’aide de D3D12SerializeRootSignature. Sortie de qui peut être passée dans ID3D12Device::CreateRootSignature.

Si une application a déjà une signature racine sérialisée, ou si elle a un nuanceur compilé qui contient une signature racine et souhaite découvrir par programmation la définition de disposition (appelée « réflexion »), D3D12CreateRootSignatureDeserializer peut être appelé. Cela génère une interface ID3D12RootSignatureDeserializer , qui contient une méthode pour retourner la structure de données D3D12_ROOT_SIGNATURE_DESC désérialisée. L’interface possède la durée de vie de la structure de données désérialisée.

API de création de signature racine

L’API ID3D12Device::CreateRootSignature prend une version sérialisée d’une signature racine.

Signature racine dans les objets d’état de pipeline

Les méthodes permettant de créer l’état du pipeline (ID3D12Device::CreateGraphicsPipelineState et ID3D12Device::CreateComputePipelineState ) prennent une interface ID3D12RootSignature facultative en tant que paramètre d’entrée (stocké dans une structure D3D12_GRAPHICS_PIPELINE_STATE_DESC ). Cela remplacera toute signature racine déjà dans les nuanceurs.

Si une signature racine est passée dans l’une des méthodes d’état du pipeline de création, cette signature racine est validée par rapport à tous les nuanceurs dans l’authentification unique pour la compatibilité et donnée au pilote à utiliser avec tous les nuanceurs. Si l’un des nuanceurs contient une signature racine différente, il est remplacé par la signature racine passée au niveau de l’API. Si aucune signature racine n’est passée, tous les nuanceurs passés doivent avoir une signature racine et ils doivent correspondre au pilote. La définition d’un psO sur une liste de commandes ou un bundle ne modifie pas la signature racine. Cela est effectué par les méthodes SetGraphicsRootSignature et SetComputeRootSignature. Au moment où draw(graphics)/dispatch(compute) est appelé, l’application doit s’assurer que l’authentification unique actuelle correspond à la signature racine actuelle ; sinon, le comportement n’est pas défini.

Code pour la définition d’une signature racine version 1.1

L’exemple ci-dessous montre comment créer une signature racine au format suivant :

RootParameterIndex Contenu Valeurs
[0] Constantes racines : { b2 } (1 CBV)
[1] Table de descripteur : { t2-t7, u0-u3 } (6 VSR + 4 UAV)
[2] CbV racine : { b0 } (1 CBV, données statiques)
[3] Table de descripteur : { s0-s1 } (2 échantillonneurs)
[4] Table de descripteur : { t8 - unbounded } (nombre illimité de SRV, descripteurs volatiles)
[5] Table de descripteur : { (t0, espace1) - unbounded } (nombre illimité de SRV, descripteurs volatiles)
[6] Table de descripteur : { b1 } (1 CBV, données statiques)

 

Si la plupart des parties de la signature racine sont utilisées la plupart du temps, il peut être préférable de devoir changer la signature racine trop fréquemment. Les applications doivent trier les entrées de la signature racine de la modification la plus fréquente à la plus petite. Lorsqu’une application remplace les liaisons par n’importe quelle partie de la signature racine, le pilote peut avoir à effectuer une copie de tout ou partie de l’état de signature racine, ce qui peut devenir un coût nontrivial lorsqu’il est multiplié sur plusieurs changements d’état.

En outre, la signature racine définit un échantillonneur statique qui effectue un filtrage de textures anisotropes au registre du nuanceur s3.

Une fois cette signature racine liée, les tables de descripteur, le cbV racine et les constantes peuvent être affectés à l’espace de paramètre [0..6]. Par exemple, les tables de descripteurs (plages dans un tas de descripteurs) peuvent être liées à chacun des paramètres racine [1] et [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));

Le code suivant illustre comment la signature racine ci-dessus peut être utilisée dans une liste de commandes graphiques.

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(2,pHeaps);
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->SetGraphicsRoot32BitConstants(
                        0,RTSlot[0].Constants.Num32BitValues,&stuff,0);

SetMyRTVAndOtherMiscBindings();

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

Signatures racine

Spécification de signatures racine en langage HLSL

Utilisation d’une signature racine