Unity UI优化总结

Unity UI优化总结


前言

最近又再一次回顾总结了一下Unity UI的优化,在此作下笔记,供学习参考。


核心四大问题

在Unity中UI优化的核心问题就是重绘和批处理之间的平衡。虽然说可以通过一些简单的技巧单方面地减少批次或者减少重绘,但进行过一波优化之后,最终还是要面临批次和重绘的平衡问题的。

常见的四大UI优化问题:

1、片段着色器利用率过高(或者说GPU fill-rate填充率过高),即每个片段处理的时间过长。(可能是片段着色器过于复杂,也可能是每个片段要处理的东西太多/采样数量过多)

一部分原因可能是由于片段着色器的实在是过于复杂了。

针对这一点的优化方案,无非就是降低片段着色器的复杂度。但一般情况下,UI渲染不会过多地修改片段着色器。

所以说大部分情况下的原因还是每个片段下要处理(重叠)的UI数量过多,可能是相机过多导致一个片段下要处理很多UI,也可能是单纯重叠大量的UI所导致的。

那为啥过量重叠UI就会产生这样的问题呢?这个问题实际上就是常说的UI Overdraw(过度绘制),并且在移动端上对性能的影响格外明显。

与正常的物体不同,Unity对于UI的渲染策略是当作半透明物体来进行渲染的,也就是说无论UI是不是包含透明度,都是后往前渲染的(具体可以参考我之前写的笔记)。

针对这一点的优化方案,如:主动消除完全透明的UI;简化UI减少重叠区域;尽可能地关闭不使用的相机;美术层面上合并贴图等。


2、CPU处理批次时间过长。(批次过多)

在Unity的UI系统中,Canvas负责将几何图形合并成批,并且生成对应的渲染命令发送给Unity图形管线。

而一个批次中所处理的事,就是Canvas合并网格并且生成合适的渲染命令发送给Unity图形管线。这个处理的结果会被缓存并且一直被重用,直到Canvas脏了。

批次过多,DC也就过多,GPU渲染压力也就会过大。所以说,我们要尽可能地合并批次。

而对Unity来说,渲染UI的顺序影响因素包括UI的深度 > 材质id > 贴图id > 渲染层级(相同条件下比较下一个因素)。(具体深度如何计算的请参考我之前写的笔记

按照渲染UI的顺序,相邻且相同材质的UI可以合批。

对于减少批次的优化方案,按顺序来,首先就是尽可能保证UI的材质都一样;其次就是尽可能保证UI的贴图都一样或者都来自同一图集;再次就是减少合批打断,可以通过调整UI的Hierarchy或者是调整UI的重叠来减少合批打断。

除此之外,如果Canvas上存在大量大型Graphic元素,在合并网络的时候,排序和分析将会花费大量时间。


3、频繁重绘Canvas花费时间过长。(Canvas过脏)

首先搞明白一点,何为重绘?或者说重绘干啥了?

Canvas的重绘把Graphic组件的布局、顶点、网格等全部重新计算了一次,或者说合批就是重绘中的一部分。当Canvas不需要重绘/合批的时候,Unity图形管线会直接用上次缓存的命令来进行后续的图形绘制。具体开销或者细节可以从CanvasUpdateRegistry中看出来。

至于什么情况下会导致Canvas重绘,这里列出了Unity中导致Canvas变脏的地方:

· 设置顶点脏——SetVerticesDirty,如RectTransform、Image中各种参数修改等;

· 设置材质脏——SetMaterialDirty,如Material、Texture相关修改等;

· 设置布局脏——SetLayoutDirty,如Layout系列组件进行的修改等;

· 设置上面三个都脏——SetAllDirty。

所以说,对于UI,基本上只要动了就会触发Canvas重绘。

一种有效降低Canvas重绘频率的方法就是采取动静分离。理论基础是子Canvas不会导致父Canvas重绘。

但是有一点需要注意,上面提到了Canvas负责UI合批,不同Canvas不能合批,也就是说采取动静分离的话,虽然减少了Canvas重绘,但是增加了批次。所以最早的时候说,在Unity中UI优化的核心问题就是重绘和批处理之间的平衡,原因就在于此。

上面也提到了,Canvas重绘的过程中就包括合并批次的操作,所以说,除了触发Canvas重绘的频率,导致合批开销过大的原因也会导致Canvas重绘的开销过大。


4、CPU生成顶点时间过长。(对UI来说,很多情况下是Text过多从而导致顶点过多)


Text

Unity的Text组件实际上有很多坑。

在了解坑之前,先需要了解Text是如何实现的。

Text在渲染的时候是根据字体将每一个字渲染成一个quad(两个三角形),所以很多情况下,每个字都存在着浪费的透明渲染区域。并且由于字形不同,每一个字的位置和字与字之间的空隙都不一样。这样就导致了下图出现的Overdraw的情况,并且很有可能在不经意之间就打断了合批。

当Text内容修改、enable或是disable的时候,每个字的mesh都将会被重新计算。

除此之外,如果使用Best Fit,那么就需要花费大量时间计算合适字体大小,并且在Unity5.4之前还会出现字体图集被大量填充的问题。

如果使用动态字体,那么就需要面临字体图集的重计算和大小扩展。

如果存在备用字体,那么就需要面临所有备用字体被提前加载完成的内存压力。


Layout

对于UI的Layout相关组件,之前提到了,Layout组件会导致Canvas变脏,并且重新计算子元素的大小和位置。所以说尽可能减少Layout布局组件的使用,可以通过改变锚点anchor和枢纽pivot,来实现自动布局,或者直接靠代码实现。


CPU与GPU瓶颈

对于不同大小的Batch,CPU处理的时间都是一样的。也就是说,假设一个CPU一秒能处理100个Batch,当GPU未达到瓶颈时,Batch越大效率越高;但是当GPU达到瓶颈时,适当地增加Batch数量,减小Batch大小,也许是更好的选择。

这就好比是坐公交车,如果每辆车只坐1-2个人,那么适当增加每辆车的人数,可以有效地提升效率;但如果每辆车坐了1000个人,那么适当地增加公交车数量,将人数平摊一些,才是比较好的做法。


实例补充

非UI对象只要移出了视口,就不会进行批处理了。

而UI对象如果想要移出视口后不进行批处理,则需要将Canvas设置成WorldSpace模式或者Camera模式,而不是Overlay模式。否则一旦使用了Overlay模式,即使移出了视口也仍然会进行批处理。

一旦某个Canvas上存在一个UI在视口内(需要被渲染),则整个Canvas上的所有UI都需要被批处理。所以说要么把Canvas移出去很远(保证Canvas上的所有内容不渲染),要么不移出去(渲染Canvas上的所有内容)。


后记

下面参考文章我读了好几遍,每过一段时间,在不同的认知和技术层面上,每次重新读一遍的收获完全不一样。


参考

https://learn.unity.com/tutorial/optimizing-unity-ui?language=en&courseId=5c87de35edbc2a091bdae346#

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值