打开微信“扫一扫”,打开网页后点击屏幕右上角“分享”按钮
首页 资讯 正文

Unity UI优化小结

作者 游资网 03-05 14:06:13 阅读 2028



最近在做Unity的项目,负责UI相关的工作,学习了一下Unity UGUI更新的原理,以及优化相关的部分。本文主要参考UWA的分享,UWA专注性能优化,感觉有很多值得学习的文章,UWA-简单优化、优化简单,打好理论基础,少走弯路,后面实际项目中就是尽可能去实现这些细节了。


目录


•1.元素更新方式


•2.Draw Call合并规则


•3.网格更新机制


•4.降低界面的渲染开销


•5.降低界面的更新开销


1.元素更新方式


UGUI


public class VertexHelper:IDisposable

{

  private List m_Position = ListPool.Get();

  private List m_Colors = ListPool.Get();

  private List m_Uv0S = ListPool.Get();

  private List m_Uv1S = ListPool.Get();

  private List m_Normals = ListPool.Get();

  private List m_Tangents = ListPool.Get();

  private List m_Indices = ListPool.Get();

}


有这样一个VertexHelper类,和UI元素有一一对应的关系,包含顶点信息,UV,颜色,等等当UI元素发生变化的时候,就会从位置,长宽等数组填充这些list。


对制作的影响


当UI发生改变的时候,须要对数组的元素进行更新,“动态元素”少用Outline,Tiled Sprite尽量减少“动态”长文本



如上图Tiled生成了大量网格,在填充的时候耗时更长。OutLine,是通过把一个四边形重复5次,画出的OutLine的效果,会使文本的定点数乘以5,使更新的数组过长。


更新方式


•UIPanel.LateUpdate


o轮询


o UIPanel.UpdateWidgets


•Cavans.SendWillRenderCanvas


o队列


o m_LayoutRebuildQueue


o m_GraphicRebuildQueue


NGUI每帧更新UIPanel,轮询,不管发生变化与否,哪怕是静态的,还是会有开销


UGUI更新包含2个队列,渲染之前在SendWillRenderCanvas的回掉里面处理2个队列的元素,如果大量静态,消耗几乎为0。


对动态HUD缓存机制的影响


•NGUI


o适量元素:Color.a=0,移出


o大量元素:SetActive(false)


o Time+二级缓存


•UGUI


o Scale=0,Alpha Group=0


如血条,伤害数字,经常会出现消失的UI元素,如果出现就创建,消失就destory,开销会非常大。所以通常的做法通过缓存,如果通过SetActive有时候会有额外的开销,


UGUI通常的操作方式可以通过scale=0,或则Alpha Group为0,可以快速隐藏,不要直接alpha=0,在draw call上是没变化的,实际上还是画了个透明度为0的面片。


NGUI中和UGUI相反,如果设置alpha=0,是会把顶点移除掉,可以减少setActive的开销。


2.DrawCall合并规则


渲染顺序


•NGUI:Depth


o设置depth值,以UIPanel为单位,按照大小进行排序,相同材质进行合并


•UGUI:hierarchy


o重叠检测


o分层合并


存在优势,也有一些问题,UGUI的合并规则是进行重叠检测,然后分层合并。下面的例子中,不同颜色代表不同图集。



第一个图,4种颜色,左边和右边数序相同,蓝色是0层,白色都是1层,这样会分层合批成4个DrawCall。


第二个图,左边的蓝色是0层,右边的黑色是0层蓝色是1层,这种情况下不会合批,所以会是9个drawCall


第三个图,把黑色延长到重叠的地方,黑色同处0层,所以DrawCall又降到了5。


所以在制作UI的时候,须要考虑层级关系,结合UGUI的合批规则,这样可以达到对drawCall的优化,


调试工具


•NGUI:DrawCall tool


•UGUI:Frame debugger


NGUI可以通过DrawCall tool看到多少个三角面,多少个widgets,通过观察widgets的关系,对NGUI层级直接调整,来进行合批。


NGUI使用drawcall tool,通过调整index,把相同材质的放在同一层。


UGUI用frame Debug看每个drawcall绘制了哪些东西,再做调整



对界面的影响


•UGUI


o不规则图标的摆放


o UI元素的旋转


o动态遮挡


o 3D UI


•NGUI


o手动排序


UGUI中,对于不规则图形,视觉上icon没有重叠,但是UI层是包围盒的形式,Icon重叠了,UGUI在判断的时候没办法进行合并。


UGUI对于发生旋转的UI,包围盒是会发生重叠,会限制UGUI在合并DrawCall的操作。


如下图:



NGUI把不同的元素设在一个图集中,进行同批次绘制。


3.网格更新的机制


•UIPanel.LateUpdate两种更新方式


o UIPanel.FillDrawCall更新单个DrawCall


o UIPanel.FillAllDrawCall更新所有DrawCall


•Canvas.BuildBatch更新所有DrawCall


o WaitingForJob子线程网格合并


o PutGeometryJobFence


o BatchRendere.Flush UI如果开多线程渲染,BatChRender.Flush会增高,主线程在等待子线程的结果时Flush会等待。


NGUI根据不同的DrawCall合并不同的网格UGUI以Canvas为单位,一个Canvas下的元素,合并成一个Mesh,不同的UI元素会以SubMeshes的形式存在。UGUI中如果一个Canvas中有很复杂的动态元素,尽量将静态元素拆分出来,确保更新的效率。



优化方法:


•UGUI


o拆分Canvas


•NGUI


o控制FillAllDrawCalls


o拆分UIPanel


性能比较


•功能界面的DrawCall控制NGUI>UGUI(NGUI通过DC树,通过调整Index进行调整)


•功能界面的网格更新机制NGUI>UGUI(UGUI更新任何一个UI,都会更新整个Canvas)


•动态HUD界面的网格更新机制UGUI>>NGUI(UGUI在处理动态UV的元素,如血条,动态UI会更有优势)


•堆内存控制UGUI>>NGUI(NGUI堆内存占用更高)


参考https://blog.uwa4d.com/archives/Implosion.html


4.降低界面的渲染开销


•Profiling定位


•DrawCall控制


•Mesh.CreateVBO UI变化的网格开销


•Overdraw UI比较容易产生Overdraw


Profiling


UGUI非多线程渲染Unity5.3主要集中在RenderSubBatch,



DrawCall控制


Z值!=0


合并时只会合并相邻层级,相同图集的元素


左边的图,4个血条红色和白色的z值相同,共2个drawcall,但是右边的图,红色和白色穿插,变成8个drawcall,在3D UI的时候尤其明显,2DUI不要通过这种方法,改Z值,因为2D改了之后,



未“隐藏”的元素


包含Null Sprite,Color.a=0屏幕外


对于隐藏的元素,NGUI的image组件中,alpha为空和sprite为空,都是占用drawcall渲染的,而且会打断前后的drawcall,穿插在上下2个元素中间的时候。



Hierarchy穿插+重叠


如下图红点和Icon在不同图集中,如果红点稍微大一点,遮挡了旁边的Icon,就不能合批,须要调整Icon和红点的节点关系,4个Icons放在一个节点下,4个红点放在一个借点下。在同步位置的时候可能稍微麻烦有点,须要写个脚本同步位置。




图集分离


可能因为压缩方式的不同,导致UI的sprite在不同图集中,也会影响渲染开销,不同图集中无法进行合批



OverDraw


•减少UI层叠


•遮挡场景时,关闭场景相机


•不用Image检测事件


参考:https://blog.uwa4d.com/archives/video_UI.html


5.降低界面的更新开销


•动静分离


•降低更新频率


•避免“敏感”操作


•优化选项


动静分离


在UGUI中细分Canvas下图中,血量和经验条会经常更新,如果在一个canvas中,PutGeometryJbFence和WaitngForJob,buildBatch出现的时候,表示更新的开销在子线程中,主线程处在一个等待的状态,差不多有5,6毫秒的等待。



拆分之后刚才的WaitingForJob等都没有了,动态的canvas开销就会很小。



降低更新的频率


•设定移动阈值


•设定更新频率



比如像小地图这样的界面,可能移动了一小段距离,小地图上更新了也不明显,可以通过设定阈值的方法,降低开销,或者直接设定更新时间。


避免“敏感”操作


•元素的Position赋值->Canvas.BuildBatch


下面的一个例子是在Canvas中,所有元素基本是静态的,但是有个元素,在Update中,会跟随target的position,每次发送改变的时候,会重建整个canvas,导致资源的浪费。



参考文献:


https://blog.uwa4d.com/


[《聚爆Implosion》性能精析UI部分]


UGUI研究院之全面理解图集与使用


专栏地址:https://zhuanlan.zhihu.com/p/43111806



免责声明:本文来自其他自媒体或独立创作者,不代表本网站的观点和立场。如有侵犯您的版权,请联系我们,我们将及时删除。


Unity UI优化小结


手游研发