与3D场景渲染机制不一样,UI渲染以Canvas为单位进行,CPU负责把UI界面的逻辑结构进行更新,汇总,并负责把这些数据准备好,最后把这些信息传给GPU。
渲染开销建议控制在2ms以内,对于一个3D游戏来说一般UI Drawcall控制在50,对于比较重UI的游戏,也尽量不要超过100。
UI影响CPU的因素:
- 界面结构复杂度
- 界面结构变化频率
- 动画复杂度
UI影响GPU的因素:
- Drawcall
- 图片最终在屏幕所展现的面积
- 图片是否透明
- Shader的复杂度
- Overrdraw,单位像素的重新绘制次数
其中,特别值得注意的是Drawcall和Overdraw。
常用的优化措施有:
Profiler分析
Canvas.Sorting() 耗时过高
减少Canvas使用数量,减少动态排序逻辑。
Canvas.SendWillRenderCanvases() 耗时过高
UI全部都在一个canvas下,层级很深并且里面有很多的UI效果,导致UI更新耗时较长,建议对UI动态和静态进行拆分(多个canvas,但不能太多,否则Canvas.Sort()会阻塞主线程)减少UI的层级。
具体一个界面应该限制在多少层级,官方并没有一个标准的推荐值,这不是单一维度可以衡量的,因为每个界面的材质纹理复杂度,Canvas Render的裁剪合并结果都不一样,所以最终需要项目组做具体的测试。
PS:如果单个UI比较复杂,还可能因为引用的资源量较多,导致IO时间过长。
批处理
UI批处理:对UI的批处理比较复杂,它受UI布局的影响:
- 是否透明
- 是否遮挡
- 是否深度相邻
- 是否Desc一致 即相同状态
参考
-
- UI合批规则完整性测试:Unity中关于UGUI中因为UI遮挡关系不同而造成DrawCall变化的猜想
- Unity UI优化指导:优化移动游戏性能:来自Unity顶级工程师的Physics、UI和音频设置小贴士 | Unity Blog
加载Load
使用异步接口。
加载-分帧
复杂界面
拆分成多个子部分,优先加载背景的部分。
列表数据
分页加载。
动静分离
优化UGUI.UpdateBatches时间
- 特别由于低端机的核数较少,导致UpdateBatches的Job很难得到调度
- 将会动的UI元素单独放到⼀个Canvas⾥,这样每帧UpdateBatches的 开销会减少
OnGui()
GUI.Repaint() 说明有使用原生的IMGUI(OnGUI),建议不要在正式上线版本中使用。
SetActive
根据UI界面使用频率的不同区别处理:
- 使用频率很低,直接Instantiate/Destroy处理
- 使用较为频繁,可缓存并通过SetActive管理可见性
- 如果使用频率非常高,比如BattleHud界面,则可通过改变UI界面Alpha = 1/0(同时开关blocksRaycasts值)的方式来处理
PS:Why SetActive 导致卡顿
- 会导致网格重建,会导致内存分配,触发GC
- 会遍历这个实体上所有继承MonoBehaviour的脚本的OnEnable和OnDisanle调用
- 对于复杂结构的物体,父物体在使用SetActive时 也会调用子物体的GameObject.Activate和GameObject.Deactivate
- 有初始化逻辑的可能重新做一些初始化的工作
填充率
- UGUI只产生quad(简单的顶点),所以顶点着色器不太可能给GPU Tiler流水线产生压力。着色器中的任何问题都应归结于填充率 Fillrate
- 在移动设备上,FillRate 的压力主要来自半透明物体
- 注意绘制像 alpha=0 这种实际上不会产生效果的颜色上去,也同样有 Blend 操作,这是一种极大的浪费
- UI渲染过程中 谨慎 全屏透明的渲染
- 隐藏不可见的对象
Alpha Test
渲染设置,避免使用Alpha Test,因为非常耗时,性价比很低。
全屏问题
UI覆盖全屏的情况下
- 可以不渲染后面的3D场景,可能导致发热降频、耗电量高
- 避免Overdraw
- ⻓时间不动的界⾯可以实现OndemandedRendering动态降频
Shader
留意到里面有discard语句,可能带来性能影响,应编写无clip版本的Shader
拼接
超大尺寸的图片 考虑拼接方式
RenderTarget
2D项目所有相机都没有⽤到RT的Depth,可以在创建RT的时候不创建Depth和Stencil,降低内存和显存带宽消耗。
组件
Mask组件
LayoutGroup组件
- 在UI没有增删的时候设置Enable = false, 否则会导致UI Layout时间大幅增加
- 不需要进行事件接收的组件,取消勾选Raycaster Target
Text组件
- 不使用富文本的Text,取消勾选Rich Text,不使用Best Fit
- 用TextMashPro代替OutLine,Shadow组件
UI-Particle
含有大量的BakeMesh操作,建议低端机去掉该功能。
图集
- 不需要的图集不进内存(不要存放在Resources目录下,打包成AssetBundle)
- 不使⽤的图集及时卸载
- 提高图集的利⽤率:如果⼤的图集中UI元素很少,且存在⼤量的透明区域时,需要进行合理的规划
不使用Built-in资源
UI引⽤了Unity⾃带的⼀部分Texture,建议批量替换成项⽬组⾃⼰的
Fonts
合理规划字体数量,并通过工具裁剪字体字库。
九宫格
对于一些规则的图,比如背景框。
贴图格式压缩
ASTC
Texture 2D Import Settings
优化Sprite填充率
- 尽量用双线性过滤,不用三线性插值
- 可读写没必要开启
MipMap
Sprite类型的mipmap,UI没必要,因为固定分辨率大小,UI图片很少有缩小的情况出现。
PS:纹理贴图载入内存时,根据屏幕空间大小来计算多层mipmap。渲染系统会根据各个mip层的大小和几何图形在屏幕上的面积来决定哪一个mip层被选择。
取消勾选Generate Physics Shape
勾选该选项会生成物理碰撞的形状。
Alpha Source
- 针对于没有Alpha通道的图可以直接设置为None。(None:无论输入纹理是否有 Alpha 通道,导入的纹理都没有 Alpha 通道)
- 针对于Alpha通道不是0 就是 1的情况下,选择Input Texture Alpha。注意同时选中Alpha is Transparency 选项
Texture Type
如果图像仅有⼀个通道,则将Texture Type设置为 Single Channel。