Search Unity

How can I optimize my frag shader containing an if-statement within a for-loop?

Discussion in 'Shaders' started by Mufas, Mar 31, 2020.

  1. Mufas

    Mufas

    Joined:
    May 14, 2016
    Posts:
    10
    The following code needs to be optimized as it takes way too much memory, but I simply can't figure out how to do it. I'm fairly new to shaders
    Any help would be appriciated :)

    Code (CSharp):
    1. fixed4 frag (v2f i) : SV_Target
    2.             {
    3.                 fixed4 col = float4(1,1,1,1);
    4.  
    5.                 //How far equals one step
    6.                 float step = 1 / _OcclusionMapHeight;
    7.  
    8.                 //Checks all pixels below the current in the occlusion Map (_MainTex)
    9.                 [loop]          
    10.                 for(float y = 0; y < i.uv.y; y+=step)
    11.                 {
    12.                     float4 colTemp = tex2D(_MainTex, float2(i.uv.x, y));
    13.                                    
    14.                     if(colTemp.a > 0) //checks if the pixels isn't completely transparent
    15.                     {
    16.                         col = float4(0,0,0,0);
    17.                         return col;
    18.                     }
    19.                 }
    20.  
    21.                 return col;
    22.             }
    Update: Here is what the code does: I have a texture with transparent parts and none transparent parts. The shader colors every pixel from the bottom up to the first none transparent pixel white, while every pixel above those, black and transparent.
    The code does exactly what I want, except it slows the fps tremendously.

    Update 2: I changed the [unroll(1024)] to [loop], which improved the code significantly, although it is still rather heavy/slow. Thanks for the tip bgolus.
    I also changed it in the code above, for an easier read of the current code. Should I update a code block in such a manner or not? I don't really know, if that is the normal manner.
     
    Last edited: Mar 31, 2020
  2. Olmi

    Olmi

    Joined:
    Nov 29, 2012
    Posts:
    1,553
    I'm not a shader guru like some folks over here, but I bet that if-statement isn't going to entirely fix the issue if you think branching is the culprit here, as you unroll your loop to 1024... that's quite a lot.

    You could also use the built-in TexelSize variable, just make a float4 with name (TextureName)_TexelSize, and you then have it, x and y have that 1.0 / width and 1.0 / height if I remember correctly.

    I would also definitely avoid overriding the step intrinsic function with your step variable... As step is very useful, among other reasons for not doing that.

    EDIT: By the way, what are you exactly trying to do? Knowing that might help others to help you.
     
    Last edited: Mar 31, 2020
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Yeah ... that's going to explode the size of your shader. Try just removing that line?
     
  4. Mufas

    Mufas

    Joined:
    May 14, 2016
    Posts:
    10
    If I remove that line, then I get the following error:
    unable to unroll loop, loop does not appear to terminate in a timely manner (1024 iterations) at line 54 (on d3d11)
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Replace it with
    [loop]
    ?

    Honestly though, what you're doing is really, really bad and is either going to be super duper slow and make a giant shader, or even more super super duper slow but a small shader.
     
  6. Mufas

    Mufas

    Joined:
    May 14, 2016
    Posts:
    10
    Thanks for the tip, changing the line to [loop], made a signifigant difference. It changed the fps from an average of 30 to and average of 180. But it's still rather heavy, as the program without the shader has a fps of around 1000.