问题概述
在shader代码的编写中不能只依赖于编译器的优化,它的优化很有局限性,编译器只能去理解shader的语义,他可以移除无用的代码及资源,但并不会把一个add+mul代码优化成mad指令;
重构源代码以减少所需的计算数量,而不是依赖编译器来应用优化。
为了优化shader代码,我们需要知道代码从被编写到被执行的流程,知道什么样的代码是不好的;
一些通用的shader代码规范
避免if、switch分支语句
避免for循环语句,特别是循环次数可变的
减少纹理采样次数
减少复杂数学函数调用
降低浮点数精度
从汇编层来看代码的效率
swizzle语法
由于硬件设计,执行一维数据和四维是一样的时间,shader提供了几个向量组件rgba,stpq,xyzw;我们需要充分利用这种硬件特性,提高硬件效率,避免算力的浪费;
如下是一组测试,分别做了一个float4的乘法和一个float的乘法,观察汇编指令,发现指令数并没有增加。
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
float4 ret = col.rrrr * col.bbbb;
return fixed4(ret.xyz, 1);
}
// 汇编
0: sample r0.xyzw, v0.xyxx, t0.xyzw, s0
1: mul o0.xyz, r0.zzzz, r0.xxxx
2: mov o0.w, l(1.000000)
3: ret
// 计算单个数据
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
float ret = col.r * col.b;
return fixed4(ret, 0, 0, 1);
}
0: sample r0.xyzw, v0.xyxx, t0.xyzw, s0
1: mul o0.x, r0.z, r0.x
2: mov o0.yzw, l(0,0,0,1.000000)
3: ret
通过dot点积代替add,如下,如果使用加法将四个分量相加,则需要三个add指令,如果使用内置点乘,则需要一个dp4指令。
Vectorization:向量化思维,Scalar Code和Vectorized Code;拆分标量的数据,放入到向量中,但是要注意不要产生额外的数学计算,否则就可能是个负优化;
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
fixed ret = col.r + col.g + col.b + col.a;
return fixed4(ret, 0, 0, 1);
}
0: sample r0.xyzw, v0.xyxx, t0.xyzw, s0
1: add r0.x, r0.y, r0.x
2: add r0.x, r0.z, r0.x
3: add o0.x, r0.w, r0.x
4: mov o0.yzw, l(0,0,0,1.000000)
5: ret
//
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
fixed4 arg4 = 1.0;
fixed ret = dot(col, arg4);
return fixed4(ret, 0, 0, 1);
}
0: sample r0.xyzw, v0.xyxx, t0.xyzw, s0
1: dp4 o0.x, r0.xyzw, l(1