性能优化-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);  }
  1. batch过程 合并canvaserender里的mesh,这个过程在其他线程,没有源码,不可见
    1. 根据层次结构(深度优先)拿到所有canvasrenderer
    2. 从下而上遍历List,检测当前renderer和下边的renderer相交
      • 没有相交, depth = 0
      • 相交,其实深度值最大的为depthi,则检测是否能batch,能depth = depthi, 不能depth = depthi+1
    3. 所有renderer根据depth排序,depth相同的根据mat id,再根据texture id
    4. 相邻renderer如果能batch就合批

ui制作规范

  • 原则 rebuild > batch
  • 目标 减少reuild次数,减少rebuild的耗时,减少drawcall
  1. 动静分离 按照不同的更新频率来划分canvas 完全静态的元素放1个canvas 低频率的在1个canvas 同步高频率的在1个canvas 不同步高频率在不同的canvas
  2. 尽可能少用layout
  3. 相同图集,相同字体(相同大小),hierarchy上尽量紧挨
  4. 不要过深的层次结构

设置

  • canvas

    • pixel perfect不要勾选 image
  • image

    • raycast 紧背景图和按钮,输入框 image
  • text

    • rich text有开销,尽量不用
    • best fit不要用 image

ui常见情况处理

  1. 全屏界面,不好处理的地方倾向于多canvas
  2. 多个相同元素,建议挂辅助脚本,在awake的时候复制多个
  3. 循环列表带动画的这种,建议在每个item上加canvas
  4. 道具格建议分成几种,不要全部都用最全的
  5. mask尽量不用

指标

  • 常在主界面 < 40
  • 全屏界面 < 70
  • 非全屏界面< 30

faq

  • 为啥节点不易过多 因为rebuild排序时,是根据父节点的个数

参考

https://blog.csdn.net/lingyun5905/article/details/84755092

猜你喜欢

转载自www.cnblogs.com/marcher/p/12161451.html