Composition

    In addition to the inheritance system, XKSL introduces the concept of composition. A composition is a member whose type is another shader class. It's defined the same way as variables.

    You can compose with an instance of the desired shader class or an instance of a shader class that inherits from the desired one.

    The compositions are compiled in their own context, meaning that the non-stage variables are only accessible within the composition. It's also possible to have compositions inside compositions.

    Example code: access root context

    1. {
    2. BaseShader rootShader = stage;
    3. float4 GetColor()
    4. {
    5. return rootShader.GetColor();
    6. }
    7. };

    This is error-prone, since CompositionShaderC expects BaseShader to be available in the root context.

    You can also create an array of compositions the same way you use an array of values. Since there's no way to know beforehand how many compositions there are, you should iterate using a foreach statement.

    Example code: stage behavior

    The behavior of the stage keyword is straightforward: only one instance of the variable or method is produced.

    1. shader BaseShader
    2. {
    3. stage float BaseStageValue;
    4. float NonStageValue;
    5. };
    6. shader TestShader : BaseShader
    7. {
    8. BaseShader comp0;
    9. BaseShader comp1;
    10. };
    11. // resulting shader (representation)
    12. shader TestShader
    13. {
    14. float BaseStageValue;
    15. float NonStageValue;
    16. float comp0_NonStageValue;

    This behavior is useful when you need a value in multiple composition but you only need to compute it once (eg the normal in view space).

    The clone keyword has a less trivial behavior. It prevents the stage keyword to produce a unique method.

    1. shader BaseShader
    2. {
    3. stage float BaseStageMethod()
    4. {
    5. return 1.0;
    6. }
    7. stage float BaseStageMethodNotCloned()
    8. {
    9. return 1.0;
    10. }
    11. };
    12. shader CompShader : BaseShader
    13. {
    14. override clone float BaseStageMethod()
    15. {
    16. return 1.0 + base.BaseStageMethod();
    17. }
    18. override float BaseStageMethodNotCloned()
    19. {
    20. return 1.0f + base.BaseStageMethodNotCloned();
    21. }
    22. };
    23. shader TestShader : BaseShader
    24. {
    25. CompShader comp0;
    26. };
    27. // resulting shader (representation)
    28. {
    29. // cloned method
    30. float base_BaseStageMethod()
    31. {
    32. return 1.0;
    33. }
    34. float comp0_BaseStageMethod()
    35. {
    36. return 1.0 + base_BaseStageMethod();
    37. }
    38. float BaseStageMethod() // in fact comp1_BaseStageMethod
    39. {
    40. return 1.0 + comp0_BaseStageMethod; // 3.0f
    41. }
    42. // not cloned method
    43. float base_BaseStageMethodNotCloned()
    44. {
    45. return 1.0f;
    46. }
    47. float BaseStageMethodNotCloned()
    48. {
    49. return 1.0f + base_BaseStageMethodNotCloned(); // 2.0f
    50. }

    This behavior is useful when you want to repeat a simple function but with different parameters (eg adding color on top of another).

    See also