性能优化-ui rebuild

时间:2020-01-07
本文章向大家介绍性能优化-ui rebuild,主要包括性能优化-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

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

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

ui常见情况处理

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

指标

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

faq

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

参考

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

原文地址:https://www.cnblogs.com/marcher/p/12161451.html