Spécification de signatures racine en langage HLSL

La spécification de signatures racines dans HLSL Shader Model 5.1 est une alternative à leur spécification en code C++.

Exemple de signature racine HLSL

Une signature racine peut être spécifiée dans HLSL sous forme de chaîne. La chaîne contient une collection de clauses séparées par des virgules qui décrivent les composants constitutifs de signature racine. La signature racine doit être identique entre les nuanceurs pour n’importe quel objet d’état de pipeline (PSO). Voici un exemple :

Signature racine version 1.0

#define MyRS1 "RootFlags( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | " \
                         "DENY_VERTEX_SHADER_ROOT_ACCESS), " \
              "CBV(b0, space = 1), " \
              "SRV(t0), " \
              "UAV(u0, visibility = SHADER_VISIBILITY_GEOMETRY), " \
              "DescriptorTable( CBV(b0), " \
                               "UAV(u1, numDescriptors = 2), " \
                               "SRV(t1, numDescriptors = unbounded)), " \
              "DescriptorTable(Sampler(s0, numDescriptors = 2)), " \
              "RootConstants(num32BitConstants=1, b9), " \
              "DescriptorTable( UAV(u3), " \
                               "UAV(u4), " \
                               "UAV(u5, offset=1)), " \

              "StaticSampler(s2)," \
              "StaticSampler(s3, " \
                             "addressU = TEXTURE_ADDRESS_CLAMP, " \
                             "filter = FILTER_MIN_MAG_MIP_LINEAR )"

Cette définition donne la signature racine suivante, notant :

  • Utilisation des paramètres par défaut.
  • b0 et (b0, espace=1) ne sont pas en conflit
  • u0 n’est visible que par le nuanceur de géométrie
  • u4 et u5 sont alias vers le même descripteur dans un tas

une signature racine spécifiée à l’aide du langage de nuanceur de haut niveau

Signature racine version 1.1

La version 1.1 de la signature racine permet d’optimiser les pilotes sur les données et les descripteurs de signature racine.

#define MyRS1 "RootFlags( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | " \
                         "DENY_VERTEX_SHADER_ROOT_ACCESS), " \
              "CBV(b0, space = 1, flags = DATA_STATIC), " \
              "SRV(t0), " \
              "UAV(u0), " \
              "DescriptorTable( CBV(b1), " \
                               "SRV(t1, numDescriptors = 8, " \
                               "        flags = DESCRIPTORS_VOLATILE), " \
                               "UAV(u1, numDescriptors = unbounded, " \
                               "        flags = DESCRIPTORS_VOLATILE)), " \
              "DescriptorTable(Sampler(s0, space=1, numDescriptors = 4)), " \
              "RootConstants(num32BitConstants=3, b10), " \
              "StaticSampler(s1)," \
              "StaticSampler(s2, " \
                             "addressU = TEXTURE_ADDRESS_CLAMP, " \
                             "filter = FILTER_MIN_MAG_MIP_LINEAR )"

Le langage de signature racine HLSL correspond étroitement aux API de signature racine C++ et a une puissance expressive équivalente. La signature racine est spécifiée sous la forme d’une séquence de clauses, séparées par des virgules. L’ordre des clauses est important, car l’ordre de l’analyse détermine la position de l’emplacement dans la signature racine. Chaque clause prend un ou plusieurs paramètres nommés. Toutefois, l’ordre des paramètres n’est pas important.

RootFlags

La clause RootFlags facultative prend soit 0 (la valeur par défaut pour indiquer aucun indicateur), soit une ou plusieurs valeurs d’indicateurs racine prédéfinies, connectées via l’opérateur OR '|'. Les valeurs d’indicateur racine autorisées sont définies par D3D12_ROOT_SIGNATURE_FLAGS.

Exemple :

RootFlags(0) // default value – no flags
RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT)
RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | DENY_VERTEX_SHADER_ROOT_ACCESS)

Constantes racines

La clause RootConstants spécifie les constantes racines dans la signature racine. Deux paramètres obligatoires sont : num32BitConstants et bReg (le registre correspondant à BaseShaderRegister dans les API C++) du cbuffer. Les paramètres d’espace (RegisterSpace dans les API C++) et de visibilité (ShaderVisibility en C++) sont facultatifs, et les valeurs par défaut sont les suivantes :

RootConstants(num32BitConstants=N, bReg [, space=0, 
              visibility=SHADER_VISIBILITY_ALL ])

Exemple :

RootConstants(num32BitConstants=3, b3)

Visibilité

La visibilité est un paramètre facultatif qui peut avoir l’une des valeurs de D3D12_SHADER_VISIBILITY.

SHADER_VISIBILITY_ALL diffuse les arguments racine à tous les nuanceurs. Sur certains matériels, cela n’a aucun coût, mais sur d’autres matériels, il y a un coût de duplication des données à toutes les étapes du nuanceur. La définition de l’une des options, comme SHADER_VISIBILITY_VERTEX, limite l’argument racine à une seule étape de nuanceur.

La définition d’arguments racines sur des phases de nuanceur uniques permet d’utiliser le même nom de liaison à différentes étapes. Par exemple, une liaison SRV de t0,SHADER_VISIBILITY_VERTEX et une liaison SRV de t0,SHADER_VISIBILITY_PIXEL seraient valides. Mais si le paramètre de visibilité était t0,SHADER_VISIBILITY_ALL pour l’une des liaisons, la signature racine n’est pas valide.

CbV de niveau racine

La CBV clause (vue mémoire tampon constante) spécifie une entrée reg de la mémoire tampon constante de niveau racine b-register. Notez qu’il s’agit d’une entrée scalaire ; il n’est pas possible de spécifier une plage pour le niveau racine.

CBV(bReg [, space=0, visibility=SHADER_VISIBILITY_ALL ])    //   Version 1.0
CBV(bReg [, space=0, visibility=SHADER_VISIBILITY_ALL,      // Version 1.1
            flags=DATA_STATIC_WHILE_SET_AT_EXECUTE ])

SRV de niveau racine

La SRV clause (vue des ressources du nuanceur) spécifie une entrée reg de registre T SRV de niveau racine. Notez qu’il s’agit d’une entrée scalaire ; il n’est pas possible de spécifier une plage pour le niveau racine.

SRV(tReg [, space=0, visibility=SHADER_VISIBILITY_ALL ])    //   Version 1.0
SRV(tReg [, space=0, visibility=SHADER_VISIBILITY_ALL,      // Version 1.1
            flags=DATA_STATIC_WHILE_SET_AT_EXECUTE ])

UAV de niveau racine

La UAV clause (vue d’accès non triée) spécifie une entrée u-register de niveau racine uAV. Notez qu’il s’agit d’une entrée scalaire ; il n’est pas possible de spécifier une plage pour le niveau racine.

UAV(uReg [, space=0, visibility=SHADER_VISIBILITY_ALL ])    //   Version 1.0
UAV(uReg [, space=0, visibility=SHADER_VISIBILITY_ALL,      // Version 1.1
            flags=DATA_VOLATILE ])

Exemple :

UAV(u3)

Table de descripteur

La DescriptorTable clause est elle-même une liste de clauses de table de descripteurs séparées par des virgules, ainsi qu’un paramètre de visibilité facultatif. Les clauses DescriptorTable incluent CBV, SRV, UAV et Sampler. Notez que leurs paramètres diffèrent de ceux des clauses de niveau racine.

DescriptorTable( DTClause1, [ DTClause2, … DTClauseN,
                 visibility=SHADER_VISIBILITY_ALL ] )

La table CBV de descripteur a la syntaxe suivante :

CBV(bReg [, numDescriptors=1, space=0, offset=DESCRIPTOR_RANGE_OFFSET_APPEND ])   // Version 1.0
CBV(bReg [, numDescriptors=1, space=0, offset=DESCRIPTOR_RANGE_OFFSET_APPEND      // Version 1.1
          , flags=DATA_STATIC_WHILE_SET_AT_EXECUTE ])

Exemple :

DescriptorTable(CBV(b0),SRV(t3, numDescriptors=unbounded))

Le paramètre obligatoire bReg spécifie le reg de début de la plage cbuffer. Le paramètre numDescriptors spécifie le nombre de descripteurs dans la plage cbuffer contiguë ; la valeur par défaut étant 1. L’entrée déclare une plage [Reg, Reg + numDescriptors - 1] cbuffer, lorsque numDescriptors est un nombre. Si numDescriptors est égal à « unbounded », la plage est [Reg, UINT_MAX], ce qui signifie que l’application doit s’assurer qu’elle ne référence pas une zone hors limites. Le champ offset représente le paramètre OffsetInDescriptorsFromTableStart dans les API C++, c’est-à-dire le décalage (en descripteurs) du début de la table. Si le décalage est défini sur DESCRIPTOR_RANGE_OFFSET_APPEND (valeur par défaut), cela signifie que la plage se trouve directement après la plage précédente. Toutefois, la saisie de décalages spécifiques permet aux plages de se chevaucher, ce qui autorise l’alias de registre.

La table SRV de descripteur a la syntaxe suivante :

SRV(tReg [, numDescriptors=1, space=0, offset=DESCRIPTOR_RANGE_OFFSET_APPEND ])    // Version 1.0
SRV(tReg [, numDescriptors=1, space=0, offset=DESCRIPTOR_RANGE_OFFSET_APPEND,      // Version 1.1
            flags=DATA_STATIC_WHILE_SET_AT_EXECUTE ])

Ceci est similaire à l’entrée de la table CBV de descripteur, sauf que la plage spécifiée est pour les vues de ressources du nuanceur.

La table UAV de descripteur a la syntaxe suivante :

UAV(uReg [, numDescriptors=1, space=0, offset=DESCRIPTOR_RANGE_OFFSET_APPEND ])    // Version 1.0
UAV(uReg [, numDescriptors=1, space=0, offset=DESCRIPTOR_RANGE_OFFSET_APPEND,      // Version 1.1
            flags=DATA_VOLATILE ])

Cela est similaire à l’entrée de la table CBV de descripteur, sauf que la plage spécifiée concerne les vues d’accès non triées.

La table Sampler de descripteur a la syntaxe suivante :

Sampler(sReg [, numDescriptors=1, space=0, offset=DESCRIPTOR_RANGE_OFFSET_APPEND ])  // Version 1.0
Sampler(sReg [, numDescriptors=1, space=0, offset=DESCRIPTOR_RANGE_OFFSET_APPEND,    // Version 1.1
                flags=0 ])

Ceci est similaire à l’entrée de la table CBV de descripteur, sauf que la plage spécifiée est pour les échantillonneurs de nuanceur. Notez que les samplers ne peuvent pas être mélangés avec d’autres types de descripteurs dans la même table de descripteur (car ils se trouvent dans un tas de descripteur distinct).

Échantillonneur statique

L’échantillonneur statique représente la structure D3D12_STATIC_SAMPLER_DESC . Le paramètre obligatoire pour StaticSampler est un reg scalaire d’échantillonnage s-register. D’autres paramètres sont facultatifs avec les valeurs par défaut indiquées ci-dessous. La plupart des champs acceptent un ensemble d’énumérations prédéfinies.

StaticSampler( sReg,
              [ filter = FILTER_ANISOTROPIC, 
                addressU = TEXTURE_ADDRESS_WRAP,
                addressV = TEXTURE_ADDRESS_WRAP,
                addressW = TEXTURE_ADDRESS_WRAP,
                mipLODBias = 0.f,
                maxAnisotropy = 16,
                comparisonFunc = COMPARISON_LESS_EQUAL,
                borderColor = STATIC_BORDER_COLOR_OPAQUE_WHITE,
                minLOD = 0.f,         
                maxLOD = 3.402823466e+38f,
                space = 0, 
                visibility = SHADER_VISIBILITY_ALL ])

Exemple :

StaticSampler(s4, filter=FILTER_MIN_MAG_MIP_LINEAR)

Les options de paramètre sont très similaires aux appels d’API C++, à l’exception de borderColor, qui est limité à une énumération dans HLSL.

Le champ de filtre peut être l’un des D3D12_FILTER.

Les champs d’adresse peuvent être chacun de D3D12_TEXTURE_ADDRESS_MODE.

La fonction de comparaison peut être l’une des D3D12_COMPARISON_FUNC.

Le champ de couleur de bordure peut être l’un des D3D12_STATIC_BORDER_COLOR.

La visibilité peut être l’une des D3D12_SHADER_VISIBILITY.

Compilation d’une signature racine HLSL

Il existe deux mécanismes pour compiler une signature racine HLSL. Tout d’abord, il est possible d’attacher une chaîne de signature racine à un nuanceur particulier via l’attribut RootSignature (dans l’exemple suivant, à l’aide du point d’entrée MyRS1 ) :

[RootSignature(MyRS1)]
float4 main(float4 coord : COORD) : SV_Target
{
…
}

Le compilateur crée et vérifie l’objet blob de signature racine pour le nuanceur et l’incorpore à côté du code d’octet du nuanceur dans l’objet blob du nuanceur. Le compilateur prend en charge la syntaxe de signature racine pour le modèle de nuanceur 5.0 et versions ultérieures. Si une signature racine est incorporée dans un nuanceur de modèle 5.0 et que ce nuanceur est envoyé au runtime D3D11, par opposition à D3D12, la partie signature racine est ignorée en mode silencieux par D3D11.

L’autre mécanisme consiste à créer un objet blob de signature racine autonome, peut-être à réutiliser avec un grand ensemble de nuanceurs, ce qui permet d’économiser de l’espace. L’outil fxC (Effect-Compiler Tool) prend en charge les modèles de nuanceur rootsig_1_0 etrootsig_1_1. Le nom de la chaîne de définition est spécifié via l’argument /E habituel. Exemple :

fxc.exe /T rootsig_1_1 MyRS1.hlsl /E MyRS1 /Fo MyRS1.fxo

Notez que la définition de la chaîne de signature racine peut également être transmise sur la ligne de commande, par exemple,/D MyRS1= »....

Manipulation des signatures racines avec le compilateur FXC

Le compilateur FXC crée un byte-code de nuanceur à partir de fichiers sources HLSL. Il existe de nombreux paramètres facultatifs pour ce compilateur. Reportez-vous à l’outil Effect-Compiler Tool.

Pour la gestion des signatures racines créées par HLSL, le tableau suivant fournit quelques exemples d’utilisation de FXC.

Courbes Ligne de commande Description
1 fxc /T ps_5_1 shaderWithRootSig.hlsl /Fo rs1.fxo Compile un nuanceur pour la cible du nuanceur de pixels 5.1. La source de nuanceur se trouve dans le fichier shaderWithRootSig.hlsl, qui comprend une signature racine. Le nuanceur et la signature racine sont compilés en tant qu’objets blob distincts dans le fichier binaire rs1.fxo.
2 fxc /dumpbin rs1.fxo /extractrootsignature /Fo rs1.rs.fxo Extrait la signature racine du fichier créé à la ligne 1, de sorte que le fichier rs1.rs.fxo ne contient qu’une signature racine.
3 fxc /dumpbin rs1.fxo /Qstrip_rootsignature /Fo rs1.stripped.fxo Supprime la signature racine du fichier créé par la ligne 1, de sorte que le fichier rs1.stripped.fxo contient un nuanceur sans signature racine.
4 fxc /dumpbin rs1.stripped.fxo /setrootsignature rs1.rs.fxo /Fo rs1.new.fxo Combine un nuanceur et une signature racine qui se trouvent dans des fichiers distincts dans un fichier binaire contenant les deux objets blob. Dans cet exemple, rs1.new.fx0 est identique à rs1.fx0 à la ligne 1.
5 fxc /T rootsig_1_0 rootSigAndMaybeShaderInHereToo.hlsl /E RS1 /Fo rs2.fxo Crée un fichier binaire de signature racine autonome à partir d’une source qui peut contenir plus qu’une simple signature racine. Notez le rootsig_1_0 cible et que RS1 est le nom de la chaîne macro de signature racine (#define) dans le fichier HLSL.

 

Les fonctionnalités disponibles via FXC sont également disponibles par programme à l’aide de la fonction D3DCompile . Cet appel compile un nuanceur avec une signature racine ou une signature racine autonome (définition de la cible rootsig_1_0). D3DGetBlobPart et D3DSetBlobPart peuvent extraire et attacher des signatures racines à un objet blob existant.  D3D_BLOB_ROOT_SIGNATURE est utilisé pour spécifier le type de partie d’objet blob de signature racine. D3DStripShader supprime la signature racine (à l’aide de l’indicateur D3DCOMPILER_STRIP_ROOT_SIGNATURE) de l’objet blob.

Notes

Notes

Alors que la compilation hors connexion des nuanceurs est fortement recommandée, si les nuanceurs doivent être compilés au moment de l’exécution, reportez-vous aux remarques relatives à D3DCompile2.

 

Notes

Les ressources HLSL existantes n’ont pas besoin d’être modifiées pour gérer les signatures racines à utiliser avec elles.

 

Indexation dynamique à l’aide de HLSL 5.1

Fonctionnalités du nuanceur HLSL Modèle 5.1 pour Direct3D 12

Liaison de ressources

Liaison de ressources dans HLSL

Signatures racine

Modèle de nuanceur 5.1

Valeur de référence du stencil spécifié par le nuanceur

Chargements d’affichage d’accès non triés typés