Search Unity

  1. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Unity 5.3 GLCore (4.1), if statement shader compiler bug

Discussion in 'Shaders' started by kode80, Dec 16, 2015.

  1. kode80


    Aug 1, 2013
    I just loaded my project in Unity 5.3 and am seeing a very weird issue with one of my custom shaders. I'm not sure if I'm just an idiot and not knowing something fundamental about GL 4, or if this is a bug.

    This doesn't work:
    color *= 0.1;

    This does work:
    color.r *= 0.1;
    color.g *= 0.1;
    color.b *= 0.1;
    color.a *= 0.1;
  2. Michal_


    Jan 14, 2015
    What do you mean by doesn't work? Doesn't compile? Care to post the shader? I'm sure that vector-scalar multiplication is supported in GLSL. So, probably a compiler bug. It wouldn't be the first one I saw lately.
  3. kode80


    Aug 1, 2013
    Vector multiplied by scalar in the example above results in 0,0,0,0. where as each component multiplied by the scalar returns the correct values.

    There are no compile errors or warning of any kind.

    I can't post the shader it's from unfortunately but it's a volumetric raycast shader. In Unity 5.2 my shader works as expected, in 5.3 I get blank. I started debugging and discovered the reason for blank is that after I perform the raycast, as a final step I multiply the resulting color by a scalar that's calculated based on distance of the point from the horizon. When I comment this line out, my shader draws something (still not 100% correct compared to 5.2 but at least I see something).

    So from there I played around with multiple variations of the final line and discovered that if I do the same calculation but on individual components (vs the whole vector) it works as expected. If I skip the whole raycast step, set the color to red, I still get the same weird behavior - i.e. vector multiplied by scalar constantly returns 0, while multiplying each component separately works as expected.

    I also tried setting the OSX renderer explicitly to OpenGL 2, and it works just like it did in Unity 5.2, so it's clearly something about the new GL 4.1 OSX render path in Unity 5.3.
  4. Michal_


    Jan 14, 2015
    I see. Looks like a bug in Unity or possibly a driver bug. I tried to reproduce it but couldn't. It works as expected for me in Unity 5.3.0f4 on Mac. I created new shader in Unity and just multiplied resulting color by a constant. No problem there.

    Unity bug reporter is your friend if you have a repro.

    Edit: I assume you're writing shaders in HLSL(Cg) and let Unity cross-compile them to GLSL.
    Last edited: Dec 17, 2015
  5. Farfarer


    Aug 17, 2010
    It might be that GL4 is more strict (like DX11). So you'd have to do
    Code (csharp):
    1. color *= vec4(0.1);

    Could be it's not picking up on those things yet.
  6. Michal_


    Jan 14, 2015
    It is true that GLSL is generally more strict than HLSL when it comes to implicit conversions. But I'm sure both support vector-scalar multiplication. For example

    Code (CSharp):
    1. // GLSL
    2. vec4 a = 1.0; // error
    3. vec4 a = vec4(1.0); // ok
    4. a *= 2; // error (implicit int->float cast isn't supported)
    5. a *= 2.0; // ok
    7. // HLSL
    8. float4 a = 1.0; // ok
    9. float4 a = float4(1.0); // ok
    10. a *= 2; // ok
    11. a *= 2.0; // ok
  7. kode80


    Aug 1, 2013
    This was my fear, that it's something else in my shader causing the issue and this is just the very weird side effect. But with no errors/warnings to go on and multiple complex passes to dig through, who the hell knows what's the root cause. It's got to be a Unity bug - even if the actual bug is that some error messages aren't getting reported correctly.

    I'll continue investigating today.

    Yeah, this was one of the things I tried funny enough, no cigar. I've been switching back and forth between OSX/Win to make sure my shader works on DX11, so I've had to deal with some of these differences already (constructors requiring all arguments in DX, while GL is/was more lax about it etc.). My shader works correctly under DX11.
  8. kode80


    Aug 1, 2013
    So far today, I've managed to narrow it down to an issue with HDR. It seems that a bunch of my values are getting super blown out but I can't for the life of me understand why. If I remove the ambient calculation from my shader, I end up with nothing on screen. If I set the rgb components of the color to something known (like pure red) at the end, still nothing - however if I set them to their alpha channel (to draw alpha as grayscale) I see what appears to be correct alpha. So weird.

    And of course now MonoDevelop has crashed, and refuses to open. Unity 5.3 :mad:
  9. kode80


    Aug 1, 2013
    Ok, so now I'm getting somewhere... the entire gamut of problems I'm seeing in 5.3 is caused by a single conditional within my raycast loop. If I remove this conditional, literally everything works - the output looks identical to 5.2.

    The conditional itself just checks if the particle value I retrieve from a 3D texture is above a certain threshold, if it is then I do a bunch of lighting calculations etc. if I remove that check and instead just run all calculations every iteration, it works fine. What makes no sense about this is a) the value I'm checking against is used in all the subsequent calcs so if it was somehow wrong, I'd be seeing incorrect lighting etc. and b) if I keep the conditional in but instead of checking against that particle value I check against a hardcoded value, it works fine.

    I should also add, that there several other conditionals inside the loop and they seem to have no impact on this weirdness. I thought maybe the amount of branching was somehow causing issues in GL 4.1 but no, removing those branches while keeping the problematic one has no impact - I see the same problem, yet removing the problem conditional while leaving all others intact works. So it's definitely something about that one conditional that's causing the entire shader to fail in spectacular/bizarre ways.

    I'll list the loop below in semi-pseudo code so you can see what I'm talking about.

    Code (CSharp):
    1. for( iterations)
    2. {
    3.     float value = SampleCloud();  // samples a 3D texture
    5.     // the following conditional is the problem,
    6.     // if I remove it (just always do the work) the results
    7.     // are the same as in Unity 5.2
    8.     if( value > 0.0)
    9.     {
    10.         // other conditionals for raycast...
    12.         // volume lighting calculations...
    13.     }
    14. }
  10. Michal_


    Jan 14, 2015
    Weird. It is a shame it is happening on a platform without half decent graphics debugger. Or at least I don't know one. Have you tried to look at the resulting GLSL code? You know, clicking on the "Compile and show code" button. You should see some difference if you compare glsl from 5.2 to glsl from 5.3. I mean that's as low level as you can get with OpenGL. There's only compiler in driver after that.

    You could also try to put [branch] attribute in front of that condition to tell the compiler you really mean it. On the other hand, it is probably stripped by cross-compiler/optimizer.
  11. kode80


    Aug 1, 2013
    Yeah gfx debugging on OSX is painful. I will say, the frame debugger in 5.3 is super stable on OSX - which is a huge upgrade from prior versions (where it would crash Unity 95% of the time).

    Thanks for the suggestion, I tried [branch] but it didn't help. I also tried [flatten], which unsurprisingly works, clearly it's that conditional that's causing the problem somehow.

    Comparing compiled shaders is next on the list. :(
  12. gustavolsson


    Jan 14, 2011
    I have the same problem, a branch inside a for-loop within my raymarching shader. The for-loop is located inside a (non-inlined) function and the branch returns (the function) if evaluated to true, but I don't know if this matters yet.

    For me Unity hangs and my Mac locks up if I don't quit (CMD-Q) quickly enough. I have to force restart it so it takes ages to debug the issue!

    UPDATE: Seems to work for me if, instead of returning true/false inside the for-loop, I wait with the return to the end of the function and just use a break to stop the loop.
    Last edited: Dec 19, 2015
  13. kode80


    Aug 1, 2013
    So I compared GL2 compiled output to GLCore (4.1) compiled output and it definitely looks like something's going wrong on the compiler's side. It's hard to say for certain since GLCore output is even more obfuscated that GL2, but I'm able to parse enough to see some oddities. Making minor changes on the HLSL side and seeing how it effects the compiled output definitely shows some pretty major differences, particularly around the conditionals.

    On another note, it seems that a lot of builtin GLSL functions are skipped in GLCore in favor of inline code; normalize & smoothstep being two I use a lot in my shader. I'm not sure if these are intentional optimizations or not.

    I've mentioned it to a couple of Unity devs on Twitter and am going to submit a bug report. For now I'm sticking with GL2 on OSX, which is a shame.

    This is probably due to for loops being compiled down to while loops, with conditionals that break out of the loop. So if something's going wrong on the compilation side, you'll potentially end up with an infinite loop.
  14. Michal_


    Jan 14, 2015
    5 years after gl 4.1 was introduced and still can't use it on Mac, huh? At least you have a chance this will be sorted out in foreseeable future. We're waiting for 4.3 to port our game to Mac. They'll sooner replace it with Metal for better or worse. Anyway, let us know how it turned out.
  15. cician


    Dec 10, 2012
    Just so you know, you're not alone. I think I stumbled upon the same bug (reported, #756217). The compiler indeed fails compiling branches in some situations.

    Here's my dumbed down shader that reproduces the problem. In the compiled shader the totalLight and totalWeights never get assigned to. Replacing the uniforms with constants fixes the problem, but I guess it's more related to the number of temporary registers used rather than anything else.

    Code (CSharp):
    1. Shader "Hidden/IndexedTSD/Repro" {
    2.     Properties {
    3.         _MainTex ("", 2D) = "" {}
    5.         _PSSProfileHigh_weighths1_var1 ("_PSSProfileHigh_weighths1_var1", Vector) = (0.234, 0.562, 0.644, 0.006)
    6.         _PSSProfileHigh_weighths2_var2 ("_PSSProfileHigh_weighths2_var2", Vector) = (0.101, 0.415, 0.341, 0.048)
    7.         _PSSProfileHigh_weighths3_var3 ("_PSSProfileHigh_weighths3_var3", Vector) = (0.113, 0.009, 0.007, 0.187)
    8.         _PSSProfileHigh_weighths4_var4 ("_PSSProfileHigh_weighths4_var4", Vector) = (0.113, 0.009, 0.007, 0.567)
    9.         _PSSProfileHigh_weighths5_var5 ("_PSSProfileHigh_weighths5_var5", Vector) = (0.359, 0.005, 0.000, 1.990)
    10.         _PSSProfileHigh_weighths6_var6 ("_PSSProfileHigh_weighths6_var6", Vector) = (0.078, 0.000, 0.000, 7.410)
    11.     }
    12.     SubShader {
    13.         Tags { "RenderType"="Opaque" }
    14.         LOD 200
    16.         Pass {      
    17.             Tags { "LightMode"="Always"}
    18.             ZTest Always
    19.             Cull Off
    20.             ZWrite Off
    21.             Blend Off
    23.             CGPROGRAM
    24.                 #pragma vertex vert
    25.                 #pragma fragment frag
    26.                 #pragma target 3.0
    27.                 #pragma glsl
    29.                 #include "UnityCG.cginc"
    31.                 sampler2D _MainTex;
    32.                 float4 _MainTex_TexelSize;
    34.                 struct v2f {
    35.                     float4 pos : SV_POSITION;
    36.                     float2 uv : TEXCOORD1;
    37.                 };
    39.                 v2f vert(appdata_full v) {
    40.                     v2f o;
    42.                     UNITY_INITIALIZE_OUTPUT(v2f,o);
    44.                     o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    46.                     o.uv.xy = v.texcoord.xy;
    48.                     return o;
    49.                 }
    51.                 float4 _PSSProfileHigh_weighths1_var1;
    52.                 float4 _PSSProfileHigh_weighths2_var2;
    53.                 float4 _PSSProfileHigh_weighths3_var3;
    54.                 float4 _PSSProfileHigh_weighths4_var4;
    55.                 float4 _PSSProfileHigh_weighths5_var5;
    56.                 float4 _PSSProfileHigh_weighths6_var6;
    58.                 #define PSS_PROFILE_WEIGHTS_1
    59.                 #define PSS_PROFILE_WEIGHTS_2
    60.                 #define PSS_PROFILE_WEIGHTS_3
    61.                 #define PSS_PROFILE_WEIGHTS_4
    62.                 #define PSS_PROFILE_WEIGHTS_5
    63.                 #define PSS_PROFILE_WEIGHTS_6
    65.                 #define PSS_PROFILE_VARIANCE_1 _PSSProfileHigh_weighths1_var1.w
    66.                 #define PSS_PROFILE_VARIANCE_2 _PSSProfileHigh_weighths2_var2.w
    67.                 #define PSS_PROFILE_VARIANCE_3 _PSSProfileHigh_weighths3_var3.w
    68.                 #define PSS_PROFILE_VARIANCE_4 _PSSProfileHigh_weighths4_var4.w
    69.                 #define PSS_PROFILE_VARIANCE_5 _PSSProfileHigh_weighths5_var5.w
    70.                 #define PSS_PROFILE_VARIANCE_6 _PSSProfileHigh_weighths6_var6.w
    72.                 float Gaussian (float v, float r) {
    73.                     return 1.0 / sqrt(2.0 * UNITY_PI * v) * exp(-(r * r) / (2.0 * v));
    74.                 }
    76.                 float3 Scatter(float r) {
    77.                     return
    78.                           Gaussian(PSS_PROFILE_VARIANCE_1 * 1.414, r) * PSS_PROFILE_WEIGHTS_1.rgb
    79.                         + Gaussian(PSS_PROFILE_VARIANCE_2 * 1.414, r) * PSS_PROFILE_WEIGHTS_2.rgb
    80.                         + Gaussian(PSS_PROFILE_VARIANCE_3 * 1.414, r) * PSS_PROFILE_WEIGHTS_3.rgb
    81.                         + Gaussian(PSS_PROFILE_VARIANCE_4 * 1.414, r) * PSS_PROFILE_WEIGHTS_4.rgb
    82.                         + Gaussian(PSS_PROFILE_VARIANCE_5 * 1.414, r) * PSS_PROFILE_WEIGHTS_5.rgb
    83.                         + Gaussian(PSS_PROFILE_VARIANCE_6 * 1.414, r) * PSS_PROFILE_WEIGHTS_6.rgb;
    84.                 }
    87.                 float4 frag(v2f i) : COLOR {
    88.                     float3 totalWeights = 0;
    89.                     float3 totalLight = 0;
    91.                     float x = i.uv.x;
    92.                     float y = i.uv.y;
    96. //                     float pixelSize = _MainTex_TexelSize.x;
    97.                     float pixelSize = 0.01;
    100.                     UNITY_LOOP
    101.                     for (float pos = 0; pos<1.0; ) {
    102.                         float4 sampleUV = float4(pos, i.uv.y, 0, 0);
    105.                         if (pos < 1.0)
    106.                         {
    107.                             float sampleDist = 0.01;
    109.                             float3 weights = Scatter(sampleDist);
    111.                             float3 sampleIrradiance = 1.0;
    113.                             totalWeights += weights;
    114.                             totalLight += sampleIrradiance * weights;
    115.                         }
    116.                         pos += pixelSize;
    117.                     }
    119.                     float3 sss = totalLight / totalWeights;
    120.                     float4 col = float4(sss, 1.0);
    121.                     return col;
    123.                 }
    124.             ENDCG
    125.         }
    126.     }
    127.     FallBack Off
    128. }