Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Help with GearVR Fragment shader

Discussion in 'Shaders' started by TerabyteTim, Sep 14, 2017.

  1. TerabyteTim

    TerabyteTim

    Joined:
    Oct 20, 2011
    Posts:
    115
    I'm attempting to develop a shader for a GearVR app we are doing, and I've hit a bit of a wall trying to optimize it.
    I have never done mobile shader development before, and it appears our shaders are taking up a lot of GPU time.

    We only use a handful of shaders, and they're all variants of the same base shader, which is why I suspect it is an issue with one of these shaders causing the performance loss.

    I ran some diagnostics and this is what I found:
    - Roughly 85% of our shader time is spent in the fragment shader
    - About 1/3rd of the time our textures are not hitting the cache
    - About 1/3rd of the time our texture goes to higher mipmap levels
    - Sometimes there are vertex fetch stalls and waiting on system memory.

    This is one of the more complex shaders we use, it uses a texture, allows lightmapping and Unity fog, applies a caustic effect, and has sliders for saturation and contrast effects.
    I would greatly appreciate any help or insights the community can provide. If anyone notices any obvious mistakes I'm making or ways in which I could optimize my calculations it would be greatly appreciated!

    Code (CSharp):
    1.  
    2. //Supports lightmap, saturation, contrast, unity fog.
    3. Shader "Underwater/Mobile Fast/Lightmap/Saturation Contrast"
    4. {
    5.     Properties
    6.     {
    7.         _MainTex("Main Texture", 2D) = "white" {}
    8.         _Saturation("Saturation", Range(0, 1)) = 1.0
    9.         _Contrast("Contrast", Range(0, 1)) = 0.0
    10.         [Header(Caustics)]
    11.         _Caustics("Caustics Texture (RGBA)", 2D) = "white" {}
    12.         _CausticsCoord("Tiling(XY) Offset(ZW) - Overrides texture coords", Vector) = (0.5,0.5,0,0)
    13.         _CausticsSpeed("Speed", Float) = 1
    14.         _CausticsBoost("Boost", Range(0,1)) = 0
    15.         _CausticsIntensity0("Intensity A", Range(0, 1)) = 1
    16.         _CausticsIntensity1("Intensity B", Range(0, 1)) = 0
    17.         _CausticsPosition0("Position A (World Y)", Float) = 2
    18.         _CausticsPosition1("Position B (World Y)", Float) = 4
    19.     }
    20.  
    21.     SubShader
    22.     {
    23.         Tags{ "RenderType" = "Opaque" "Queue" = "Geometry" }
    24.         LOD 100
    25.  
    26.         Pass
    27.         {
    28.             Tags{ "LightMode" = "ForwardBase" }
    29.  
    30.             CGPROGRAM
    31.  
    32.             #pragma target 3.0
    33.             #pragma vertex vert
    34.             #pragma fragment frag
    35.             #pragma multi_compile_fog
    36.             #include "UnityCG.cginc"
    37.  
    38.             uniform sampler2D _MainTex;
    39.             uniform fixed _Saturation;
    40.             uniform fixed _Contrast;
    41.             uniform sampler2D _Caustics;
    42.             uniform float4 _CausticsCoord;
    43.             uniform fixed _CausticsSpeed;
    44.             uniform fixed _CausticsBoost;
    45.             uniform fixed _CausticsIntensity0;
    46.             uniform fixed _CausticsIntensity1;
    47.             uniform fixed _CausticsPosition0;
    48.             uniform fixed _CausticsPosition1;
    49.  
    50.             uniform half4 _MainTex_ST;
    51.             uniform half _UVSec;
    52.             uniform float4 _DetailAlbedoMap_ST;
    53.  
    54.             struct vertexInput
    55.             {
    56.                 float4 vertex : POSITION;
    57.                 float3 normal : NORMAL;
    58.                 float4 texcoord : TEXCOORD0;
    59.                 float4 texcoord1 : TEXCOORD1;
    60.             };
    61.  
    62.             struct vertexOutput
    63.             {
    64.                 float4 position : SV_POSITION;
    65.                 float4 mainTexCoord : TEXCOORD0;
    66.                 float4 worldSpacePos : TEXCOORD1;
    67.                 float4 lightmapTexCoord : TEXCOORD2;
    68.                 float3 worldNormal : TEXCOORD3;
    69.                 UNITY_FOG_COORDS(4)
    70.                 UNITY_VERTEX_OUTPUT_STEREO
    71.             };
    72.  
    73.             //This function fixes issues with single-pass stereo rendering
    74.             inline float4 TexCoords(vertexInput v)
    75.             {
    76.                 float4 texcoord;
    77.                 texcoord.xy = TRANSFORM_TEX(v.texcoord, _MainTex); // Always source from uv0
    78.                 texcoord.zw = TRANSFORM_TEX(((_UVSec == 0) ? v.texcoord : v.texcoord1), _DetailAlbedoMap);
    79.                 return texcoord;
    80.             }
    81.  
    82.             vertexOutput vert(vertexInput v)
    83.             {
    84.                 vertexOutput o;
    85.  
    86.                 UNITY_INITIALIZE_OUTPUT(vertexOutput, o);
    87.                 UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    88.  
    89.                 o.worldSpacePos = mul(unity_ObjectToWorld, v.vertex);
    90.                 o.worldNormal = UnityObjectToWorldNormal(v.normal);
    91.  
    92.                 o.position = UnityObjectToClipPos(v.vertex);
    93.                 o.mainTexCoord = TexCoords(v);
    94.                 o.lightmapTexCoord = v.texcoord1;
    95.  
    96.                 UNITY_TRANSFER_FOG(o,o.position);
    97.  
    98.                 return o;
    99.             }
    100.  
    101.             fixed4 frag(vertexOutput i) : COLOR
    102.             {
    103.                 UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
    104.  
    105.                 fixed4 color = tex2D(_MainTex, _MainTex_ST.xy * i.mainTexCoord.xy + _MainTex_ST.zw);
    106.  
    107.                 //Calculate caustic effect based on world space pos
    108.                 fixed4 caustics0 = tex2D(_Caustics, i.worldSpacePos.xz * _CausticsCoord.xy + float2(_CausticsCoord.z + (_Time.x * _CausticsSpeed), _CausticsCoord.w));
    109.                 fixed4 caustics1 = tex2D(_Caustics, i.worldSpacePos.xz * _CausticsCoord.xy + float2(_CausticsCoord.z - (_Time.x * _CausticsSpeed), _CausticsCoord.w));
    110.                 fixed4 caustics2 = tex2D(_Caustics, i.worldSpacePos.xz * _CausticsCoord.xy + float2(_CausticsCoord.z, _CausticsCoord.w + (_Time.x * _CausticsSpeed)));
    111.                 fixed4 caustics3 = tex2D(_Caustics, i.worldSpacePos.xz * _CausticsCoord.xy + float2(_CausticsCoord.z, _CausticsCoord.w - (_Time.x * _CausticsSpeed)));
    112.                 fixed4 caustics = ((saturate(caustics0.r + caustics1.g + caustics2.b + caustics3.a) * (_CausticsBoost + 1)) + _CausticsBoost);
    113.  
    114.                 //Calculate intensity based on above calculations
    115.                 fixed causticsIntensity = clamp((i.worldSpacePos.y - _CausticsPosition0) / (_CausticsPosition1 - _CausticsPosition0), 0, 1);
    116.                 causticsIntensity = lerp(_CausticsIntensity0, _CausticsIntensity1, causticsIntensity);
    117.                 causticsIntensity = causticsIntensity * min(1.0f, max(0.0f, dot(i.worldNormal, float3(0, 1, 0)) + 0.5f));
    118.  
    119.                 //Caustics are white, which makes the intensity look bad, lerp to 70% of base intensity for contrast effect
    120.                 causticsIntensity = lerp(causticsIntensity, 0.7 * causticsIntensity, _Contrast);
    121.  
    122.                 //Apply contrast effect to base color
    123.                 fixed lum = saturate(Luminance(color.rgb));
    124.                 fixed4 cont = fixed4(lum, lum, lum, 1.0);
    125.                 cont.rgb *= (1.0 - lum);
    126.                 fixed lum2 = (cont.r + cont.g + cont.b) / 3;
    127.                 cont.rgb += (lum2 + (2 * lum2)) * _Contrast;
    128.                 //After contrast calculations, perform lerp based on contrast power.
    129.                 color.rgb = lerp(color.rgb, cont.rgb, _Contrast);
    130.  
    131.                 //This is the code that makes lightmaps work. 1.4 is the power factor of the lightmap.
    132.                 //Lightmaps don't get applied if contrast is in effect, and power factor gets reduced.
    133.                 //Apply caustics then lightmap
    134.                 fixed4 light = ((caustics * causticsIntensity) + (1 - causticsIntensity));
    135.                 light.rgb *= color.rgb * lerp(1.4, 1, _Contrast) *  DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap, unity_LightmapST.xy * i.lightmapTexCoord.xy + unity_LightmapST.zw));
    136.  
    137.                 //Apply saturation to final color
    138.                 lum = saturate(Luminance(light.rgb));
    139.                 fixed4 output;
    140.  
    141.                 //Lerp based on either contrast or saturation level, whichever is stronger.
    142.                 fixed sat = 1 - _Contrast;
    143.                 if (_Saturation < sat) {
    144.                     sat = _Saturation;
    145.                 }
    146.                 output.rgb = lerp(fixed3(lum, lum, lum), light.rgb, sat);
    147.  
    148.                 //Apply Unity fog
    149.                 UNITY_APPLY_FOG(i.fogCoord, output);
    150.                 UNITY_OPAQUE_ALPHA(output.a);
    151.  
    152.                 return output;
    153.             }
    154.  
    155.             ENDCG
    156.         }
    157.     }
    158.  
    159.     FallBack "Mobile/VertexLit"
    160. }
    161.  
     
  2. brownboot67

    brownboot67

    Joined:
    Jan 5, 2013
    Posts:
    375
    You're doing all your Caustic UV transformations in the pixel shader, and they aren't based on a texture read, so those should all go in the vert program and packed.

    Your saturation if statement is overkill. Just throw a min in there.

    Other than that I'd only be worried about your sample count... 5 samples is no joke for mobile, let alone mobile VR. Honestly I'd take a hard look at your caustic problem and figure out a cheaper way to solve it.
     
  3. TerabyteTim

    TerabyteTim

    Joined:
    Oct 20, 2011
    Posts:
    115
    Thanks for the reply! It was very helpful. I've adjusted the shader to move most of the UV calculations into the vert function. I'm also going to look at performing the caustics calculation in a cheaper way, I'll update when I figure something out. Here's my current shader as it stands, any other comments are much appreciated.

    Code (CSharp):
    1. //Supports lightmap, saturation, contrast, unity fog.
    2. Shader "Underwater/Mobile Fast/Lightmap/Saturation Contrast"
    3. {
    4.     Properties
    5.     {
    6.         _MainTex("Main Texture", 2D) = "white" {}
    7.         _Saturation("Saturation", Range(0, 1)) = 1.0
    8.         _Contrast("Contrast", Range(0, 1)) = 0.0
    9.         [Header(Caustics)]
    10.         _Caustics("Caustics Texture (RGBA)", 2D) = "white" {}
    11.         _CausticsCoord("Tiling(XY) Offset(ZW) - Overrides texture coords", Vector) = (0.5,0.5,0,0)
    12.         _CausticsSpeed("Speed", Float) = 1
    13.         _CausticsBoost("Boost", Range(0,1)) = 0
    14.         _CausticsIntensity0("Intensity A", Range(0, 1)) = 1
    15.         _CausticsIntensity1("Intensity B", Range(0, 1)) = 0
    16.         _CausticsPosition0("Position A (World Y)", Float) = 2
    17.         _CausticsPosition1("Position B (World Y)", Float) = 4
    18.     }
    19.  
    20.     SubShader
    21.     {
    22.         Tags{ "RenderType" = "Opaque" "Queue" = "Geometry" }
    23.         LOD 100
    24.  
    25.         Pass
    26.         {
    27.             Tags{ "LightMode" = "ForwardBase" }
    28.  
    29.             CGPROGRAM
    30.  
    31.             #pragma target 3.0
    32.             #pragma vertex vert
    33.             #pragma fragment frag
    34.             #pragma multi_compile_fog
    35.             #include "UnityCG.cginc"
    36.  
    37.             uniform sampler2D _MainTex;
    38.             uniform fixed _Saturation;
    39.             uniform fixed _Contrast;
    40.             uniform sampler2D _Caustics;
    41.             uniform float4 _CausticsCoord;
    42.             uniform fixed _CausticsSpeed;
    43.             uniform fixed _CausticsBoost;
    44.             uniform fixed _CausticsIntensity0;
    45.             uniform fixed _CausticsIntensity1;
    46.             uniform fixed _CausticsPosition0;
    47.             uniform fixed _CausticsPosition1;
    48.  
    49.             uniform half4 _MainTex_ST;
    50.             uniform half _UVSec;
    51.             uniform float4 _DetailAlbedoMap_ST;
    52.  
    53.             struct vertexInput
    54.             {
    55.                 float4 vertex : POSITION;
    56.                 float3 normal : NORMAL;
    57.                 float4 texcoord : TEXCOORD0;
    58.                 float4 texcoord1 : TEXCOORD1;
    59.             };
    60.  
    61.             struct vertexOutput
    62.             {
    63.                 float4 position : SV_POSITION;
    64.                 float2 mainTexUV : TEXCOORD0;
    65.                 float2 lightmapUV : TEXCOORD1;
    66.                 float4 causticsUV0 : TEXCOORD2;
    67.                 float4 causticsUV1 : TEXCOORD3;
    68.                 float4 worldSpacePos : TEXCOORD4;
    69.                 float3 worldNormal : TEXCOORD5;
    70.                 UNITY_FOG_COORDS(4)
    71.                 UNITY_VERTEX_OUTPUT_STEREO
    72.             };
    73.  
    74.             //This function fixes issues with single-pass stereo rendering
    75.             inline float4 TexCoords(vertexInput v)
    76.             {
    77.                 float4 texcoord;
    78.                 texcoord.xy = TRANSFORM_TEX(v.texcoord, _MainTex); // Always source from uv0
    79.                 texcoord.zw = TRANSFORM_TEX(((_UVSec == 0) ? v.texcoord : v.texcoord1), _DetailAlbedoMap);
    80.                 return texcoord;
    81.             }
    82.  
    83.             vertexOutput vert(vertexInput v)
    84.             {
    85.                 vertexOutput o;
    86.  
    87.                 UNITY_INITIALIZE_OUTPUT(vertexOutput, o);
    88.                 UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    89.  
    90.                 o.worldSpacePos = mul(unity_ObjectToWorld, v.vertex);
    91.                 o.worldNormal = UnityObjectToWorldNormal(v.normal);
    92.  
    93.                 o.position = UnityObjectToClipPos(v.vertex);
    94.  
    95.                 float4 texCoords = TexCoords(v);
    96.                 //New main tex UV calculation
    97.                 o.mainTexUV = _MainTex_ST.xy * texCoords.xy + _MainTex_ST.zw;
    98.                 //New lightmap UV calculation
    99.                 o.lightmapUV = unity_LightmapST.xy * v.texcoord1.xy + unity_LightmapST.zw;
    100.                 //New caustics UV calculations.
    101.                 o.causticsUV0.xy = o.worldSpacePos.xz * _CausticsCoord.xy + float2(_CausticsCoord.z + (_Time.x * _CausticsSpeed), _CausticsCoord.w);
    102.                 o.causticsUV0.zw = o.worldSpacePos.xz * _CausticsCoord.xy + float2(_CausticsCoord.z - (_Time.x * _CausticsSpeed), _CausticsCoord.w);
    103.                 o.causticsUV1.xy = o.worldSpacePos.xz * _CausticsCoord.xy + float2(_CausticsCoord.z, _CausticsCoord.w + (_Time.x * _CausticsSpeed));
    104.                 o.causticsUV1.zw = o.worldSpacePos.xz * _CausticsCoord.xy + float2(_CausticsCoord.z, _CausticsCoord.w - (_Time.x * _CausticsSpeed));
    105.  
    106.                 UNITY_TRANSFER_FOG(o,o.position);
    107.  
    108.                 return o;
    109.             }
    110.  
    111.             fixed4 frag(vertexOutput i) : COLOR
    112.             {
    113.                 UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
    114.  
    115.                 fixed4 color = tex2D(_MainTex, i.mainTexUV);
    116.  
    117.                 fixed4 caustics0 = tex2D(_Caustics, i.causticsUV0.xy);
    118.                 fixed4 caustics1 = tex2D(_Caustics, i.causticsUV0.zw);
    119.                 fixed4 caustics2 = tex2D(_Caustics, i.causticsUV1.xy);
    120.                 fixed4 caustics3 = tex2D(_Caustics, i.causticsUV1.zw);
    121.                 fixed4 caustics = ((saturate(caustics0.r + caustics1.g + caustics2.b + caustics3.a) * (_CausticsBoost + 1)) + _CausticsBoost);
    122.  
    123.                 fixed causticsIntensity = clamp((i.worldSpacePos.y - _CausticsPosition0) / (_CausticsPosition1 - _CausticsPosition0), 0, 1);
    124.                 causticsIntensity = lerp(_CausticsIntensity0, _CausticsIntensity1, causticsIntensity);
    125.                 causticsIntensity = causticsIntensity * min(1.0f, max(0.0f, dot(i.worldNormal, float3(0, 1, 0)) + 0.5f));
    126.  
    127.                 //Caustics are white, which makes the intensity look bad, lerp to 70% of base intensity
    128.                 causticsIntensity = lerp(causticsIntensity, 0.7 * causticsIntensity, _Contrast);
    129.  
    130.                 //Apply contrast effect to base color
    131.                 fixed lum = saturate(Luminance(color.rgb));
    132.                 //Get grayscale color
    133.                 fixed4 cont = fixed4(lum, lum, lum, 1.0);
    134.                 //Normalize it
    135.                 cont.rgb *= (1.0 - lum);
    136.                 fixed lum2 = (cont.r + cont.g + cont.b) / 3;
    137.                 //2 is the power factor of contrast whiteness.
    138.                 cont.rgb += (lum2 + (2 * lum2)) * _Contrast;
    139.                 //Lerp to contrast color
    140.                 color.rgb = lerp(color.rgb, cont.rgb, _Contrast);
    141.  
    142.                 //This is the code that makes lightmaps work. 1.4 is the power factor of the lightmap.
    143.                 //Lightmaps don't get applied if contrast is in effect, and power factor gets reduced.
    144.                 //Apply caustics then lightmap
    145.                 fixed4 light = ((caustics * causticsIntensity) + (1 - causticsIntensity));
    146.                 light.rgb *= color.rgb * lerp(1.4, 1, _Contrast) *  DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap, i.lightmapUV));
    147.  
    148.                 //Apply saturation to final color
    149.                 lum = saturate(Luminance(light.rgb));
    150.                 fixed4 output;
    151.  
    152.                 //Lerp based on either contrast or saturation level, whichever is stronger.
    153.                 fixed sat = min(1 - _Contrast, _Saturation);
    154.                 output.rgb = lerp(fixed3(lum, lum, lum), light.rgb, sat);
    155.  
    156.                 //Apply Unity fog
    157.                 UNITY_APPLY_FOG(i.fogCoord, output);
    158.                 UNITY_OPAQUE_ALPHA(output.a);
    159.  
    160.                 return output;
    161.             }
    162.  
    163.             ENDCG
    164.         }
    165.     }
    166.  
    167.     FallBack "Mobile/VertexLit"
    168. }