【Unity3D杂谈】记一次优化shader性能的过程

一、问题描述

我们的手游的主场景主要由一系列的2D图片的SpriteRenderer组成,有一片大的水面背景,有一个shader去实现了水颜色渐变和波纹的效果。在岛上有很多建筑物。
按理论上,有更多建筑的区域应该渲染压力更大,而纯水面的地方应该只有一个shader在渲染。
但是经测试发现,在一个低端的Android手机上(大约千元的三星手机)上,发现将相机拖动到只有背景海面的区域,帧率也会明显下降,明显不符合预期,因此开始探究为什么这个shader会导致帧率下降。

二、探索过程

通过阅读shader代码,实际上并没有特别多的计算量,因此,怀疑shader使用了什么特性,在一些低端设备上支持不好导致。
因此,拿这个shader改造了几个版本:
1、去掉法线采样和unpack
2、简化计算
3、去掉顶点数据传递的uv数组,改成非数组传递,即原本有float2[4]改成两个float4的xy和zw去表示
4、去掉for循环,由于循环次数(3次)有限,直接展开,重复代码写3份

// 旧
float2 uv[4]        : TEXCOORD0;
// 新
float4 uv1        : TEXCOORD0;
float4 uv2        : TEXCOORD1;



// 旧
for (uint idx = 0; idx < _calcTime; idx++)
{
    half3 norDir = UnpackNormal(tex2D(_NormalMap, i.uv[idx]));
    // 逻辑处理
}

// 新

half3 norDir = UnpackNormal(tex2D(_NormalMap, i.uv1.xy));
// 逻辑处理
norDir = UnpackNormal(tex2D(_NormalMap, i.uv1.zw));
// 逻辑处理
norDir = UnpackNormal(tex2D(_NormalMap, i.uv2.xy));
// 逻辑处理

然后拼了一个简单的界面,在不同的shader切换,测试帧率。
最后发现,以上起作用的是3和4,起决定性作用的是4。去掉循环之后帧率就恢复到60了。

三、结论

这种现象可能是由于GPU的优化问题。在一些低端设备上,GPU对于循环的支持可能不是很好。
尤其是当循环次数不是固定的时候,上述代码循环次数是由_calcTime决定的,这可能导致GPU无法在编译阶段进行循环展开优化。
此外,使用数组也可能导致一些额外的开销,因为GPU需要在内存中查找数组元素的位置。使用两个独立的变量可能使得内存访问更加高效。

你可能感兴趣的:(unity,游戏引擎,游戏)