found 2 pdfs about Non-hardware specific hlsl shader optimizations tricks, wonder if the following are still true for mobiles?
First read:
http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2012/10/Dark_Secrets_of_shader_Dev-Mojo.pdf
summary 15 shader optimization tricks
1-Use Intrinsic Functions (make sense)
2-Properly Use Data Types (seems contradict with #3?)
3-Reduce Typecasting (seems contradict with #2?)
4-Avoid Integer Calculations (make sense)
5-Use Integers For Indexing (make sense)
6-Pack Scalar Constants (make sense)
7-Pack Arrays of Constants (not sure)
8-Properly Declare Constants (not sure)
9-Vectorize Calculations (make sense, but not sure)
10-Vectorize Even More (make sense)
11-Vectorize Comparisons (make sense)
12-Careful With Matrix Transpose (only used square matrix before, so not sure)
13-Use Swizzles Wisely (can not understand)
14-Use 1D Texture Fetches (make sense)
15-Use Signed Textures (signed normal map exists in mobile platform?)
wonder should I apply all these optimizations to my mobile shader(mostly for Opengles2/3)?
Second read:
http://www.humus.name/Articles/Persson_LowLevelThinking.pdf (the difficulty of this pdf is beyond my shader knowledge, not sure if I should apply this direcly to mobile, because hlsl/cg is translated to glsl by Unity, I saw the translated glsl code, it generates very different code for ES2 & ES3)
some optimization tricks mentioned in this pdf,
-make calculation in MAD form = just 1 assembly level instruction (confirmed by bgolus), which means
x * a + b // 1 MAD. good
(x + a) * b //1 ADD, 1 MUL. bad
-seperate scalar & vector calculations if possible (confirmed by bgolus), which means
vector4 * scalar * vector4 * scalar;//slow
(vector4 * vector4) * (scalar * scalar);//better
-abs() neg() for input, saturate() for output is free (confirmed by bgolus)
float c = saturate(abs(a) + abs(b)); //1 ADD, all abs & saturate is free
float c = abs(a+b); //1ADD, 1MOV, bad
float c = -a * b; //1 MUL, free negative
float c = -(a*b); //1 MUL, 1MOV, bad
float c = saturate(1-a);//1 ADD, free saturate
float c = 1 - saturate(a);//1MOV, 1ADD, bad
-conditional assignment is faster then sign() or step()
(x >= 0) ? 1 : -1;//fast
sign(x);//so no reason to write this
(x > 0 ) ? 1 : 0; //fast
step(x);//so no reason to write this
is the above “best-practice” still correct if targeting ES2/ES3 (glsl)?
currently our fragment shader optimization stick to the following rules:
-calculate all uv data in vertex shader, use them in fragment shader directly(passing uv via varying to avoiding dependent texture read(maybe it is only useful for es2 PowerVR TBDR gpus? which is iPhone4S,iPhone5,iPad2…only)
Even end up using more varyings (currently use up all ES2’s 8 TEXCOORD varyings to pass uvs / packed scalars, also a few COLORs, not sure if it will cause shader interpolator bound…)
see this for more info about dependent texture read:
struct v2f{
half2 uv : TEXCOORDn; //pass uvs direcly
half4 packed2uvs : TEXCOORDn; //afraid of dependent texture read, avoid packing 2 uvs into 1 varying
half4 packed4Scalars : TEXCOORDn; //for general purpose scalar packing
}
tex2D(_Texture,i.uv); //good, fastest
tex2D(_Texture,i.uv.xy); //afraid of dependent texture read, depends on driver?. avoid
tex2D(_Texture,i.uv.zw); //afraid of dependent texture read. avoid
-combine multiple vertex light’s calculation in vertex shader just to make lighting independent to #of pixels
-if alphatest(clip function) is needed , execute as early as possible = call clip() asap
fixed4 mainTex = tex2D(_MainTex,i.uv);
clip(mainTex.a - _AlphaTestThersold); //call immidiately once we know the alpha value
-seperate fragment shader to 3 parts, just to lower data dependency to minimum:
first, read all textures to temp variables, so when GPU is waiting the texture data, it can switch to do other calculation(not sure if it is true)
second, do all maths using the fetched textures above, store result in seperate temp variable also
finally, use all result temp variables to calculate final result color
*Not sure if using more variable to store temp result is actually causing trouble(register count limit?)
(confirmed by bgolus)
-use fixed for everything in fragment shader to avoid unexpected swizzle(make sure all calculation is within -2~2 range, value outside -2~2 is not always clamped inside -2~2).
(confirmed by bgolus)
-use the smallest data type whenever possible (fixed3 instead of fixed4 if alpha color is not needed), for example if the result do not care about alpha, do all calculation in fixed3, then return it as fixed4 finally
fixed3 resultColor = ....;
//many calculations, add or mul to resultColor
return fixed4(resultColor,1); //don't care alpha output
-use #if #endif,shader_feature,multi_compile to turn on/off the only calculation needed, no if() in hlsl
wonder if I am doing something wrong / not useful for optimizing my fragment shader?
I use these pdfs to help me when writing shaders, wonder if there are other useful resources?
because the whole game use a shared uber shader for 90% of the rendering, the fragment part of this uber shader is really worth optimizing. I know that my current situation is fragment bound(not sure ALU bound / tex fetch bound) because it runs faster if I lower the resolution and no performance gain if all textures replaced by 2x2 textures, so I wrote this post, try to ask for help before any premature optimizations.
currently testing on:
-Mali400MP - GALAXY Note II LTE - Samsung ES2
-Adreno305 - HTC Desire 816 ES3
-Galaxy Tab A (2016) with S Pen - Mali T830 ES3.1