Unity UI优化策略


前言

最近学习了Unity的图形渲染和UI的优化部分,感觉还是有挺多东西的。在此做一个简单的总结和记录。

如果把计算机绘制想象成画画,想要加快画画速度,我们可以从几个方面来进行优化:

1、先画背景,再画物体;先画物体,再画背景。(Overdraw)

2、一次知道要画什么东西没,减少画笔换颜料的次数。(Batch / Draw Call)

3、用一个颜料就尽量一次把要画的都画完,免得之后还得再换回来。(提交顺序)

4、减少修改。(Rebuild)


Batch和Draw Call

Batch可以看作是为了绘制物体从而生成对应的渲染命令发送给GPU的一个过程。

Draw Call可以看作是调用渲染命令的一个过程。

Batch在引擎中一般认为是经过打包后的Draw Call,也可以认为就是Draw Call的另一种称呼。

Batch里装的是一堆顶点数据,这一堆顶点数据实际上就是一个物体的Mesh。这一堆数据提交给GPU的过程就是一个批次,即一个Mesh一个批次。(可以看作每个批次只提交一个vertex数组和index数组)

所以我们需要尽可能地合并批次。策略如下:

静态合批

对于使用同一Material的静态/不动/背景物体,可以勾上Static。打包时Unity会自动提取这些静态物体的顶点数据,构建成一个大的顶点数据缓存(形成一个新的大Vertex Buffer/数组和Index Buffer/数组),从而可以看作形成了一个大的Mesh。最终会在一个批次提交这些顶点数据。

之后Unity会根据自己的场景管理系统判断各个子物体的可见性,并调用多次DC分别绘制每个需要绘制的子物体。由于这些物体不再变化,所以绘制命令也不会变化,于是这些绘制命令也会被缓存起来。

也就是说,静态合批可以有效减少Batch次数,但不会减少DC。但是由于这些子物体共享Material,所以渲染状态/绘制命令并没有切换,调用DC时会缓存绘制命令到Command Buffer,还是起到了优化的目的。

由于静态合批的Buffer既增加运行时内存又增加包体,所以还可以采取运行时进行静态合批的策略,用第一次加载的内存和时间换包体大小。

动态合批

对于动态物体,Unity动态合批策略是针对同一材质的物体。通过一次DC来绘制多个物体,从而实现合批。

动态合批是在每帧中进行的,所以是会增加CPU开销的。并且Unity限制动态合批的模型最多只能有900个顶点属性。(顶点属性非顶点)

和静态合批相比,由于不需要预先复制顶点数据,所以内存消耗会小一些。虽然减少了DC,但也增加了CPU开销。

对于UGUI来说,Unity动态合批的底层源码是这样的:

1、对Canvas下所有UI进行深度优先排序。

2、遍历这些深度优先排序的UI:如果该UI不渲染,则depth = -1;如果该UI下面没有其他UI与其重叠,则depth = 0;如果该UI下面只有一个UI与其重叠,并且该UI与这个下面的UI可以合批,则depth = 下面的UI的depth;如果该UI下面只有一个UI与其重叠,并且该UI与这个下面的UI不能合批,depth = 下面的UI的depth + 1;如果该UI下面有多个UI与其重叠,则depth = max(与其重叠的UI的depth) + 1。

3、计算完UI的depth之后,剔除depth == -1的UI,根据depth -> Material ID -> Texture ID -> UI渲染层级高低(这是条件优先级,相同的情况下比较下一个条件)的条件进行排序。depth越小越先渲。

4、根据这个顺序进行渲染,相邻且相同Material的UI可以合批。

(简单概括来说,就是减少重叠,实在是有重叠的话,需要合批的物体重叠高度也尽量一样。高度不一样的时候可以通过垫一个UI来完成合批。)

本质上来说,就是计算出来同depth就可以合批。

GPU Instance

实际上,静态合批和动态合批在某些方面还是有很大弊端的。大场景中的大量静态物体合批后,内存增加会非常大;动态合批的要求苛刻,且有些情况下消耗可能过大,得不偿失。

于是Unity提供了一种强大的功能,GPU Instancing。

即首先静态物体的信息缓存(位置、缩放、光照、UV偏移…),然后将静态物体从场景中剔除。当需要渲染这些静态物体的时候,通过Instance来进行渲染。跟静态合批相比,减少了巨大物体静态合批产生的内存。

节省的原理是:如果使用GPU Instance,就会缓存实例的偏移量,在每次绘制的时候,不再需要传顶点数据给GPU,GPU会根据实例偏移量自行计算并绘制。简而言之,就是规避了合并Mesh导致的内存与性能问题。

优先级:静态合批 > GPU Instancing > 动态合批。只要前一个成功了,后一个就不会再进行了。

SRP Batcher

Unity 2018的可编程渲染管线中包含的批处理器可以大幅提升渲染时的速度。

导致无法合批的注意事项

1、Mask导致无法合批。

2、不同材质无法合批。

3、有lightmap的物体含有额外/隐藏的Material属性,除非lightmap一样,否则无法合批。

4、提交顺序很关键,提交顺序不合理会导致合批被打断。


Overdraw

概念

一个像素被多次重复Rasterization、Shading,导致性能浪费。

对于在UI上优化Overdraw,首先就是注意少用点特殊效果、尽可能合并UI。

UI字体也是按照矩形的方式来渲染的,单纯的文本还好,但加了Outline、Shadow等特效的时候,Overdraw就会大幅增加了。

除此之外,一些透明响应区域对Overdraw影响巨大。把Image转化为Empty4Raycast,使其不参与渲染即可。

对于Sprite网格,可以采用多边形网格,增加顶点数量来减少Overdraw。

还有一种高级策略,即自定义渲染管线,使得UI可以按需以不透明物体的模式绘制,即使其存入Z-Buffer。

简单策略

全屏界面——可关闭后面的场景相机。

半屏界面——可降低场景的分辨率、降低场景相机的更新频率、改成Blur静态背景。这些做法通常需要把场景绘制到RT上。

透明点击区域——可改用empty4raycast、CullTransparentMesh。

Mask组件(有两层Overdraw)——可改用RectMask2D(这个不知道,需要再做研究)。

UI——Sprite使用Tight模式(Quad Mesh 变成了多边形Mesh),Image使用Use Sprite Mesh,配合来完成UI Overdraw减少。

九宫UI——边框图片的九宫拉伸可以去掉 FillCenter。

渲染顺序/提交顺序

不透明物体

Unity等成熟的商业引擎对于不透明物体几乎都是从前往后提交的。这样的话,被遮挡掉的像素,无需渲染。我们看Frame Debugger的时候,可以发现引擎就是这样做的。

Z-Buffer(在着色阶段做遮挡剔除)

HSR(Hidden Surface Removal,在几何阶段做遮挡剔除,节省了光栅化的开销)

透明物体

透明像素的渲染是需要进行颜色叠加算法计算的。也就是说,透明像素需要后面层次的颜色信息。

所以透明物体都是从后往前绘制的。

1、不同相机,按照相机Depth来定,其中Depth数值越小,越先渲染。

2、相同相机,场景透明物体比UI先渲染。

3、相同相机,相同场景当中的物体,Sorting Order越小,越先渲染(看起来距离自己也是越远)。

4、相同相机,相同场景的物体,相同Sorting Order,则Shader或者材质上面设置的Render Queue越小越先渲染。

5、以上条件均相同,则按照物体中心点距离相机的距离来渲染,越远越先渲染。其中这个距离有两种计算方式,如果按照透视方式计算,则按照中心点的深度来计算;如果按照正交方式计算,则按照中心点与相机近裁剪平面垂直距离计算。另外自己也可以自定义计算方式,可以自己在Project Settings - Graphics Settings - Camera Settings - Transparency Sort Mode选择。

UI

UI其实是个很特殊的组件,你可以理解为,一个Canvas就是一个铺满相机的四方片(Screen Space - Overlay / Camera)或者场景当中的一个透明的面片(World Space)。所以其渲染有点类似于透明物体渲染。

UI提交顺序通常按照它在Hierarchy当中Canvas下的节点顺序来定。一般来说,放在上面的先提交,下面的后提交(具体提交顺序见上文动态合批部分)。这和放到场景当中的透明物体有所差异。所以为了实现UI合批,提交顺序/UI层级很关键。

当然也可以添加Canvas组件来人为地打乱这种顺序。

Render Queue

部分物体,可以通过手动修改Render Queue来调整Unity渲染顺序。


Quad Overdraw

概念

一个2*2的像素Quad被多次重复Rasterization、Shading,导致性能浪费。

GPU为了节省效率,一般会使用2*2的像素Quad来进行渲染,而非一个一个像素渲染。当出现一个Quad中不止一个物体时,才需要进行额外处理。

这个额外处理就是Quad Overdraw出现的原因。

假如一个Quad中出现了物体A和物体B,那么在渲染Quad的时候先按照只有A的时候渲染,之后抛弃那些不属于A的像素;然后按照只有B的时候渲染,之后抛弃那些不属于B的像素。这就导致了一个Quad渲染了两次。

因此即使这些物体没有进行遮挡/重合,只要这些物体产生的三角形足够小、足够多,仍会产生Quad Overdraw的问题。

简单策略

复杂物体使用LOD来规范,避免因为大量三角形占据面积小于一个像素带来的Quad Overdraw。


Rebuild

概念

Rebuild指的是Canvas上所有Graphic组件的网络重新被计算。

Canvas负责将它包含的几何体组合成Batch,生成合适的渲染命令发送给Unity图形系统。这个过程在底层的C++代码中完成,这个过程被称为一次rebatch或者一次batch build。当一个Canvas被标记为dirty时,这个Canvas被认为是需要进行一次Rebuild的。

一个子Canvas仅仅是一个嵌套在父Canvas中的组件,子Canvas将它的子物体和它的父Canvas隔离,一个子Canvas下dirty的子物体不会触发父Canvas的Rebuild,反之亦然。

简单策略

1、Canvas动静分离,但需要注意的是不同Canvas上的UI不能合批。

2、需要改颜色时,通过修改Material颜色代替修改UI组件颜色。

归根结底就是防止Canvas变脏。


Raycast

Unity的射线检测这一操作是非常费的,不仅仅是遍历所有接收射线的物体,而且还会进行射线穿透/传递的判断等等一系列操作。

UI上的射线检测会遍历所有开启Raycast Target选项的UI,所以不需要射线检测的UI就最好给它关了。

子Canvas中的OverrideSorting属性将会造成Graphic Raycast测试停止遍历Transform层级。如果启动它不会带来排序或者射线检测的问题,那么就应该使用它来降低射线进行层级遍历的性能成本。(这个没测试,不确定)


Text

UGUI的Text也是一个性能坑。

注意Text在Canvas层级的位置,很多时候Text是导致合批被打断的罪魁祸首。

使用静态字体虽然包大,但是不会重建图集;使用动态字体虽然包小,但是可能会重建图集。

尽量不要使用Best Fit,因为Unity会把每个要显示的独立的字形的每个单独的尺寸渲染进字体图集,所以使用Best Fit的话字体图集将会被迅速的被不同的尺寸的字形所填满。频繁的字体图集重建会迅速降低运行时性能,并导致内存碎片。


Layout

UGUI的各种Layout组件也是非常费的。

因为它们必须在每次被标记为dirty的时候重新计算其子元素的大小和位置。

如果可以的话,可以使用RectTransform的锚点来进行布局,以代替Layout组件。


总结

图形渲染的优化方法有很多很多,想要做好优化,关键就在于得深知底层原理。只有知道它是这么做的,才能够对它进行合适的优化。

学习的道路永无止境。


参考

https://learn.unity.com/tutorial/optimizing-unity-ui#

https://zhuanlan.zhihu.com/p/76562327

https://zhuanlan.zhihu.com/p/76562370

https://zhuanlan.zhihu.com/p/76562384

https://zhuanlan.zhihu.com/p/68530142

Unity UI优化策略相关推荐

  1. UI优化策略-Shader篇

    原文:https://zhuanlan.zhihu.com/p/33458843 前言 优化本身是一件琐碎且耗神的事情,需要经历问题定位.原因探查.优化方案设计和实现.效果验证.资源修改多个步骤,也会 ...

  2. 【厚积薄发】Unity UI 显隐(共存)关系的优化处理方案

    这是第146篇UWA技术知识分享的推送.今天我们继续为大家精选了若干和开发.优化相关的问题,建议阅读时间10分钟,认真读完必有收获. UWA 问答社区:answer.uwa4d.com UWA QQ群 ...

  3. Unity 性能优化:资源篇

    Unity性能优化 大的方面来说,通过Unity对于项目的性能优化大概可以分为下面几个部分: 资源 渲染 程序 项目配置 而在这个部分中,资源的性能优化属于最基础.最有效的优化手段,也是游戏开发者日常 ...

  4. Unity 性能优化(力荐)

    开始之前先分享几款性能优化的插件: 1.SimpleLOD : 除了同样拥有Mesh Baker所具有的Mesh合并.Atlas烘焙等功能,它还能提供Mesh的简化,并对动态蒙皮网格进行了很好的支持. ...

  5. Unity性能优化之编辑器检查——贴图

    优化选项 图片资源一般可做如下优化设置 打包图集 mipmap不必要时选择关闭 Read/Write Enabled不必要时关闭 纹理压缩 图集打包的可以参考Unity性能优化之图集打包:mipmap ...

  6. Unity - 性能优化 - 包体,内存 - 偏静态资源的优化

    文章目录 静态资源优化 - AssetPostprocessor Texture 压缩 Model 网格.动画 压缩 音频压缩 纹理的优化经验 尺寸 通道 发布出来的包资源再次分析 如何工具快速定位静 ...

  7. 常用Unity的优化技巧集锦

    Unity性能优化是面试的时候经常被问道的一些内容,今天给大家分享一些常用的Unity的优化技巧和思路,方便大家遇到问题时候参考与学习. 包体大小优化 游戏的安装包体大小对于游戏开发而言非常重要,因为 ...

  8. Unity手册-优化一(图形性能)

    找出影响图形性能的主要因素 游戏的图形部分主要影响计算机的两个系统:CPU 和 GPU.找到性能问题所在是一切优化的首要法则,因为 GPU 与 CPU 的优化策略大不相同(甚至相反:例如,通常在优化 ...

  9. 第一章:unity性能优化之内存优化

    目录 前言 unity性能优化之内存的优化 一.unity Analysis工具的使用. 二.内存优化方法 1.设置和压缩图片 2.图片格式 3.动画文件 4.模型 5.RenderTexture(R ...

最新文章

  1. 【Linux】Linux简单操作之管道与重定向
  2. vivado----fpga硬件调试 (二)----mark_debug
  3. 【django轻量级框架】在线视频教育系统设计与实现
  4. Tor真的匿名和安全吗?——如果是http数据,则在出口节点容易被嗅探明文流量,这就是根本问题...
  5. 打破技术型思维:产品经理的门槛在门里面
  6. SQL之用户自定义函数
  7. 转:java网络编程-HTTP编程
  8. kdj指标主要看哪个值_悟空CRM:在线crm主要看这两个指标,都非常重要!
  9. Flutter 项目开发指导 从基础入门到精通使用目录
  10. 程序员学历要求越来越高,薪酬天花板犹如发际线,原因很简单!
  11. Spring 整合Mybatis Mapper动态代理方法
  12. Mac如何将DVD转换为MP3格式
  13. 2017:社保再选管理人基金公司争“主力”
  14. Egret引擎的常用倒计时
  15. 软件工程--总体设计过程包括那些步骤---软件设计过程中应该遵循那些基本原理--模块独立性
  16. 弗洛伊德学说中的本我、自我和超我
  17. 工作中提升效率的工具
  18. Python基础语法(五)—常用模块和模块的安装和导入
  19. 卸载wps后office图标丢失变白,系统卡顿
  20. Redis容灾备份的方法

热门文章

  1. 搭建一个直播平台源码,可以选择的流媒体服务器
  2. 朋友高考没考好怎么安慰的话
  3. 转:优秀程序员的十个习惯
  4. 零基础该如何学习UI设计,你的学习方法正确吗?
  5. Delphi下实现全屏快速找图找色
  6. html页面怎么四舍五入,javascript如何四舍五入保留几位小数?
  7. windows10无法正常关机的解决方法
  8. 截杀“WannaCrypt”,终结“永恒之蓝”!
  9. 汇编语言求无符号字数组中的最大偶数
  10. 电脑计算机为什么不是有效程序,Win7打开软件提示不是有效Win32应用程序怎么办...