Distortion shader problem

Hi,

We have a shader that makes a nice lens like distortion effect. It works fine in most mobile platforms, like Asus Nexus 7 with Tegra, but freezes the fps totally in Asus Nexus 7 with Snapdragon. Both devices run Android 4.4. Any ideas what might go wrong?

Shader "Custom/Aberration" {
	Properties {
		_DistortAmt  ("Distortion", Float) = 10.0
		_DamageTex ("Base (RGBA)", 2D) = "white"
		_DamageMin("Damage Min", float) = 0.0
		_DamageMax("Damage Max", float) = 0.0
	}
	Category {
	
		// We must be transparent, so other objects are drawn before this one.
		Tags { "Queue"="Transparent+1" "RenderType"="Opaque" }
		
		SubShader {
		
			// This pass grabs the screen behind the object into a texture.
			// We can access the result in the next pass as _GrabTexture
			GrabPass {							
				Name "BASE"
				Tags { "LightMode" = "Always" }
			}
			
			// Main pass: Take the texture grabbed above and use the bumpmap to perturb it
			// on to the screen
			Pass {
				Name "BASE"
				Tags { "LightMode" = "Always" }
				Tags { "RenderType" = "Opaque" }
				Blend SrcAlpha OneMinusSrcAlpha
				CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#pragma fragmentoption ARB_precision_hint_fastest
				#include "UnityCG.cginc"
					float _DistortAmt;
					sampler2D _GrabTexture;
					sampler2D _DamageTex;
					float _DamageMin;
					float _DamageMax;
					float4 _GrabTexture_TexelSize;
					
					struct appdata_t {
						float4 vertex : POSITION;
						float2 uv : TEXCOORD0;
						float3 normal : NORMAL;
					};
					
					struct v2f {
						float4 vertex : POSITION;
						float4 pos : TEXCOORD0;
						float3 normal : TEXCOORD1;
						float2 uv : TEXCOORD2;
						float view : TEXCOORD3;
					};
					v2f vert (appdata_t v) {
						v2f o;
						#if UNITY_UV_STARTS_AT_TOP
						float scale = -1.0;
						#else
						float scale = 1.0;
						#endif
						
						o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
						o.uv = v.uv;
						o.pos.xy = (float2(o.vertex.x, o.vertex.y*scale)+o.vertex.w)*0.5;
						o.pos.zw = o.vertex.zw;
						o.normal = mul((float3x3)UNITY_MATRIX_MVP, v.normal);
						o.view = dot(normalize(v.normal),normalize(WorldSpaceViewDir(v.vertex)));
						return o;
					}
					half4 frag( v2f i ) : COLOR {
						float4 c = tex2D(_DamageTex, i.uv);
						float2 offset = i.normal.xy*(1.0-i.view) * _DistortAmt * _GrabTexture_TexelSize.xy;
						float4 posOrig = i.pos;
						i.pos.xy = i.pos.z * offset + i.pos.xy;
						half4 dist = tex2Dproj( _GrabTexture, UNITY_PROJ_COORD(i.pos));
						return half4(dist.rgb, smoothstep(_DamageMin, _DamageMax, c.r));
					}
				ENDCG
			} 
		}
		Fallback "Custom/CheapoAberration"
	}
}

br, pertsa

In addition to previous, I noticed that in Android build shader worked OK on Tegra when compiled with Unity 4.2.2. But when I build with Unity 4.3.4, it drops the framerate to near zero on Tegra device too.

br, pertti

It might not be the performance problem you’re dealing with, but I see your GrabPass doesn’t specify a texture name, only a pass name, so it’s doing that expensive operation every time.

Well spotted and that improved things a bit. Unfortunately on device performance is still not acceptable.

I tend to believe that we must come up with a replacement shader for the “glass” on mobile. Any tips highly appreciated.

br, pertsa

Don’t use grabpass on mobile, period. Instead, render to a texture, and use that.

I find grabpass doesn’t work on ios,by causing the screen black.Do you have any ideas about it?

A small hint only. You should use all texture lookups in the first place of code if its possible.

half4 frag( v2f i ) : COLOR {
                        float4 c = tex2D(_DamageTex, i.uv);
                        half4 d = tex2Dproj( _GrabTexture, UNITY_PROJ_COORD(i.pos));
..
                    }

That supports the optimized memory and multi shader threading on mobil and desktop gpu.
Should be not the solution for your issue, but can raise performance drastically.