Shader classes, mixins and inheritance
Xenko Shading Language (XKSL) is an extension of HLSL, which makes it closer to C# syntax and concepts. The language is object-oriented:
- member types can be shader classesXKSL uses an original way to handle multiple inheritance. Inheritance is performed through mixins, so the order of inheritance is crucial:
- the order of inheritance defines the actual implementation of a method (the last override)
base.<method name>(<arguments>)Keywords
XKSL uses the keywords as HLSL, and adds new ones:stage: method and member keyword. This keyword makes sure the method or member is only defined once and is the same in the compositions.stream: member keyword. The member is accessible at every stage of the shader. For more information, see Automatic shader stage input/out.streams: sort of global structure storing variables needed across several stages of the shader. For more information, see Automatic shader stage input/out.override: method keyword. If this keyword is missing, the compilation returns an error.abstract: used in front of a method declaration (without a body).clone: method keyword. When a method appears several times in the inheritance tree of a shader class, this keyword forces the creation of multiple instances of the method at each level of the inheritance instead of one. For more information, see Composition.Input: for geometry and tessellation shaders. For more information, see Shader stages.Output: for geometry and tessellation shaders. For more information, see Shader stages.Input2: for tessellation shaders. For more information, see Shader stages.Constants: for tessellation shaders. For more information, see Shader stages.Abstract methods
abstractkeyword. You can inherit from a shader class with abstract methods without having to implement them; the compiler will simply produce a harmless warning. However, you should implement it in your final shader to prevent a compilation error.Annotations
Like HLSL, annotations are available in XKSL. Some of the most useful ones are:[Color]for float4 variables. The ParameterKey will have the typeColor4instead ofVector4. It also specifies to Game Studio that this variable should be treated as a color, so you can edit it in Game Studio.[Link(…)]specifies which ParameterKey to use to set this value. However, an independent default key is still created.[Map(…)]specifies which ParameterKey to use to set this value. No new ParameterKey is created.[RenameLink]prevents the creation of a ParameterKey. It should be used with[Link()].Example code: annotations
shader BaseShader{ [Color] float4 myColor; [Link("ProjectKeys.MyTextureKey")] [RenameLink] Texture2D texture; [Map("Texturing.Texture0")] Texture2D defaultTexture;};
Example code: inheritance
shader BaseInterface{ abstract float Compute();};shader BaseShader : BaseInterface{ float Compute() { return 1.0f; }};shader ShaderA : BaseShader{ override void Compute() { return 2.0f; }};shader ShaderB : BaseShader{ override void Compute() { float prevValue = base.Compute(); return (5.0f + prevValue); }};
Example code: the importance of inheritance order
ShaderAandShaderB.shader MixAB : ShaderA, ShaderB{};shader MixBA : ShaderB, ShaderA{};// Resulting code (representation)shader MixAB : BaseInterface, BaseShader, ShaderA, ShaderB{ float Compute() { // code from BaseShader float v0 = 1.0f; // code from ShaderA float v1 = 2.0f; // code from ShaderB float prevValue = v1; float v2 = 5.0f + prevValue; return v2; // = 7.0f }};shader MixBA : BaseInterface, BaseShader, ShaderA, ShaderB{ float Compute() { // code from BaseShader float v0 = 1.0f; // code from ShaderB float prevValue = v0; float v1 = 5.0f + prevValue; // code from ShaderA float v2 = 2.0f; return v2; // = 2.0f }};
Static calls
<shader_name>.<variable or method_name>. It behaves the same way as a static call. Note that if you statically call a method that uses shader class variables, the shader won’t compile. This is a convenient way to only use a part of a shader, but this isn’t an optimization. The shader compiler already automatically removes any unnecessary variables.Code example: static calls
shader StaticClass{ float StaticValue; float StaticMethod(float a) { return 2.0f * a; } // this method uses a float NonStaticMethod() { return 2.0f * StaticValue; }};// this shader class is fineshader CorrectStaticCallClass{ float Compute() { return StaticClass.StaticValue * StaticMethod(5.0f); }};// this shader class won't compile since the call is not staticshader IncorrectStaticCallClass { float Compute() { return StaticClass.NonStaticMethod(); }};// one way to fix thisshader IncorrectStaticCallClassFixed : StaticClass{ float Compute() { return NonStaticMethod(); }};
See also
- Effect language
- Shading language index
- Composition
- Templates
- Shader stage input/output automatic management
- Shader stages
