Notifications
Article
Unity UI Profiling:你怎么敢破坏我的批处理?
Updated 13 days ago
1.2 K
5
你花费了大量的时间来优化Unity UI,但只需将某个几乎看不见的UI画布元素属性悄悄地修改这么一点,整个性能就会被搞砸。而在这种情况下,即使Unity UI Profiling都没法拯救你于掉帧之中。前路艰险,你,准备好了吗?
作者:Rubén Torres Bonet,2019年10月21日
原文链接:https://www.gamasutra.com/blogs/RubenTorresBonet/20191023/352664/Unity_UI_Profiling_How_dare_you_break_my_Batches.php

我上个项目中发生了这么个情况……
在把我们项目搬到Oculus Quest中时,我花了大把精力来优化几处UI面板。整个过程基本上就是将重复绘制(Overdraw)降低到一个可接受的次数中,保证GPU能适应实时3D渲染。
我花了至少一个月来优化Unity UI,进展特别不错。
在某些时候,优化是如此之好,以至于GPU处理UI时几乎都没花时间。我使用的不透明UI着色技术很好地补偿了大部分UI Layering(在其它元素上绘制的元素)导致的重复绘制(Overdraw)。
我那时,把混合UI系统优化到了极致,有效地挡住了在其后头绘制的3D元素,方便摒弃这些遮住的部分。
当我挂上Unity UI Profiler时,发现了一件事。
我发现忙不过来的CPU花了整整1毫秒在每帧的UI渲染上。要知道整个游戏的执行只能花上13毫秒:整个包括物理、逻辑、3D渲染、输入、VR、网络等等,对这样一个平台来说这时间简直太多了。
我还看见几次UI消耗了更多的CPU性能。
这就是说:UI优化可以对GPU友好,但并不一定适用于CPU
事实上,CPU和GPU二者在完成Unity UI渲染上有完全不同的任务。正是如此,在之前的博文Unity UI Optimization中,我才会建议以不同的方法针对CPU和GPU进行优化。
在经过更多的Unity UI Profiling后,我发现了问题所在:UI在每一帧都会被重建,就是说,每一帧上都有一次Canvas Rebuild(画布重建)
CPU处理上多个1毫秒……好伤。
实际上没错,Unity缓存了画布,确保构建过程只进行一次。
而当你更改了任一UI元素画布的属性,比如颜色、位置之类的,问题就出现了。
这意味着,所有受人喜爱的动画,如按键悬停效果,会消耗性能,而你却察觉不到。
当UI属性被更改,Unity会执行著名的Canvas Rebuild,碾碎你游戏的性能
每次Canvas Rebuild会更迭所有该画布上的UI元素,生成一个最优的绘制调用列表(包括顶点、颜色、材质等的数据)。而Canvas Rebuild花费的时间比西雅特Panda百公里测试的时间还长。
于是,在发现UI Canvas Rebuild是你的苦难之源后,问题就变成了……
为什么会出现Canvas Rebuild?我该怎么办?
为了回答这个简单直白的问题,我花了5个多小时研究了该话题,又增强了Unity UI Profiler。
来看看我是如何增强Profiler的。

目录

  1. Unity UI Profiling:非常好用,可是……
  2. Unity UI Profiling:一只野生的Canvas Rebuild出现了!
  3. 揪出捣乱者:非政治正确的蛮力方法
  4. 福利:针对UI优化的Unity Profiler强化

1. Unity UI Progiling:非常好用,直到……

假如我们面前摆着个怪怪的UI。
这UI啥都没干,就静静地躺在那,挡在玩家面前,阻止玩家看到后边的东西,让人烦躁。
这个UI是一个使用Grid Layout Group、包含了350多个图片的合集,它长这(鬼)样:
可即使包含了350多张图片,它也没什么问题。这里只有两张不同的图片需要在精灵图上定位绘制,一般用两次绘制调用就能完成。
如果效率够高的话,在分析器的CPU使用上我几乎看不到起伏。大部分情况下时间都低于0.01毫秒,效率非常高。
(……大部分情况下而已)
等下,图标最后的CPU高峰是个什么东西?

2. Unity UI Profiling:一只野生的Canvas Rebuild出现了!

上面Unity Profile图标的最后发生了什么?UI的CPU耗费在一秒之内高了不止一倍,好奇怪。
我想和你玩个游戏
你可在下方两张图表中找出不同(点击图片可以放大查看)。
你有五秒钟的时间来找出不同。
5,4……好吧,给你些提示:
诶呀!
PostLateUpdate.UpdateRectTransform和UGUI.Rendering.UpdateBatches两人看起来要做今天最靓的仔。

这两部分是做什么的?

首先,UpdateRectTransform意味着某个对象的变换被更改了,因此Unity需要运行一些耗费性能的逻辑,以保持视图一致。而我们也不清楚到底是位置、朝向、大小还是其它RectTransform属性被更改了。
见鬼,我们甚至不知道变更的只是一个属性还是所有属性,是单个对象还是多个对象,或者具体是哪个对象。这是个大问题:我们什么都不知道
第二个开销UpdateBatches则是由于整个画布几何形需要被重建才出现的。整个进程便是著名的Canvas Rebuild。每次画布重建意味着Unity将处理一遍所有的画布层级,生成一个绘制调用的列表,可谓麻烦至极。这些顶点、标记、颜色,以及UV的所有元素都将被计算一遍,直到单个批处理通道完成,于是我们就会合并尽可能多的绘制调用,减少将其分发给图形处理器所造成的CPU开销。
那我们现在知道发生了什么,起码知道一点了。路是对的,但是我们又得如何避免这些画布重建命令呢?这些命令的起因是什么?
我们只需要更多的信息……

总结

  • 单个UI元素的某个属性更改会使该元素变“脏”
  • 一个UI元素可能从头到脚都是“脏”的,也可能只是部分“脏”了:“脏”的部分可能是顶点、布局、材质。部分“脏”的状态比较容易恢复
  • 只要有任何元素是“脏”的,Unity就会重建整个画布。
  • 画布重建会消耗许多CPU性能,避免重建是关键

3. 揪出捣蛋鬼:非政治正确的蛮力方法

我们还得寻找下面这个问题的答案:
谁触发了恶心的Unity UI Canvas Rebuild?
然而,想要马上找出答案几乎是不可能的,尤其是当你的画布层级非常巨大时。
在这里,我向你介绍一种揪出导致UI Canvas Rebuild源头的蛮力法

1. 让Unity UI Profiler保持记录状态

设置好筛选条件,专注于真正重要的部分:渲染、脚本和UI。
盯住基准线,注意基准线部分当前开销的图像表示,其中应该包含了耗费较大的Canvas Rebuild。

2. 禁用UI游戏对象,作个对比

选择几个游戏对象,禁用它们。
对比其与之前性能基准线的不同。
如果基准线变化没什么改善,继续禁用游戏对象,直到出现明显的性能提升。

3. 查出那个一直修改属性的对象

现在我们找到了触发Canvas Rebuild的对象了,但到底是什么在触发它们呢?
是有个脚本在缩放吗?还是一个动画改变了位置?
右击RectTransform,按下“Find References in Scene(查找场景内的参照)”,继续寻找答案。
一旦找到UI画布重建的起因后,找个办法处理一下,比如禁用动画或变换。
Ruben,要是我的UI层级超大,这个方法不是太麻烦了嘛?别搪塞我
我说过这个方法又慢又无聊,但是需要有人来修复问题呀。
重点是,一个超大的层级视图本身就并不理想。而正是那些体量巨大、错综复杂的层级会让画布重建吃掉许多CPU性能。
可是巨大的嵌套式UI层级的确(也一定)存在,画布重建便会时不时击中你的痛处:玩家的游戏体验。
虽然蛮力法能找出画布重建的源头,可该方法没法一直用下去。
在学习了更多UI优化的专业知识后,我终于创建出了一个解决工具,可以达到玩家们的期望了……

4. 福利:针对UI优化的Unity Profiler增强

到这里,我已经强调过UI画布重建的发生几率和影响力有多大了。
这些恼人的画布重建染指我的游戏后,会窃取整整10%的CPU性能!
正如我所介绍的,你可以用龟速蛮力法来找出画布重建的源头。然后,如果你看过我的Unity UI Optimization文章(去看看吧,我保证完全免费!),可以用里头的策略处理一下这个问题。
不过,一个真正的Guru不会满足于只用错误导向的方法。你可以花上好几天努力规避画布重建,一旦有疏忽,错误可能会重新出现,然后又在Unity UI Profiler中神秘消失。
规避重建在VR开发中显得更加重要。在虚拟世界中最好不要出现什么UI画布重建。如果不解决这些问题,你的玩家可能会变成眩晕症病人。
懂了,我会处理画布重建的。但是Unity Profiler根本不会告诉我详细信息!你能给我什么建议吗?
很高兴你问了,其实我们可以说服Unity Profiler,给我们提供干涉UI性能因素的具体信息
我们可以更改Unity UI的公开源码,增强Unity UI Profiler的功能。在源码中,你需要找到执行Canvas Rebuild的函数代码,然后,我们只需要加上些BeginSample和EndSample Profiler的爱之API魔法。
如果你运行的是Unity 2019.1或更早的版本,Unity UI源码可在公司的Bitbucket代码存储处找到。你可以遵循他们的指南下载、安装、修改源码。
我的建议?使用更新的Unity版本,至少是2019.2.0。由于UI系统已经属于包管理器的一部分,新版本的Unity默认携带UI的源码。这样一来可少去许多麻烦。
以下是我在调查时找到的几份代码,你可以将其加入到Profiling的API调用中:
  • CanvasUpdateRegistry.cs: 函数PerformUpdate
  • Graphic.cs: 函数SetAllDirty
  • Graphic.cs: 其它函数如SetVerticesDirty、SetMaterialDirty之类的..
有用吗?当然。
对艺术家/设计师友好吗?并不。
这就是为什么我为你写了一个小小的开源Unity拓展程序来增强Unity Profiler。
该免费工具允许你快速在分析模式间切换,确保游戏的性能可达到最佳。
这个增强程式的最棒之处在于可在编辑器之外运行,省去你分析安卓或其它什么平台UI花去的大把精力,防止劳累过度,积劳成疾的可能性。
而你只需点击下面两个按钮,就能掌控它所有的力量:
  • Buff my Unity Profiler(增强我的Unity Profiler)
  • Nerf my Unity Profiler(削弱我的Unity Profiler)

开发过程中遇到问题?在这里提问:connect.unity.com/g/discussion
Tags:
Unity开发者原创
17
Comments
小宇殿下
11 days ago
ZQYUIElements快好了,估计不会再投入精力去优化UGUI了
感谢回复,了解了
0
ZQY
11 days ago
小宇殿下官方什么时候优化下ugui呢?scrollview的性能差得不能再差了
UIElements快好了,估计不会再投入精力去优化UGUI了
1
小宇殿下
12 days ago
官方什么时候优化下ugui呢?scrollview的性能差得不能再差了
0
雨落随风
14 days ago
程序猿
很cool
0
hua1
Staff
14 days ago
😁😌
棒棒哒
0