性能优化-ui rebuild
版本记录
日期 | 版本 | 说明 | 作者 |
---|---|---|---|
2019-11-25 | 0.1 | 文档初始版本 | 李俊 |
原理
- rebuild过程
- 正确算出来 canvasrenderer mesh, material
- Graphic rebuild
- Graphic ugui中显示的基类
- Image 图片
- Text 文字
RectTransform.widthGraphic.OnRectTransformDimensionsChangeGraphic.SetVerticesDirtyCanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuildCanvasUpdateRegistry.PerformUpdateGraphic.RebuildGraphic.UpdateGeometry&UpdateMaterialcanvasRenderer.SetMesh&SetMaterial&SetTexturegraph TB A[RectTransform.width] B[Graphic.OnRectTransformDimensionsChange] C[Graphic.SetVerticesDirty] D[CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild] E[CanvasUpdateRegistry.PerformUpdate] F[Graphic.Rebuild] G[Graphic.UpdateGeometry&UpdateMaterial] H[canvasRenderer.SetMesh&SetMaterial&SetTexture] A-->B B-->C C-->D D-->E E-->F F-->G G-->H
- Layout Rebuild
- 计算子节点的recttransfrom
- ILayoutGroup 布局接口
- LayoutGroup 布局基类
- GridLayoutGroup 格子布局
- HorizontalLayoutGroup 水平布局
RectTransform.OnRectTransformDimensionsChangeLayoutGroup.SetDirtyLayoutRebuilder.MarkLayoutForRebuildLayoutRebuilder.MarkLayoutRootForRebuildCanvasUpdateRegistry.TryRegisterCanvasElementForLayoutRebuildLayoutRebuilder.RebuildILayoutElement.CalculateLayoutInputHorizontal&SetLayoutHorizontal&CalculateLayoutInputVertical&SetLayoutVerticalgraph TB A[RectTransform.OnRectTransformDimensionsChange] B[LayoutGroup.SetDirty] C[LayoutRebuilder.MarkLayoutForRebuild] D[LayoutRebuilder.MarkLayoutRootForRebuild] E[CanvasUpdateRegistry.TryRegisterCanvasElementForLayoutRebuild] F[LayoutRebuilder.Rebuild] G[ILayoutElement.CalculateLayoutInputHorizontal&SetLayoutHorizontal&CalculateLayoutInputVertical&SetLayoutVertical] A-->B B-->C C-->D D-->E E-->F F-->G
- 核心代码 CanvasUpdateRegistry.PerformUpdate
private void PerformUpdate()
{
UISystemProfilerApi.BeginSample(UISystemProfilerApi.SampleType.Layout);
CleanInvalidItems();
m_PerformingLayoutUpdate = true;
m_LayoutRebuildQueue.Sort(s_SortLayoutFunction);
for (int i = 0; i <= (int)CanvasUpdate.PostLayout; i++)
{
for (int j = 0; j < m_LayoutRebuildQueue.Count; j++)
{
var rebuild = instance.m_LayoutRebuildQueue[j];
try
{
if (ObjectValidForUpdate(rebuild))
rebuild.Rebuild((CanvasUpdate)i);
}
catch (Exception e)
{
Debug.LogException(e, rebuild.transform);
}
}
}
for (int i = 0; i < m_LayoutRebuildQueue.Count; ++i)
m_LayoutRebuildQueue[i].LayoutComplete();
instance.m_LayoutRebuildQueue.Clear();
m_PerformingLayoutUpdate = false;
// now layout is complete do culling...
ClipperRegistry.instance.Cull();
m_PerformingGraphicUpdate = true;
for (var i = (int)CanvasUpdate.PreRender; i < (int)CanvasUpdate.MaxUpdateValue; i++)
{
for (var k = 0; k < instance.m_GraphicRebuildQueue.Count; k++)
{
try
{
var element = instance.m_GraphicRebuildQueue[k];
if (ObjectValidForUpdate(element))
element.Rebuild((CanvasUpdate)i);
}
catch (Exception e)
{
Debug.LogException(e, instance.m_GraphicRebuildQueue[k].transform);
}
}
}
for (int i = 0; i < m_GraphicRebuildQueue.Count; ++i)
m_GraphicRebuildQueue[i].GraphicUpdateComplete();
instance.m_GraphicRebuildQueue.Clear();
m_PerformingGraphicUpdate = false;
UISystemProfilerApi.EndSample(UISystemProfilerApi.SampleType.Layout);
}
- batch过程 合并canvaserender里的mesh,这个过程在其他线程,没有源码,不可见
- 根据层次结构(深度优先)拿到所有canvasrenderer
- 从下而上遍历List,检测当前renderer和下边的renderer相交
- 没有相交, depth = 0
- 相交,其实深度值最大的为depthi,则检测是否能batch,能depth = depthi, 不能depth = depthi+1
- 所有renderer根据depth排序,depth相同的根据mat id,再根据texture id
- 相邻renderer如果能batch就合批
ui制作规范
- 原则 rebuild > batch
- 目标 减少reuild次数,减少rebuild的耗时,减少drawcall
- 动静分离 按照不同的更新频率来划分canvas 完全静态的元素放1个canvas 低频率的在1个canvas 同步高频率的在1个canvas 不同步高频率在不同的canvas
- 尽可能少用layout
- 相同图集,相同字体(相同大小),hierarchy上尽量紧挨
- 不要过深的层次结构
设置
ui常见情况处理
- 全屏界面,不好处理的地方倾向于多canvas
- 多个相同元素,建议挂辅助脚本,在awake的时候复制多个
- 循环列表带动画的这种,建议在每个item上加canvas
- 道具格建议分成几种,不要全部都用最全的
- mask尽量不用
指标
- 常在主界面 < 40
- 全屏界面 < 70
- 非全屏界面< 30
faq
- 为啥节点不易过多 因为rebuild排序时,是根据父节点的个数
参考
https://blog.csdn.net/lingyun5905/article/details/84755092
原文地址:https://www.cnblogs.com/marcher/p/12161451.html
- 分布式系统唯一ID生成方案汇总
- 操作系统底层技术——CPU亲和性
- AngularJS例子 ng-repeat遍历输出 通过js的splice方法删除当前行
- mongoDB报错Cannot find module '../build/Release/bson'
- 计算机视觉处理三大任务:分类、定位和检测
- Windows下RabbitMQ安装及入门
- 计算机视觉任务:图像梯度和图像完成
- Yarn【label-based scheduling】实战总结(一)
- 配置sonarqube+maven
- Yarn【label-based scheduling】实战总结(二)
- HDFS学习:HDFS机架感知与副本放置策略
- spring cloud 报错Error creating bean with name 'hystrixCommandAspect' ,解决方案
- Spring Security OAuth2 Demo
- SpringBoot学习:整合shiro(身份认证和权限认证),使用EhCache缓存
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Ubuntu中安装MySQL更改默认密码的步骤详解
- linux下用csplit命令分割文件的方法示例
- Linux基础命令之mktemp详解
- CentOS7设置jar应用程序开机启动的方法
- linux jexus服务设置开机启动
- linux如何利用crontab添加定时任务详解
- windows安装openssh并通过生成SSH密钥登录Linux服务器
- SSH设置别名访问远程服务器详细介绍
- Linux检查Swap交换空间的五个命令小结
- seaborn数据总体分布的可视化策略
- 配置 Apache 服务器禁止所有非法域名 访问自己的服务器
- Ubuntu16.04源码安装Mininet
- Kotlin基础学习之Deprecated与Suppress注解使用
- Centos 7下利用crontab定时执行任务详解
- 树莓派无线上网时无屏幕下发现树莓派IP的方法