第七课 Unity编辑器创建的资源优化_UI篇(UGUI)

上期我们学习了简单的Scene优化,接下来我们继续编辑器创建资源的UGUI优化

UI篇(UGUI)

优化UGUI应从哪些方面入手?

可以从CPU和GPU两方面考虑,CPU方面,避免触发或减少Canvas的Rebuild和Rebatch,减少Drawcall,减少CPU处理顶点的时间;GPU方面,降低Overdraw,缩小纹理大小。

Canvas的Batch构建过程(The Batch building process),简称Rebatch


Batch 构建过程是指Canvas通过结合网格绘制它所承载的UI元素,生成适当的渲染命令发送给Unity图形流水线。Batch的结果被缓存复用,直到这个Canvas被标为dirty,当Canvas中某一个构成的网格改变的时候就会标记为dirty,这个Dirty就会触发Rebatch。

Rebatch不仅有批处理排序,还有网格合并之类的。Canvas的网格从那些Canvas下的CnavasRenderer组件中获取,但不包含任何子Canvas。

所以对于UGUI的性能分析,要分开两点

Canvas的批处理过程 Rebatch
Graphic 和Layout的 Rebuild
这两点,都会影响性能。但是Rebatch是有多线程的加持的,而Rebuild是在主线程的。

Rebuild自身会有性能消耗,同时Rebuild会触发Rebatch。
Rebatch除了被Rebuild触发,还会被其他情况触发。
Rebatch的性能上的问题,在电脑上比较难看的出来,因为有多线程加持。


Rebuild过程

Rebuild 过程是指Layout和Graphic组件的网格被重新计算,这是在CanvasUpdateRegistry类中执行的。这是一个C#类,打开UI的源码,它里面的一个函数,叫做PerformUpdate,当一个Canvas组件触发它的WillRenderCanvases事件时,这个方法就会被执行。这个事件每帧调用一次,这也是为什么我们看Profile的时候,出现性能高峰的总是会看到WillRenderCanvases的原因了。

PerformUpdate的运行过程分3步:

按顺序遍历调用Dirty的Layout组件的Rebuild函数
要求任何已注册的裁剪组件(例如Mask),剔除所有裁剪的组件。这是通过ClippingRegistry.Cull完成的。
按顺序遍历调用Dirty的Graphic组件的Rebuild函数


Rebuild分为 Layout Rebuild 和 Graphic Rebuild

Layout Rebuild
要重新计算一个或多个Layout组件中包含的组件的适当位置(和可能的大小),必须按其适当的层次结构顺序应用Layouts。在GameObject层次结构中靠近根部的布局可能会更改嵌套在其中的任何布局的位置和大小,因此必须首先进行计算。
为此,UGUI根据层次结构中的深度对dirty的Layout组件列表进行排序。层次结构中较高的Layout(即,父节点较少)将被移到列表的前面。
然后,排序好的Layout组件的列表将被rebuild,在这个步骤Layout组件控制的UI元素的位置和大小将被实际改变。

Graphic Rebuild
当Graphic组件被rebuild的时候,UGUI会将控制权传递给ICanvasElement接口的Rebuild方法。Graphic执行了这一步,并在rebuild过程中的PreRender阶段运行了两个不同的rebuild步骤:

如果顶点数据已标记为Dirty(例如,当组件的RectTransform的大小更改时),则将重新构建网格。
如果将材质数据标记为Dirty(例如,当更改组件的材质或纹理时),则将更新附加的Canvas Renderer的材质。
Graphic的Rebuild不会按照Graphic组件的特殊顺序进行,并且不需要任何排序操作。

Rebatch和Rebuild的触发条件总结
 

触发Rebatch的条件:

当Canvas下有Mesh发生改变时,如:
SetActive
Transform属性变化
Graphic的Color属性变化(改Mesh顶点色)
Text文本内容变化
Depth发生变化
触发Rebuild的条件:

Layout修改RectTransform部分影响布局的属性
Graphic的Mesh或Material发生变化
Mask裁剪内容变化

Unity UI性能的四类问题

1. Canvas Re-batch 时间过长
2. Canvas Over-dirty, Re-batch次数过多
3. 生成网格顶点时间过长
4. Fill-rate overutilization

Canvas画布

Canvas负责管理UGUI元素,负责UI渲染网格的生成与更新,并向GPU发送DrawCall指令。

Canvas Re-batch过程(合批)

1. 根据UI元素深度关系进行排序
2. 检查UI元素的覆盖关系
3. 检查UI元素材质并进行合批

合批对比

UGUI渲染细节

  • UGUI中渲染是在Transparent半透明渲染队列中完成的,半透明队列的绘制顺序是从后往前画,由于UI元素做Alpha Blend,我们在做UI时很难保障每一个像素不被重画,UI的Overdraw太高,这会造成片元着色器利用率过高,造成GPU负担。
  • UI SpriteAtlas图集利用率不高的情况下,大量完全透明的像素被采样也会导致像素被重绘,造成片元着色器利用率过高;同时纹理采样器浪费了大量采样在无效的像素上,导致需要采样的图集像素不能尽快的被采样,造成纹理采样器的填充率过低,同样也会带来性能问题。如下图

Re-Build过程(重构)

1.在WillRenderCanvases事件调用PerformUpdate::CanvasUpdateRegistry接口

通过ICanvasElement.Rebuild方法重新构建Dirty的Layout组件

通过ClippingRegistry.Cullf方法,任何已注册的裁剪组件Clipping Compnents(Such as Masks)的对象进行裁剪剔除操作

任何Dirty的 Graphics Compnents都会被要求重新生成图形元素

2.Layout Rebuild

UI元素位置、大小、颜色发生变化

优先计算靠近Root节点,并根据层级深度排序

3.Graphic Rebuild

顶点数据被标记成Dirty

材质或贴图数据被标记成Dirty

使用Canvas的基本准则

  1. 将所有可能打断合批的层移到最下边的图层,尽量避免UI元素出现重叠区域,尤其是一些很小的ui元素,比如字体,是一个很小的矩形,容易被忽略,但是每一帧都会导致重新合批
  2. 可以拆分使用多个同级或嵌套的Canvas来减少Canvas的Rebatch复杂度
  3. 拆分动态和静态对象放到不同Canvas下,来避免动态UI元素导致每帧都要做太复杂的canvas Rebatch操作
  4. 不使用Layout组件,这样就不会有太多的rebuild 的过程了,同样可以减少Rebatch的时间消耗
  5. Canvas的RenderMode尽量Overlay模式,减少Camera调用的开销

 UGUI射线(Raycaster)优化

  1. 必要的需要交互UI组件才开启“Raycast Target”
  2. 开启“Raycast Targets”的UI组件越少,层级越浅,性能越好
  3. 对于复杂的控件,尽量在根节点开启“Raycast Target”
  4. 对于嵌套的Canvas,OverrideSorting属性会打断射线,可以降低层级遍历的成本 

UGUI优化思路 

1.Canvas 子节点动静分离

在UI Canvas 中进行子节点动静分离,主要是将频繁变动的UI元素,如动态文本动画图标与相对静态的元素,如背景图,固定按钮分开管理。这样可以减少不必要的重绘,提高性能。通常将动态节点放在一个单独的Canvas或者层级下。在需要更新时只重绘该部分,避免影响影响静态元素。

2.Sprite Packer

是将多个小的UI精灵纹理合并成一个大纹理图集的工具,这有助于减少纹理加载次数和内存占用,提高渲染性能

3.运行时动态合并图集

当然UI运行时动态合并图集性能,

1.减少合并频率

2.合并前筛选出真正需要合并的元素,避免不必要的操作

3.尽量采用多线程技术来处理合并任务,防止阻塞主线程

4.对图集大小合理限制,防止内存占用过大

4.自动布局组件 

不要使用Layout组件,Layout组件性能消耗相对昂贵,会大大地增加Canvas.SendWillRenderCanvases函数耗时,利用好RectTransform同样可实现简单布局。

5.动态加载和裁剪

对于UI动态加载,可以采用对象池来管理UI元素,避免频繁的实例化和销毁,在裁剪方面,设置合理的裁剪区域,减少不必要的计算,同时利用层级结构和遮挡关系,减少渲染压力。

6.组件中自定义材质球打断合批

在UI组件中,自定义的材质球打断合批主要是因为其改变了渲染状态,当材质球的shader,纹理等属性与其他UI组件不同时,就无法合批渲染,可以通过统一材质属性或者使用相同的材质模板来减少这种打断,提高渲染效率

7. Raycat Target

1.只需要在交互的UI元素上勾选,如按钮等。

2.使用射线检测范围更小的组件替换

3.将不需要检测的UI元素放在不勾选Raycat Target的父物体下,利用层级关系减少不必要的检测

8.不可见元素 Cull Transparent Mesh

对于UI中不可见元素,使用Cull Transparent Mesh(剔除透明网格)可以提高性能,它通过不渲染这些不可见的透明元素,减少了渲染计算量和内存占用,一般在合适的渲染管线设置中开启此功能即可

9.UI 字体(TTF和OTF)

1.选择简洁的字体,减少复杂笔画

2.使用字体图集,合并常用字符纹理

3.根据需要动态加载字体资源

4.避免字体框重叠,造成合批打断

5.字体网格重建,UIText组件发生变化时,父级对象发生变化时,UI组件或其父对象enable和disable时

谨慎使用Text的Best Fit选项,虽然这个选项可以动态的调整字体大小以适应UI布局而不会超框,但其代价是很高的,一方面是适配本身在调整文本框大小时有CPU耗时开销,另一方面每个字号下新生成的字都会在Font Texture上占用一个字的大小,容易导致Font Texture过大(这个类似图集,当Font Texture当前大小放不下时才会占用更多内存)。这个特定问题已在 Unity 5.4 中得到纠正,Best Fit 不会不必要地扩展字体的纹理图集,但仍然比静态大小的文本慢得多。

10.计时器和倒计时,触发UI重绘

11.Sprite 九宫格设置

如果是较大的背景图的UI元素建议也要使用Sprite的九宫格拉伸处理,充分减小UI Sprite大小,提高UI Atlas图集利用率

12.GameObject Active/Deactive -> UI CanvasGroup Alpha

13.Canvas Pixel Perfect

谨慎使用Canvas的Pixel Perfect选项,该选项会使得ui元素在发生位置变化时,造成layout Rebuild。(比如ScrollRect滚动时,如果开启了Canvas的pixel Perfect,会使得Canvas.SendWillRenderCanvas消耗较高)

14.shadow 顶点和面数翻倍

15.outline 顶点和面数翻倍

慎用自带组件Outline和Shadow,都是通过重复绘制多个Mesh实现的,其中Shadow绘制为原文本Mesh的2倍,而Outline为5倍,对渲染面数、顶点数,BuildBatch和SendWillRenderCanvases的耗时,Overdraw都有影响。如经常用,可考虑其它方式,如TextMeshPro,或把阴影和描边做到字体里。

16.元素 position Z值不为 0 打断合批

17.Mask vs RectMask2D

Mask依赖Image组件,占用两个Batch,多一倍Overdraw,可以裁剪任意形状。
RectMask2D不依赖Image组件,不占用Batch,没有Overdraw,只能裁剪规则形状。
因此,一般情况下,规则的裁剪尽量用RectMask2D代替Mask,特别是在使用ScrollRect时。
RectMask2D一定比Mask好吗?并不是,Mask间是可以合批的,而RectMask2D间不行,因此当要使用多Mask时,如背包界面中的道具格子,每个格子有裁剪需求时,尽量用Mask,Mask可合批,而RectMask2D会导致合批被打断。

因此:

  • 当一个界面只有一个Mask,那么RectMask2D优于Mask;
  • 当有两个Mask,那么两者差不多;
  • 当大于两个Mask,那么Mask优于RectMask2D。

18.避免多层 激活UI元素堆叠

19.加载页/活动图等几乎充满屏幕的UI,关闭场景相机渲染,或渲染一张RT模糊后作背景

20.文本组件建议使用Text Mesh Pro 

21.界面操作一般会触发UI的开关或者隐藏显示,Active 和DeActive,必然会造成UI重建。而采用控制Canvas组件的激活与关闭。

22.不需要参与点击事件的Canvas取消激活Graphic Raycaster 脚本。

射线检测遍历所有将'Raycast Target'设置为true的Graphic组件。每一个Raycast Target都会被进行测试。如果一个Raycast Target通过了所有的测试,那么它就会被添加到“被命中”列表中。
每个Graphic Raycaster都将遍历 Transform层次结构一直到根,此操作的成本与层次结构的深度成比例线性增长。因此进行射线检测的元素越多,层级越深,消耗越高。
鉴于所有射线检测目标都必须由Graphic Raycaster进行测试,因此最好的做法是仅在必须接收点击事件的UI组件上启用'Raycast Target'设置。检测目标列表越少,遍历的层级越浅,每次射线检测的速度越快。

23.持续性的UI动态效果特效,最好采用特效的方式制作,脱离UI系统。

24.动态合静态的UI 要分开,分别挂上canvas。

25. 在使用非全屏但模态对话框时,建议使用**OnDemandRendering**接口,对渲染进行降频。

26.优化裁剪UI Shader,根据实际使用需求移除多余特性关键字。

27.滚动视图Scroll View优化

  •     使用RectMask2d组件裁剪
  •     使用基于位置的对象池作为实例化缓存

这篇文章是摘取自多个大神的文章,所以会有知识点重复的情况,但是我主张的是可以重复,但是知识点一定要全!!!

今天是2024年11月30日

重复一段毒鸡汤来勉励我和你

你的对手在看书

你的仇人在磨刀

你的闺蜜在减肥

隔壁的老王在练腰

而你在干嘛?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/483549.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微服务搭建----springboot接入Nacos2.x

springboot接入Nacos2.x nacos之前用的版本是1.0的,现在重新搭建一个2.0版本的,学如逆水行舟,不进则退,废话不多说,开搞 1、 nacos2.x搭建 1,首先第一步查询下项目之间的版本对照,不然后期会…

Node.js 实战: 爬取百度新闻并序列化 - 完整教程

很多时候我们需要爬取一些公开的网页内容来做一些数据分析和统计。而多数时候,大家会用到python ,因为实现起来很方便。但是其实Node.js 用来爬取网络内容,也是非常强大的。 今天我向大家介绍一下我自己写的一个百度新闻的爬虫,可…

Flink四大基石之State(状态) 的使用详解

目录 一、有状态计算与无状态计算 (一)概念差异 (二)应用场景 二、有状态计算中的状态分类 (一)托管状态(Managed State)与原生状态(Raw State) 两者的…

底部导航栏新增功能按键

场景需求: 在底部导航栏添加power案件,单击息屏,长按 关机 如下实现图 借此需求,需要掌握技能: 底部导航栏如何实现新增、修改、删除底部导航栏流程对底部导航栏部分样式如何修改。 比如放不下、顺序排列、坑点如…

如何在 Firefox 中清除特定网站的浏览历史记录

以下,我将介绍如何清除特定网站的浏览历史记录。清除历史记录可以保护隐私,特别是在公共或共享设备上使用时,还能节省设备存储空间,避免浏览历史占用过多内存。 如何清除特定网站的浏览历史记录 在 Firefox 中,清除特…

SpringMVC(二)

Model 以Map方式进行存储,用于向作用域中存值。 注意:在Model中增加模型数据,若不指定key,则默认使用对象的类型作为key Controller //控制器类 public class IndexController {RequestMapping("/index3")public Strin…

ABE 中的隐藏属性:DIPPE(去中心化内积谓词加密)

1. 引言 相关论文有: Yan Michalevsky 和 Marc Joye 2018年论文 Decentralized policy-hiding ABE with receiver privacy,发表于23rd European Symposium on Research in Computer Security, ESORICS 2018。Amit Sahai 和 Brent Waters 2005年论文 Fu…

计算机网络——不同版本的 HTTP 协议

介绍 HTTP,即超文本传输协议(HyperText Transfer Protocol),是应用层的一个简单的请求-响应协议,它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。本文将介绍 HTTP 协议各个版本。 HTTP/1.0 HTTP/1…

Linux——基础命令(2) 文件内容操作

目录 ​编辑 文件内容操作 1.Vim (1)移动光标 (2)复制 (3)剪切 (4)删除 (5)粘贴 (6)替换,撤销,查找 (7&#xff…

嵌入式硬件实战提升篇(三)商用量产电源设计方案 三路电源输入设计 电源管理 多输入供电自动管理 DCDC降压

引言:本文你能实际的了解到实战量产产品中电源架构设计的要求和过程,并且从实际实践出发搞懂电源架构系统,你也可以模仿此架构抄板到你自己的项目,并结合硬件篇之前的项目以及理论形成正真的三路电源输入设计与开发板电源架构块供…

30分钟学会正则表达式

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。 作用 匹配 查看一个字符串是否符合正则表达式的语法 搜索 正…

如何手搓一个智能激光逗猫棒

背景 最近家里的猫胖了,所以我就想做个逗猫棒。找了一圈市场上的智能逗猫棒,运行轨迹比较单一,互动性不足。 轨迹单一,活动范围有限 而我希望后续可以结合人工智能物联网,通过摄像头来捕捉猫的位置,让小…

【C语言】递归的内存占用过程

递归 递归是函数调用自身的一种编程技术。在C语言中,递归的实现会占用内存栈(Call Stack),每次递归调用都会在栈上分配一个新的 “栈帧(Stack Frame)”,用于存储本次调用的函数局部变量、返回地…

Bert+CRF的NER实战

CRF(条件随机场-Conditional Random Field) 原始本文:我在北京吃炸酱面 标注示例(采用BIO标注方式): 我O在O北B-PLA京I-PLA吃O炸B-FOOD酱I-FOOD面I-FOOD CRF: 目的:提出一些不可能…

pycharm链接neo4j数据库(简单)

1.安装pycharm 2.安装库 pip install py2neo -i https://pypi.tuna.tsinghua.edu.cn/simple 3.代码试运行 from py2neo import Graph, Node, Relationship# 连接到Neo4j数据库,使用Bolt协议 graph Graph("bolt://localhost:7687", auth("neo…

故障诊断 | Transformer-LSTM组合模型的故障诊断(Matlab)

效果一览 文章概述 故障诊断 | Transformer-LSTM组合模型的故障诊断(Matlab) 源码设计 %% 初始化 clear close all clc disp(此程序务必用2023b及其以上版本的MATLAB!否则会报错!) warning off %

flask的第一个应用

本文编写一个简单的实例来记录下flask的使用 文章目录 简单实例flask中的路由无参形式有参形式 参数类型不同的http方法本文小结 简单实例 flask的依赖包都安装好之后,我们就可以写一个最简单的web应用程序了,我们把这个应用程序命名为first.py: from fl…

jmeter 压测常用静默参数解释应用

简介: JMeter静默压测(即无界面压测)是一种常用的性能测试方法,用于模拟多个用户同时访问系统并测量系统的响应时间和吞吐量等关键性能指标。在JMeter静默压测中,常用的压测参数及其解释如下: 一、基本…

《Python基础》之Pandas库

目录 一、简介 二、Pandas的核心数据结构 1、Series 2、DataFrame 三、数据读取与写入 1、数据读取 2、数据写入 四、数据清洗与处理 1、处理缺失值 2、处理重复值 3、数据转换 五、数据分析与可视化 1、统计描述 2、分组聚合 3、数据可视化 六、高级技巧 1、时…

【C语言】结构体(四)

本篇重点是typedef关键字 一,是什么? typedef用来定义新的数据类型,通常typedef与结构体的定义配合使用。 简单来说就是取别名 ▶ struct 是用来定义新的数据类型——结构体 ▶ typedef是给数据类型取别名。 二,为什么&#xf…