一、简介
很久没有更新博客了,最近马三比较忙,一直在处理游戏中优化相关的事务。我们的游戏自从开发以来一直没有做过比较系统的性能优化,最近因为各种原因需要对游戏进行优化,其他同事都有开发任务,因此性能优化的任务就落在了马三身上,说实话马三在性能优化方面也没有太多的经验,都是不断地咨询前辈并且结合网上的资料摸着石头过河。本篇博客中马三就和大家分享一些优化过程中的心得体会,顺便记录一下方便自己日后查阅。
二、优化
1.闪退问题排查
这次优化是因为手机频繁地闪退引起的,我们测试机用的的iPhone11 Pro和iPhone11 Pro Max(博主写下这篇博客的时间是2020年),此时这两款手机可以说是市面上性能最强劲的两款手机了,但是我们的游戏最近跑在上面缺频繁地闪退。虽然马三比较有先见之明地接入了Bugly SDK,并且在Bugly控制台上也捕获到了闪退信息,而且进行了符号表解析,但是Bugly上仅仅有下面这张图这样一个简单的堆栈信息,并不能看出具体是因为什么引起的闪退。
此时就需要进行iOS真机调试了,当马三准备真机调试的时候才发现我们打包机的XCode版本是10.x,而我们的测试机的版本是iOS13.4.1,XCode版本太低并不能直接调试。马三按照网上的教程去下载了真机调试包,然后放在指定目录下。但是并没有什么作用,又试了网上各路大神提供的各种奇巧淫技,最后都没能奏效。后来只能老老实实地对XCode进行了升级操作,在升级XCode之前还需要把Max系统升级到最新,真是蛋疼。不过最后全部升级完以后,去尝试连接真机果然一瞬间就连上了。此时我突然想起了前辈说过的一句话“没事千万别跟软件较劲”,的确软件该升级就升级,别跟软件较劲,吃力不讨好。
在升级完MaxOS系统和XCode软件版本,并且能够连接上真机以后,马三满心期待地点下了Build for running的按钮,并且自信地以为马上就可以看到分析器信息了。但是现实又给了马三残酷的一击,XCode报了一个can not launch的警告信息。马三又是去网上一通查找,把那些方法都试了一遍,还是没有解决问题。后来我怀疑是苹果证书的问题,我们是企业证书,我一度怀疑企业证书打的包不能进行真机调试。后来请教了快手的iOS开发前同事以后,得知了企业证书也可以真机调试,我们这个企业证书不能真机调试的原因很可能是这个企业证书是发布证书,不是Development证书,因此打出的包无法进行调试。时值周六,IT部门并没有上班,因此想去弄一个开发版的企业证书也不现实。后来马三忽然想起苹果现在可以免费申请iOS开发者账户了,于是乎赶紧去申请了一个,然后顺利的打出了包并且进行了真机调试,打开instrument一分析,最后定位到闪退是limit memory of 2GB上面,也就是应用程序占得内存太多了,导致被系统杀死了。
2.ShaderLab内存占用量优化
知道闪退是什么原因导致的就知道了去向着什么方向进行优化了,内存占用高就去降低内存峰值就好了,连接上了Profiler后,马三一看,好家伙,ShaderLab占用了630MB的内存,按理来说Unity游戏中ShaderLab的内存占用量在40MB上下才是比较合理的,我们这个直接顶到了630MB,不崩溃才怪了。ShaderLab的占用量一般和Shader变体数量有关系,变体数量多的话,编译Shader就需要更长的时间并且占用更多的内存。但是咨询过TA以后,说我们游戏还是DMEO期,并没有使用到很多的Shader,但是为什么分析器中还显示占用了这么多内存呢?马三决定写个Shader变体数量收集统计小工具,批量查询一下游戏中的Shader的变体数量,康康到底的是怎么回事。工具的原理很简单,就是收集项目中所有的Shader文件,然后依次对他们执行通过反射拿到的UnityEditor.ShaderUtil.GetVariantCount方法,获取到变体数量,然后输出到csv文件就好了,csv文件可以用Excel工具打开,可以利用Excel按照变体数量进行排序,然后从高到低逐个优化。
代码语言:javascript复制[MenuItem("Tools/AAAAAAAAAAAA")]
public static void GetAllShaderVariantCount()
{
Assembly asm = Assembly.LoadFile(@"D:UnityUnity2018.4.7f1EditorDataManagedUnityEditor.dll");
System.Type t2 = asm.GetType("UnityEditor.ShaderUtil");
MethodInfo method = t2.GetMethod("GetVariantCount", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
var shaderList = AssetDatabase.FindAssets("t:Shader");
var output = System.Environment.GetFolderPath(System.Environment.SpecialFolder.DesktopDirectory);
string pathF = string.Format("{0}/ShaderVariantCount.csv", output);
FileStream fs = new FileStream(pathF, FileMode.Create, FileAccess.Write);
StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);
EditorUtility.DisplayProgressBar("Shader统计文件", "正在写入统计文件中...", 0f);
int ix = 0;
sw.WriteLine("ShaderFile, VariantCount");
foreach(var i in shaderList)
{
EditorUtility.DisplayProgressBar("Shader统计文件", "正在写入统计文件中...", ix/shaderList.Length);
var path = AssetDatabase.GUIDToAssetPath(i);
Shader s = AssetDatabase.LoadAssetAtPath(path, typeof(Shader)) as Shader;
var variantCount = method.Invoke(null, new System.Object[] { s, true});
sw.WriteLine(path "," variantCount.ToString());
ix;
}
EditorUtility.ClearProgressBar();
sw.Close();
fs.Close();
}
通过上面的工具一查,好家伙,有一个Shader竟然有18.4K个变体,这个数量真是惊人。再次与TA进行沟通了以后,发现这个Shader里面有相当一部分的关键字并没有用到,都是一起遗留下来的,能用到的KeyWord就5个左右。众所周知,Shader的变体数量和关键字数目有关,一般来说一个Shader中的关键字每增加一个,该Shader的变体数量就会x2,是成几何裂变的方式去增加的,着实恐怖啊!赶紧让TA把没用的关键字去掉了,然后再次打包进行观察,发现ShaderLab的占用量一下降低到峰值为260多MB了,是小了不少,但是这个占用量依然不合理,还是太多了。
材质球里会记录之前使用的关键字,打个比方 A Shader 使用的关键字 _WOYAOKAIFEIJI B Shader使用率关键字 _WOYEYAOKAIFEIJI 材质球C 开始使用了Ashader 那么会把_WOYAOKAIFEIJI这个关键字记录在材质球,由于某个原因这个材质球不想使用Ashader了,这是切换到B Shader ,那么这个材质球就会包含_WOYAOKAIFEIJI、_WOYEYAOKAIFEIJI这两个关键字。
比如下图中的这个关键字,完全没有材质在显式地使用它,但是就可以搜出来有用了这个关键字的材质。然后打开对应材质球的debug面板,可以发现shader keywords这一栏记录了以前的残留。
未完待续...
三、总结
未完待续...