U3D性能优化之UGUI(持续更新)

一、Canvas的合批处理

这是UGUI性能优化中最基础的部分,也是不可忽视的部分。

首先我们需要简单了解一些概念:

批(Batches),也就是每帧的DrawCall次数,我们可以从Game面板的Stats面板查看。

DrawCall,就是每次CPU准备数据并通知GPU的过程。

我们经常听到说要降低DrawCall,DrawCall的次数非常影响性能,为什么影响性能?我们需要先大致了解一下CPU和GPU。

CPU的内部结构异常复杂,因为CPU需要很强的通用性来处理各种不同的数据类型,复杂的逻辑判断以及随时可能出现的中断处理,而GPU则是处于类型高度统一的、相互无依赖的大规模数据和不需要被打断的纯净的计算环境。由图我们可以看出GPU比CPU拥有更多的计算单元和流水线(pipeline)。可以想到,当一堆数据,经由CPU多次少量地通知GPU处理,那么整个过程中,GPU很可能存在许多空闲的流水线,反之,如果经过CPU的批处理,少次多量地通知给GPU,那么GPU所拥有的许多计算单元和流水线才能更多的被利用起来,那么整个过程的性能自然会得到优化。

在了解到合批处理可以有效降低DrawCall后,我们再来说说Canvas。

Canvas(画布)是所有UI控件的父物体,Canvas的子物体也可以添加Canvas(子画布),所有的UI控件都必须在Canvas中才能被绘制出来。同一个Canvas下的UI控件在满足条件时会被合批处理,那么这个条件是什么呢?

①同Canvas下

②同一图集

③同一材质

④同一渲染深度(depth)

⑤同遮罩下或都不处于遮罩下

①②③⑤我就不赘述了,这里着重讲一下④,什么是同一渲染深度(depth)?

在图一中,Image1的后面没有其他需要渲染的东西,所以Image1的depth为0,同理Image2的depth也为0,而Image3是盖在Image2上的,且Image2和Image3不满足②(此时Image2和Image3所用资源并没有打入同一图集),,所以Image2和Image3不能合批,Image3的depth就需要在Image2的基础上加一,Image3的depth为1,此时我们注意到Image1和Image3是满足②的,但是却又不满足④,所以绘制这三张图片时,Batches为3。

在图二中,我们基于图一将Image3稍微移动一下位置,让Image3和Image2不重叠,此时Image1、Image2、Image3的depth都为0,Image1和Image3满足合批条件,会被合批处理,而Image2不满足②,所以Batches为2。

这里简单提一下Unity提供的两个分析批处理的工具"Frame Debugger"&"Profiler-UI",如下图所示为图二中的情况

二、UGUI的动静分离

先简单说明一下什么是动静分离,由于我们的UI可能会一直不变,也可能会时时改变(比如动态切换图片或者改变文字内容,亦或是在一个按钮上添加了呼吸效果等等),那么我们需要把这些不变的UI和会变动的UI分别放在两个Canvas下。

为什么需要将UI动静分离?因为当某个元素发生改变时,该元素会被标记为dirty(脏),会引起整个Canvas下的Rebatch(重合批),Rebatch的过程是很复杂的,性能消耗非常大。

值得一提的是,Unity在5.2版本之后对Canvas.BuildBatch做了优化,在设备的CPU超过一个核心时,Unity会将Canvas.BuildBatch流程放在主线程之外,使用多线程进行计算,由于是在另外的线程里面,所以只要那个线程没有超负荷,那么BuildBatch的消耗就可以忽略不计,我们在主线程查看Profiler也不会发现有问题(难怪之前我查看Profiler时并没有发现动静分离的性能差别)。但是考虑到用户的设备参差不齐,并且即使用户的设备是多核的,BuildBatch会在其他线程里运算,运算过多会使CPU发热,有些设备可能会因此降频,所以UGUI的动静分离还是有意义的。

三、对于频繁作显示隐藏操作的UI对象,不使用SetActive(fasle),而是设置其CanvasGroup的Alpha为0或是Scale为0亦或是设置CanvasRenderer.Cull为True

SetActive操作会清除VBO(顶点缓冲对象,是在显卡存储空间中开辟出的一块内存缓存区,用于存储顶点的各类属性信息,如顶点坐标,顶点法向量,顶点颜色数据等。在渲染时,可以直接从VBO中取出顶点的各类属性数据,由于VBO在显存而不是在内存中,不需要从CPU传输数据,处理效率更高),不仅如此,重新SetActive(true)还会使Canvas强制进行Rebatch和Rebuild。需要注意的是,当你避免使用SetActive的方式后,继承MonoBehaviour的脚本的生命周期。

对于一段不需要改变的字依具体情况使用Image代替Text

由图所示可以看出,同样的一段字,Image只用了2个三脚面和4个顶点,而Text需要8个三角面和16个顶点,但是相应的Image会增加包体大小,所以选用Image或是Text需依具体情况而定。

尽量避免使用Text的outline和shadow

因为outline和shadow的原理是绘制多次再做偏移(outline多绘制4次shadow多绘制1次同时开启会多绘制9次)这会极大的增加三角面和顶点数以及OverDraw如下所示:

​ 

无需射线检测的Image、Text等,将其RaycastTarget关掉

因为Graphic Raycaster会对每一个Raycast Target设为true的组件执行一系列测试,并且将测试通过的组件添加到射线检测命中列表里,这个检测列表越大,必须遍历的层级就越深,每次射线检测的速度越慢,性能自然会受到影响,所以对于一些不必进行射线检测的Image、Text等,将其RaycastTarget关掉。如图所示为Image组件的RaycastTarget

视情况开启OverrideSorting属性

每个Graphic Raycast都会对根节点层级进行从头至尾的遍历,层级中所有拥有Transform的组件都必须经过检查,这样的组件越多,检查的次数越多,那性能自然会更差,而子Canvas中的OverrideSorting属性会使Graphic Raycast测试停止遍历Transform层级。如果启动它不会造成排序或者射线检测等问题,不会影响需求功能,那么就应该勾选它来降低射线进行的层级遍历次数。但需要注意的是,不要为了去勾选OverrideSorting而去添加一个Canvas,Canvas本身就会有性能消耗,下图所示为OverrideSorting的勾选

在构图时,可以将一些装饰性的元素直接切在背景图上

目的是降低对Fill Rate(填充率,是指显卡每帧每秒能够渲染的像素数)的过度使用和减轻Canvas合批处理时的遍历压力以及可以减少透明队列的采样数量,但这样做会增加项目图集的大小,时间和空间,怎么取舍依具体情况而定。下图所示如何分析透明队列的采样数量和OverDraw(就是在某个像素点同一帧内被绘制了多次,被绘制的次数越多越就越亮,最理想的情况是一个像素每次只会被绘制一次,但在多层布局中,不可见部分也会被绘制,这就产生了额外开销)。

九、如果只需要固定矩形遮罩,不需要特殊形状遮罩且只需要一个遮罩的情况下,优先使用RectMask2D

首先我们要简单地了解一下两者实现遮罩的原理:

Mask需要一个Image来当作遮罩区域,子节点在Image的"渲染区域"才会显示
RectMask2D以自身RectTransform为裁剪区域,子节点在"RectTransform区域"内显示

由此看出,在做同一实现时,RectMask2D不需要渲染本身的Image,所以需要的Batches更少,如下两图所示

接着往下说,我们知道RectMask2D是以自身RectTransform作为裁剪区域,所以只能实现固定矩形的遮罩,那又为什么要说"且只需要一个遮罩的情况下",这里又涉及到了我们经常关心的合批处理,依旧是因为RectMask2D是以自身RectTransform作为裁剪区域,所以RectMask2D本身不会产生DrawCall,而Mask在满足合批的条件(处于同Canvas下,同渲染层级(depth), 同材质,同图集,处于同遮罩下或都不处于遮罩下)时,是可以合批的,所以究竟是选用RectMask2D还是选用Mask,需要具体情况具体分析,而不是无脑选用RectMask2D。

图片资源启用Crunch Compression

Crunch Compreesion是将一张图片分成图块,然后进行压缩,压缩的原理类似于顶点缓冲和顶点索引缓冲,它是创建颜色缓冲和颜色缓冲索引,由于一般一张图片会存在相同的色块,那么颜色索引会存在一对多的可能,所以crunch压缩可以减小纹理的大小、节省磁盘空间、加快下载速度。需要注意的是,这种方式是一种有损压缩,我比较推荐的是启用Crunch Compreesion后,Compressor Quality设置为100,这样既能优化一部分性能,又能保证图片质量不会太差。

十一、依具体情况用TMP代替Text

TMP即Text Mesh Pro,它有着许多优势。它使用SDF(Signed Distance Field,即有向距离场作为主要的文本渲染管线),在不同的分辨率和不同的位置下都能渲染出清晰的文本,而且它通过不同的着色器来实现描边,阴影,发光等效果,并且它也能像Text一样进行批处理,表现更出色的同时性能也更好。反观Text,在调整缩放比例或是拉近距离等情况下,会变得模糊,并且在添加outline或是shadow时带来的性能开销是极为不划算的。但是! 但是! 但是! 重要的事情说三遍,使用TMP时,需要我们自行制作一个中文的Font Asset,这会使包体大小增加大概20MB左右(具体增加量依制作Font Asset时添加的字量而定),所以对于一些看重包体大小且没有严重性能问题的项目,还是使用Text最好。如图所示为比例同时放大四倍

十二、大多情况下推荐关闭Canvas的PixelPerfect

PixelPerfect是只有Canvas的RenderMode为Screen类型时才有的选项,它的作用是使UI元素像素对应,边缘清晰不模糊,大多情况下是没有必要启用的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值