Search Unity

Questions regarding #pragma multi_compile

Discussion in 'Shaders' started by wender3321, Jul 19, 2017.

  1. wender3321

    wender3321

    Joined:
    Jul 19, 2017
    Posts:
    1
    Hello,

    So I have this sprite shader which I was hoping to strip of unnecessary calculations when they are not being used. I'm working on a mobile game so only doing certain calculations on characters when necessary is crucial.

    So let's say that I have "#pragma multi_compile ON" in my code.

    1. Will I be able to toggle the #if ON calculations at runtime, or is this only for compile time?
    2. Do I actually gain any performance by doing this, if the shader has a bunch of characters with it applied to them?
    3. Does it work with material property blocks?
    4. If I have a bunch of material instances running the shader, will it compile them twice for each character?
    If multi_compile is not the way to go when it comes to stripping shader of turning on and of functions at runtime to save performance, then what would be the correct way to go about it?

    Thanks
     
  2. GearKlik

    GearKlik

    Joined:
    Sep 21, 2015
    Posts:
    58
    For starters with a toggle you should define it as:
    #pragma multi_compile __ ON

    Or even better:
    #pragma multi_compile __ myFeature

    "on" is implied by toggling it on and off. The "__" defines this as a toggle state without having to define multiple keywords (there's a global limit of 256 keywords I believe). It will compile a version with and without your feature enabled.

    Will I be able to toggle the #if ON calculations at runtime, or is this only for compile time?
    Yes* you can toggle at runtime. Unity will compile all variants then just switch variants at runtime.

    Do I actually gain any performance by doing this, if the shader has a bunch of characters with it applied to them?
    Yes since you in theory are removing a large chunk of the shader code from certain variants. BUT you will be using more memory to store all the variants so it's a trade off. Don't have to worry if only a couplf of #ifdefs but remember that multi_compile compiles ALL variants which could easily be 10s of 1000s of shaders.

    Does it work with material property blocks?
    Nope AFAIK there's no way to dynamically create/remove shaderLab properties. BUT shaderLab properties may not necessarily affect your final shader. You can include hundreds of properties and if they're not used in the final vertex/pixel output they will be ignored by the compiler. Bonus fact: the shader compiler will also try and pack float properties into float4 properties. That said you CAN use #ifdef to throw out shader global variables. All compiler directive can only be used between CGPROGRAM and ENDCG as they're HLSL directives not Unity ShaderLab. See here for more info: https://msdn.microsoft.com/en-us/library/windows/desktop/bb943993(v=vs.85).aspx

    Bonus fact:
    Go here and read about concatenation in the remarks section ;)
    https://msdn.microsoft.com/en-us/library/windows/desktop/dd607345(v=vs.85).aspx

    PS: Sorry this is HLSL info, I'm sure it's a similar story for GL platforms.

    If I have a bunch of material instances running the shader, will it compile them twice for each character?
    Not if you're using multi_compile, each shader variant will be pre-compiled.

    *You might also consider shader_feature instead of multi_compile. They work exactly the same way except one key difference:
    multi_compile will compile ALL variants.
    Pros:
    Can switch to any variant at runtime since you're guaranteed all variants will be included in your build. This would be a good option for a smaller number of variants that are commonly switched between at runtime

    Cons:
    Takes up more memory. If you have a very complicated shader you could take up hundreds of MB of video memory.

    shader_feature will only compile USED variants.
    Pros:
    Less memory. can have a lot of #ifdef's and not worry about variant count.

    Cons:
    Only variants used by materials at build time can be used at runtime. You can however manage this with ShaderVariantCollection https://docs.unity3d.com/ScriptReference/ShaderVariantCollection.html

    A good reference is to download the standard shader source and look at how it's put together.
     
    Last edited: Jul 19, 2017
    flashframe likes this.
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    To further explain this, don't use keywords to do something like:

    #if WHITE
    fixed4 color = fixed4(1,1,1,1);
    #elif BLACK
    fixed4 color = fixed4(0,0,0,1);
    #endif


    It'll be faster to just use Material.SetColor("_Color", color) for uses this simple. However if you're doing:

    #if USE_SPECULAR
    // complex specular lighting calculations
    #end


    This is a good use case, and is actually one Unity uses for forward rendering.


    Also, in case @GearKlik 's comments on shader_feature are confusing, yes you can switch between shader_feature keywords at runtime like you can with multi_compile. They're just not guaranteed to work in standalone builds unless you ensure the variant you're enabling was referenced so the build process knew to compile it and include it with the build. That's what the ShaderVariantCollection is for as you can run the game in the editor and you can use them track what variants are being used even if there aren't any existing material assets with the setting explicitly enabled.
     
    flashframe likes this.