UGUI 优化笔记
UI标准
UI 每帧占用时间均值2-3ms
性能问题
问题定位
- profiling
DrawCall
-
batch 避免同类型控件重叠
同类型控件在不重叠时会自动合批传入一个drawcall
UI重叠导致分层,分层导致同类型UIBatch分离,增加drawcall数量
不规则图形,图形2D/3D旋转,图形放大,导致透明区域重叠
z不一致也会导致分层 - 使用图集
同一图集图像在合批时会自动优化到同一批,且加载时会同时加载 - 减少顶点修改以减少Rebatch
如Image组件的Color属性改为修改Material属性 的tintcolor - 调用方法
Canvas.BuildBatch
WaitingForJob
PutGeometryJobFence
BatchRender.Flush - 单纯隐藏 UI, scale = 0, Alpha Group = 0
OverDraw
- 关闭 Image 检测事件 一个像素位置被重复渲染
图片压缩
- RGBA32 Bit
- 表示每个像素占用32位4字节,内存大小 1024 X 1024 X 4 = 4M
- RGBA16 Bit
- 表示每个像素占用16位2字节,内存大小 1024 X 1024 X 2= 2M
- RGB ETC1 4Bit
- 表示每个像素占用4位0.5字节,内存大小 1024 X 1024 X 0.5= 0.5M
- 不带透明通道
- 图片宽和高可以不相等但是必须被4整除
- RGBA ETC2 8Bit
- 表示每个像素占用8位1字节,内存大小 1024 X 1024 X 1= 1M
- 带透明通道
- 图片宽和高可以不相等但是必须被4整除
- OpenGL ES 3.0以上的Android手机(2013年以后)
- RGBA PVRTC 4Bit
- 表示每个像素占用4位0.5字节,内存大小 1024 X 1024 X 0.5= 0.5M
- 要求图片的宽高必须相等并且是2的整数次幂,对无法满足的图片可单独打包成图集
- 压缩的效果较差,尤其透明通道
- RGBA ASTC 4X4
- 表示每个像素占用8位1字节,内存大小 1024 X 1024 X 1= 1M
- ASTC 5X5(表示每个压缩块的大小是5 X 5=25),不带透明通道
- ASTC 4X4(表示每个压缩块的大小是4 X 4=16),带透明通道,图片宽和高可以不相等但是必须被4整除
- ASTC只支持苹果A8以后的设备(iP6)
- Crunched
Unity Android 可以进行二次压缩
Android : ETC1>ETC2->RGBA16->RGBA32
iOS : PVRTC>ASTC->RGBA16->RGBA32
通道分离
针对Android平台Unity还提供了一种通道分离的方式:将图片压缩成ETC1,提取Alpha生成一张通道图。为了让混合起来的Alpha效果更好,Unity将通道图保存的格式设定为a8格式。比如一张1024X1024的贴图,ETC1压缩结果为0.5M,通道图提取后a8格式压缩结果为1M,加起来就是1.5M
通道图内存上可以减少一些,但是在Shader中需要进行2次采样
UI优化
- 界面中的Tab页尽量都做成单独的界面,切换后再动态加载
- 界面粒子特效最好不要预制在界面Prefab中,需要播放时再动态加载
- 界面打开从同步变成异步,从一帧变成多帧
如打开界面先开一个底图,然后上面的元素再一个一个地冒出来,再配合一些UI动画,一帧一帧加载列表中的元素 - 有些类似头像或者icon的图标美术无法给出宽高被4整除的尺寸,可以给它设置一个唯一的SpriteTag打成一个图集,这样就可以压缩了
- 打开界面首帧Active和DeActive的次数过多,这个次数可以在Profiler中看到,其中Call的数量就是
- 很多UI在开发的时候都是从另一个相似界面复制一份出来,有些没用的对象只是隐藏并没有真正删除,所以要保证将没用的都删除掉
- 界面有可能有一些状态要进行切换,例如Tab页为了做起来方便,会把每个Tab页每个页面的内容都放进去,通过隐藏显示来控制各自的游戏对象。这种类似的情况都尽可能修改成运行时动态加载
- 做好分帧加载,可以将滑动列表的元素分帧一个个加载
- UI特效和界面上显示的模型,最好不要预制在界面中,一方面影响AssetBundle打包的依赖关系,还容易造成打开界面慢,可以通过代码动态分帧加载
- 尽可能少用LayoutGroup相关组件,或者自行来实现类似的功能
- 宽高任意一边超过256的图片最好不要打进图集
- 总之,对于首次打开慢的界面,我们一定要控制一帧内加载的UI元素和图集的数量
- 将每个界面的图片从Prefab中拆出来,将需要加载的图片名字序列化在Prefab中。这样界面中只保留了GameObject的树形结构,加载就不会慢了。界面打开后,再通过之前序列化好的名称,动态分帧地将贴图一个个加载上
- 操作界面之前以及操作界面之后确定UI是否在无意义重建,可以参考文章前面提到的方法得到到底哪些UI元素了引起UI的整个重建。
- 避免Active和DeActive,而采用修改显示layer的方式来控制隐藏显示。
- 不需要参与点击事件的Canvas取消激活Graphic Raycaster脚本。
- 仅用于显示的图片或者文本禁止勾选Raycast Target。
- 持续性的UI动态效果特效,最好脱离UI系统,采用特效的方式制作。
- 动态和静态的UI要区别对待,分别挂上canvas。
- 适当对UI界面做缓存,保证再次打开更快。
- 界面初始化代码部分添加上Profiler监测,统计代码效率。
- 将复杂的界面拆成多个界面,比如Tab页点击切换时再加载。
- 再次打开界面也需要控制一帧内加载UI的数量,做好分帧加载策略。
其他
- 特效优化
不要将特效放入Prefab,使用动态加载,减少AssetBundle冗余 - 资源规划
可使用图片的MD5进行对比,查找重复图片,合理分配图集 - Profiler
- Deep Profiler 查看函数栈
- 模拟器数据不准确
- #if UNITY_EDITOR,部分代码编辑模式下运行
- Editor有缓存
- 渲染性能与时间
- List缓存池
- 插入操作耗时,尽量Add()少Insert(),Remove可先将数据与最后一位调换再Remove,减少list整体下标移动
- List Contain方法本质为遍历,Dictionary快很多,可单独使用Dictionary建立查询表