Interfaces et classes

La liaison de nuanceur dynamique utilise des interfaces et des classes de langage de nuanceur de haut niveau (HLSL) qui sont syntactiques similaires à leurs équivalents C++. Cela permet aux nuanceurs de référencer des instances d’interface abstraites au moment de la compilation et de laisser la résolution de ces instances à des classes concrètes pour l’application au moment de l’exécution.

Les sections suivantes expliquent comment configurer un nuanceur pour utiliser des interfaces et des classes et comment initialiser des instances d’interface dans le code d’application.

Déclaration d’interfaces

Une interface fonctionne de la même manière qu’une classe de base abstraite en C++. Une interface est déclarée dans un nuanceur à l’aide du mot clé d’interface et contient uniquement des déclarations de méthode. Les méthodes déclarées dans une interface seront toutes des méthodes virtuelles dans toutes les classes dérivées de l’interface. Les classes dérivées doivent implémenter toutes les méthodes déclarées dans une interface. Notez que les interfaces sont la seule façon de déclarer des méthodes virtuelles, il n’existe aucun mot clé virtuel comme dans C++, et les classes connot déclarent des méthodes virtuelles.

L’exemple de code nuanceur suivant déclare deux interfaces.

interface iBaseLight
{
   float3 IlluminateAmbient(float3 vNormal);
   float3 IlluminateDiffuse(float3 vNormal);
   float3 IlluminateSpecular(float3 vNormal, int specularPower );
};       

interface iBaseMaterial
{
   float3 GetAmbientColor(float2 vTexcoord);
   
   float3 GetDiffuseColor(float2 vTexcoord);

   int GetSpecularPower();

};
      

Déclaration de classes

Une classe se comporte de la même manière que les classes en C++. Une classe est déclarée avec le mot clé de classe et peut contenir des variables et des méthodes membres. Une classe peut hériter de zéro ou d’une classe et d’une ou plusieurs interfaces. Les classes doivent implémenter ou hériter des implémentations pour toutes les interfaces de sa chaîne d’héritage ou la classe ne peut pas être instanciée.

L’exemple de code nuanceur suivant illustre la dérivation d’une classe à partir d’une interface et d’une autre classe.

class cAmbientLight : iBaseLight
{
   float3            m_vLightColor;     
   bool     m_bEnable;
   float3 IlluminateAmbient(float3 vNormal);
   float3 IlluminateDiffuse(float3 vNormal);
   float3 IlluminateSpecular(float3 vNormal, int specularPower );
};

class cHemiAmbientLight : cAmbientLight
{
   float4   m_vGroundColor;
   float4   m_vDirUp;
   float3 IlluminateAmbient(float3 vNormal);
};        
      

Déclarations d’instance d’interface dans un nuanceur

Une instance d’interface agit comme un espace réservé pour les instances de classe qui fournissent une implémentation des méthodes de l’interface. L’utilisation d’une instance d’une interface permet au code nuanceur d’appeler une méthode sans savoir quelle implémentation de cette méthode sera appelée. Le code nuanceur déclare une ou plusieurs instances pour chaque interface qu’elle définit. Ces instances sont utilisées dans le code nuanceur de manière similaire aux pointeurs de classe de base C++.

L’exemple de code nuanceur suivant illustre la déclaration de plusieurs instances d’interface et leur utilisation dans le code du nuanceur.

// Declare interface instances
iBaseLight     g_abstractAmbientLighting;
iBaseLight     g_abstractDirectLighting;
iBaseMaterial  g_abstractMaterial;

struct PS_INPUT
{
    float4 vPosition : SV_POSITION;
    float3 vNormal   : NORMAL;
    float2 vTexcoord : TEXCOORD0;
};

float4 PSMain( PS_INPUT Input ) : SV_TARGET
{ 
    float3 Ambient = (float3)0.0f;       
    Ambient = g_abstractMaterial.GetAmbientColor( Input.vTexcoord ) *         
        g_abstractAmbientLighting.IlluminateAmbient( Input.vNormal );

    float3 Diffuse = (float3)0.0f;  
    Diffuse += g_abstractMaterial.GetDiffuseColor( Input.vTexcoord ) * 
        g_abstractDirectLighting.IlluminateDiffuse( Input.vNormal );

    float3 Specular = (float3)0.0f;   
    Specular += g_abstractDirectLighting.IlluminateSpecular( Input.vNormal, 
        g_abstractMaterial.GetSpecularPower() );
     
    float3 Lighting = saturate( Ambient + Diffuse + Specular );
     
    return float4(Lighting,1.0f); 
}

Déclarations d’instance de classe dans un nuanceur

Chaque classe qui sera utilisée à la place d’une instance d’interface doit être déclarée comme variable dans une mémoire tampon constante ou créée par l’application au moment de l’exécution à l’aide de la méthode ID3D11ClassLinkage::CreateClassInstance . Les instances d’interface seront signalées aux instances de classe dans le code de l’application. Les instances de classe peuvent être référencées dans le code nuanceur comme n’importe quelle autre variable, mais une classe dérivée d’une interface ne sera généralement utilisée qu’avec une instance d’interface et ne sera pas référencée directement par du code nuanceur.

L’exemple de code nuanceur suivant illustre la déclaration de plusieurs instances de classe.

cbuffer cbPerFrame : register( b0 )
{
   cAmbientLight     g_ambientLight;
   cHemiAmbientLight g_hemiAmbientLight;
   cDirectionalLight g_directionalLight;
   cEnvironmentLight g_environmentLight;
   float4            g_vEyeDir;   
};        
      

Initialisation d’instances d’interface dans une application

Les instances d’interface sont initialisées dans le code de l’application en passant un tableau de liaison dynamique contenant des attributions d’interface à l’une des méthodes ID3D11DeviceContext SetShader.

Pour créer un tableau de liaisons dynamiques, procédez comme suit

  1. Créez un objet de liaison de classe à l’aide de CreateClassLinkage.

    ID3D11ClassLinkage* g_pPSClassLinkage = NULL;            
    pd3dDevice->CreateClassLinkage( &g_pPSClassLinkage );
    
    
  2. Créez le nuanceur qui utilisera la liaison de classes dynamiques, en passant l’objet de liaison de classe en tant que paramètre à la fonction de création du nuanceur.

    pd3dDevice->CreatePixelShader( pPixelShaderBuffer->GetBufferPointer(),
        pPixelShaderBuffer->GetBufferSize(), g_pPSClassLinkage, &g_pPixelShader ) );            
    
    
  3. Créez un objet ID3D11ShaderReflection à l’aide de la fonction D3DReflect .

    ID3D11ShaderReflection* pReflector = NULL; 
    D3DReflect( pPixelShaderBuffer->GetBufferPointer(),                  
        pPixelShaderBuffer->GetBufferSize(), 
        IID_ID3D11ShaderReflection, (void**) &pReflector) );            
    
    
  4. Utilisez l’objet de réflexion du nuanceur pour obtenir le nombre d’instances d’interface dans le nuanceur à l’aide de la méthode ID3D11ShaderReflection::GetNumInterfaceSlots .

    g_iNumPSInterfaces = pReflector->GetNumInterfaceSlots();             
    
    
  5. Créez un tableau suffisamment grand pour contenir le nombre d’instances d’interface dans le nuanceur.

    ID3D11ClassInstance** g_dynamicLinkageArray = NULL;            
    g_dynamicLinkageArray = 
        (ID3D11ClassInstance**) malloc( sizeof(ID3D11ClassInstance*) * g_iNumPSInterfaces );            
    
    
  6. Déterminez l’index dans le tableau qui correspond à chaque instance d’interface à l’aide d’ID3D11ShaderReflection::GetVariableByName et ID3D11ShaderReflectionVariable::GetInterfaceSlot.

    ID3D11ShaderReflectionVariable* pAmbientLightingVar = 
        pReflector->GetVariableByName("g_abstractAmbientLighting");
        g_iAmbientLightingOffset = pAmbientLightingVar->GetInterfaceSlot(0);            
    
    
  7. Obtenez une instance de classe pour chaque objet de classe dérivé d’une interface dans le nuanceur à l’aide d’ID3D11ClassLinkage::GetClassInstance.

    g_pPSClassLinkage->GetClassInstance( "g_hemiAmbientLight", 0, 
        &g_pHemiAmbientLightClass );            
    
    
  8. Définissez des instances d’interface sur des instances de classe en définissant l’entrée correspondante dans le tableau de liaisons dynamiques.

    g_dynamicLinkageArray[g_iAmbientLightingOffset] = g_pHemiAmbientLightClass;            
    
    
  9. Transmettez le tableau de liaisons dynamiques en tant que paramètre à un appel SetShader.

    pd3dImmediateContext->PSSetShader( g_pPixelShader, g_dynamicLinkageArray, g_iNumPSInterfaces );            
    
    

Liaison dynamique