UGUI初级优化探索(Unity)


引语:

  • 作为一个基本上已经摸清了UI开发的方法和步骤,熟悉了工作流程的Unity客户端程序员,这时候想来应该就该略微朝着UI的优化方面进行简单的探索了。
  • 说到优化,在客户端方面应该包括分开为两个方面:1、代码。2、渲染
  • 对于代码来说,无外乎是从代码的设计思路来说,都是那些老一套,算法优化结构优化内存管理等等。这些都需要大量的算法学习与实际开发经验来堆砌,说起来也不是初级开发者能说透的,我还是从渲染表现方面进行轻微的探索总结吧,当然,计算机的渲染还是得回到代码来说。

目的:

??优化Unity的UGUI表现,减少设备渲染压力,提高帧率。


探究方向:

??合批处理


原理分析说明:
??一、首先要简单解释一下什么是渲染。计算机要显示一个图形的过程从根本上来说,就和我们人类画画一样,将图片画在屏幕上,这就是一个渲染过程,它大致可以分为以下几个部分:
??1. 顶点绘制
??2. 连线顶点(画三角形)
??3. 着色计算(算像素点的颜色)
??4. 光影计算
??......
??这些过程的深入探究就很麻烦,不是一个初级客户端程序员必须掌握的。

??二、Unity的图形绘制实质上也是调用了OpenGL或是其他开放图形库提供的接口,那些太过的细节并不需要深究。而在图形绘制过程中,不同的图形一般采用的材质和纹理是不一样的,而计算机在处理这些不同的材质和纹理时,会进行相对来说耗费算力较大的切换操作。为此,将大量相同材质和纹理的对象打包在一起进行渲染的话,就会节省很多传递和切换操作的耗能,这种打包操作其实就可以叫做合批。(当然实际很复杂)
??Unity中提供了三种合批方法:
??1. 静态批处理(Static Batching)
??2. 动态批处理(Dynamic Batching)
??3. GPU处理(GPU Instancing)。
??Unity中默认的策略优先级为:static > GPU > dynamic

??三、Unity的UGUI中,是以Canvas(画布)为单位进行批次生成和渲染的,而Canvas又可以嵌套包含Canvas。合批(Batching)的主要流程如下:
??1. 计算Canvas的alpha
??2. UI层级结构发生变化时,更新Batch的顺序,对Canvas下所有UI元素,按层级结构深度优先排序,生成UI Instructions
??3. 更新所有需要同步数据的renderer UI数据(这里需要注意点是,active为false的UI元素的数据不会同步,也就是不会刷新)
??4. Canvas数据更新时,计算UI Instructions的depth并排序、生成Batch。(数据更新包括:UI层次结构变化,新增、删除UI或UI子节点。所以结合第三点,应该避免频繁删除/增加UI对象,可以换做GameObject.SetActive(false)隐藏即可避免重复刷新batch)

??既然是初步探索,就不深入探究depth的排序计算和细节的合批(batch)处理了,接下来直接进入正题。


优化方法总结:

??1. 尽量避免使用Mask组件,使用Mask至少增加两个DrawCall(CPU将需要渲染图形所需要的元数据提交给GPU的操作),当然更重要的是,这可能导致本来可以batch在一起的UI元素无法batch,分开处理,而导致更多的DrawCall。(由于DrawCall的全过程都是由CPU处理进行的,而目前GPU的发展进度远超CPU,导致CPU现在的性能其实是制约了GPU的能力的,而根据一篇国外大佬的实验结果来说,当DrawCall到达130这个数值时,就开始拖累GPU了,所以得减少DrawCall数量,减轻CPU压力,而适当的将DrawCall一次的数据包内容增多,可以有效利用游刃有余的GPU,这样平衡的处理才是最优解,当然这都是大佬需要深入考虑的。)

??2. 避免在Canvas中,频繁改变层级结构(包括删除/增加UI元素,调整层级结构等),因为层级结构的改变会引起整个Canvas的UI顺序更新,特别是极其复杂的UI树形结构。
??ps:这里就不得不提一个矛盾点了,前面也说了,在Canvas的更新会触发一个depth的深度计算,那么也就是说,如果一个canvas的结构设计的极其复杂(也就是树形结构深度很深),那么这个Canvas如果需要频繁的刷新,就会频繁计算depth,那么耗能可能会极其恐怖,卡到爆也不是不可能。
??那么矛盾点就来了,什么矛盾?当然是分层设计的赏心悦目的UI结构,与对应耗能更多的矛盾了。(尤其对我们这种强迫症来说,井井有条的结构真是让人舒心),不过为了能够更多解放设备的压力,所以Canvas的结构还是尽量扁平化,不要搞分类加节点加深度了。

??3. 避免频繁地动态更新UI元素的Vertex(顶点)、Rect(大小和位置)、Color(颜色)、Material(材质)、Texture(贴图)等,这其实也是和第2点一样,避免Canvas频繁刷新。

??4. 尽可能使用少的材质和贴图,目的是为了让Unity能更多的进行batch操作。

??5. 相邻的UI元素(在Hierarchy中的排列顺序),使用相同材质贴图的UI元素尽可能排在一起,便于Unity进行batch操作。

??6. 同一父节点下所有子节点,保持相同的层级结构,便于底层相同depth下UI元素Batch。(解释见第2点。)

??7. 避免UI元素数目过多和层级结构过于复杂影响Batch更新速度。(解释见第2点)


结语:

??初步探究就到此为止吧,其实还有很多技巧可以总结,但还是得继续探究合批的过程,所以下次另开一文进行总结学习。渲染之路还需要继续深入进行下去,希望能尽快在屏幕上渲染出炫酷的画面效果。