Dynamic Shader Linkage and ShaderReflection

Started by
7 comments, last by pulo 9 years, 10 months ago

Hey there,

i am currently trying to integrate Dynamic Shader Linkage into my ShaderReflection Code. I examined the Win32 Sample Microsoft provided, where they are declaring a cbuffer like this:


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

While cAmbientLight, cHemiAmbientLight etc. are all classes interfacing the iBaseLight interface. This is a example:


interface iBaseLight
{
   float3 IlluminateAmbient(float3 vNormal);
   
   float3 IlluminateDiffuse(float3 vNormal);


   float3 IlluminateSpecular(float3 vNormal, int specularPower );
   
};


//--------------------------------------------------------------------------------------
// Classes
//--------------------------------------------------------------------------------------
class cAmbientLight : iBaseLight
{
   float3 m_vLightColor;     
   bool     m_bEnable;
   
   float3 IlluminateAmbient(float3 vNormal);
      
   float3 IlluminateDiffuse(float3 vNormal)
   { 
      return (float3)0;
   }


   float3 IlluminateSpecular(float3 vNormal, int specularPower )
   { 
      return (float3)0;
   }
};

Now i am already able to get the needed constant buffer variables out of the reflection (m_vLightColor and m_bEnable in this case), but i was wondering if there is a reliable method to detect if the constant buffer variable is a interface class (like cAmbient Light for example).
The Reflection of this constant buffer only gives D3D_SVC_STRUCT as class type for the variables. By browing through the MSDN i notcied that there are existing two classes which would suit: D3D_SVC_INTERFACE_POINTER and D3D_SVC_INTERFACE_CLASS.

Am i doing something wrong or is it normal that the class displayed here is a STRUCT?

Advertisement

But the constant buffer is the same regardless of which interface you are using, right? I haven't ever used the interface mechanism, but I thought it was a way to swap out implementations of a 'class'. It doesn't modify the constant buffers though, does it?

Yes the constant buffer stays the same, regardless of which class implementation i am using at runtime. (Was this what you were asking?)

The abstract interface is used like this:


iBaseLight     g_abstractAmbientLighting;


struct PixelInput
{
float4 position : SV_POSITION;
float3 normals : NORMAL;
float2 tex: TEXCOORD0;
};


float4 main(PixelInput input) : SV_TARGET
{
float3 Ambient = (float3)0.0f;
Ambient = g_txDiffuse.Sample(g_samplerLin, input.tex) * g_abstractAmbientLighting.IlluminateAmbient(input.normals);


return float4(saturate(Ambient), 1.0f);
}

The reason i would like to reliably differentiate between a normal struct and a class used for dynamic shader linkage is that i would later use the variable name to automatically get the class instances like this:


 g_pPSClassLinkage->GetClassInstance( varDesc.Name, 0, &g_pAmbientLightClass );

Well i found something which might help. If i use the GetInterfaceByIndex on the Shader Reflection type i get the expected class "D3D_SVC_INTERFACE_CLASS" which i suspect would be different or non-existent on a normal struct.

EDIT: The only thing now left for me is to find out if it is possible to get the name from here through shader reflection. Any Ideas?:


iBaseLight     g_abstractAmbientLighting;
               ^^^^^^^^^^^^^^^^^^^^^^^^

struct PixelInput
{
float4 position : SV_POSITION;
float3 normals : NORMAL;
float2 tex: TEXCOORD0;
};

Congratulations on being the first person that I've met who's actually trying to use dynamic shader linkage. I actually had to read through the chapter that I wrote on this to find an answer to your question. tongue.png

So when you just have interface instances declared in global scope, you can query their info by calling ID3D11ShaderReflection::GetVariableByName. Once you have the ID3D11ShaderReflectionVariable, you can then call GetInterfaceSlot to get the index of that interface that you use to bind an ID3D11ClassInstance when binding the shader. Unfortunately there's no GetVariableByIndex, so you can't just iterate over all of the variables and find your interface. It *might* show up if you get the reflection interface for the default constant buffer, but I'd have to test to find out.

Thanks for your answer ;).


Congratulations on being the first person that I've met who's actually trying to use dynamic shader linkage

Why is that? Is it to cumbersome, or bad performance wise? I find it a quite interesting idea, especially because it gives you the possibility to create new class instances in run time.


It *might* show up if you get the reflection interface for the default constant buffer, but I'd have to test to find out

How do i get the reflection interface for the default constant buffer? I already checked most of the reflection interfaces, including the constant buffers (with GetConstantBufferByIndex()) and bound resources (with GetResourceBindingDesc()). Sadly it does not show up on either of those.

Getting the global interface name by index would be nice, because then one would not have to change the code manually if something changes inside the shader code and it would be easy to "calculate" the offset into the dynamic linkage array.


How do i get the reflection interface for the default constant buffer?

If you compile your shader with FXC, and then check the output listing for the name of the buffer you should be able to use that name to reflect the constant buffer. Regarding the performance, I think there is a penalty for using the dynamic linkage (although I've never heard hard numbers on this before). Most of the systems I have heard of just generate the appropriate shader code and compile the needed variants accordingly.

Is that something that isn't a usable solution for you?

I think Jason is right in that most people already have shader permutation systems in place, and making use of dynamic linkage would require a lot of refactoring for little gain (unless your offline build process is really bottlenecked by shader compilation). It's also a pretty awkward API like you've mentioned (interfaces and classes were a bad idea, IMO), and the fact that it's not available in any other API is probably the final nail in the coffin. I wouldn't be surprised if the drivers are full of bugs related to dynamic linkage, since those code paths don't get used very often.

As for reflecting the default constant buffer, I believe it shows up with the name '$Global' if you iterate over the constant buffers. If you checked all of them and the interface isn't in there, then I think that you're out of luck. The docs only show examples of getting an interface by name, rather than querying all interfaces and getting their info.


How do i get the reflection interface for the default constant buffer?

If you compile your shader with FXC, and then check the output listing for the name of the buffer you should be able to use that name to reflect the constant buffer. Regarding the performance, I think there is a penalty for using the dynamic linkage (although I've never heard hard numbers on this before). Most of the systems I have heard of just generate the appropriate shader code and compile the needed variants accordingly.

Is that something that isn't a usable solution for you?

I also heard about this solution, but could not find any samples or good explanation of whats the best way to go with this kind of system. Do you have any of those at hand?

As i found out about the Dynamic Shader Linkage i was not expecting it being used so rarely, but the explanation from MJP now makes it quite clear why this might be the case.

So: Does anyone have a good article or something about how one would usually go about this kind of system?

Thank you for all your input!

This topic is closed to new replies.

Advertisement