recyclerview源码分析_react diff算法原理[通俗易懂]

recyclerview源码分析_react diff算法原理[通俗易懂]重建与合批Rebuild什么是Rebuild布局组件Rebuild图形组件RebuildRebuild是怎么被触发的Rebatch什么是RebatchRebatch是怎么被触发的优化减少Rebuil

Rebuild


什么是Rebuild

当发生一些脏标记的行为时,被标记为脏的对象需要进行重新计算或渲染

图形组件Rebuild

简介
在渲染Canvas前一帧,会去判断Graphic是否被标记为脏标记,如果是顶点脏标记就会去重建网格,如果是材质被标记为脏那么就会去重建材质
具体实现

//重建方法
public virtual void Rebuild(CanvasUpdate update)
{ 
   
    if (canvasRenderer == null || canvasRenderer.cull)
        return;

    switch (update)
    { 
   
        case CanvasUpdate.PreRender:
            //顶点被标记为脏(true)
            if (m_VertsDirty)
            { 
   
                //更新网格
                UpdateGeometry();
                m_VertsDirty = false;
            }
            if (m_MaterialDirty)
            { 
   
                //更新材质
                UpdateMaterial();
                m_MaterialDirty = false;
            }
            break;
    }
}
网格刷新方法
private void DoMeshGeneration()
{ 
   
    if (rectTransform != null && rectTransform.rect.width >= 0 && rectTransform.rect.height >= 0)
        //更新网格数据
        OnPopulateMesh(s_VertexHelper);
    else
        //清除网格数据
        s_VertexHelper.Clear(); // clear the vertex helper so invalid graphics dont draw.

    var components = ListPool<Component>.Get();
    GetComponents(typeof(IMeshModifier), components);
    //https://zhuanlan.zhihu.com/p/340601601 可以看该链接去看这方法的作用
    //简介就是继承该接口的组件(Shadow Outline)也行进行刷新网格信息
    for (var i = 0; i < components.Count; i++)
        ((IMeshModifier)components[i]).ModifyMesh(s_VertexHelper);

    ListPool<Component>.Release(components);
    //将网格数据赋值给workerMesh
    s_VertexHelper.FillMesh(workerMesh);
    //存储数据供渲染
    canvasRenderer.SetMesh(workerMesh);
}
//材质更新代码
protected virtual void UpdateMaterial()
{ 
   
    if (!IsActive())
        return;
	//将数据赋值给了canvasRenderer
    canvasRenderer.materialCount = 1;
    canvasRenderer.SetMaterial(materialForRendering, 0);
    canvasRenderer.SetTexture(mainTexture);
}

布局组件Rebuild

简介
当组件发生一些脏标记行为时,对象就需要进行重建,例如ScrollRect组件的RectTransform组件参数发生了改变就会引发Rebuild

具体实现
拿ScrollRect组件举例,当修改了RectTransform的参数时

protected override void OnRectTransformDimensionsChange()
{ 
   
	//当该对象的RectTransform参数被修改时调用
    SetDirty();
}
//脏标记方法
protected void SetDirty()
{ 
   
    if (!IsActive())
        return;
	//通过LayoutRebuilder类去把该对象收集到CanvasUpdateRegistry类中去重建
    LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
}

Rebuild是怎么被触发的

Graphic对象一共有两种脏标记,以下我只写一些常见引起脏标记行为
顶点被标记为脏

  • 当修改fillAmount的数值时
  • 修改了RectTransform
  • 修改Image 的color
  • 禁用或启用SetActive(两则都会标记为脏)
  • 设置Image的SetNativeSize(两则都会标记为脏)
  • 替换Sprite(两则都会标记为脏)

材质被标记为脏

  • 替换材质
  • 修改RectTransform

ILayoutGroup(ScrollRect,LayoutGroup)对象发生脏标记
布局脏标记

  • Graphic对象(Image)的禁用或激活
  • Text对象的字体大小修改或布局修改

触发收集的脏元素

CanvasUpdateRegistry.cs

//收集所有的布局或图形脏对象
private readonly IndexedSet<ICanvasElement> m_LayoutRebuildQueue = new IndexedSet<ICanvasElement>();
private readonly IndexedSet<ICanvasElement> m_GraphicRebuildQueue = new IndexedSet<ICanvasElement>();

//被注册的方法,被底层调用
private void PerformUpdate()
{ 
   
    UISystemProfilerApi.BeginSample(UISystemProfilerApi.SampleType.Layout);
    CleanInvalidItems();

    m_PerformingLayoutUpdate = true;

    m_LayoutRebuildQueue.Sort(s_SortLayoutFunction);
    var layoutRebuildQueueCount = m_LayoutRebuildQueue.Count;

    for (int i = 0; i <= (int)CanvasUpdate.PostLayout; i++)
    { 
   
        UnityEngine.Profiling.Profiler.BeginSample(m_CanvasUpdateProfilerStrings[i]);

        for (int j = 0; j < layoutRebuildQueueCount; j++)
        { 
   
            var rebuild = m_LayoutRebuildQueue[j];
            try
            { 
   
                if (ObjectValidForUpdate(rebuild))
                	//重建布局元素
                    rebuild.Rebuild((CanvasUpdate)i);
            }
            catch (Exception e)
            { 
   
                Debug.LogException(e, rebuild.transform);
            }
        }
        UnityEngine.Profiling.Profiler.EndSample();
    }

    for (int i = 0; i < layoutRebuildQueueCount; ++i)
        m_LayoutRebuildQueue[i].LayoutComplete();

    m_LayoutRebuildQueue.Clear();
    m_PerformingLayoutUpdate = false;
    UISystemProfilerApi.EndSample(UISystemProfilerApi.SampleType.Layout);
    UISystemProfilerApi.BeginSample(UISystemProfilerApi.SampleType.Render);

    // now layout is complete do culling...
    UnityEngine.Profiling.Profiler.BeginSample(m_CullingUpdateProfilerString);
    ClipperRegistry.instance.Cull();
    UnityEngine.Profiling.Profiler.EndSample();

    m_PerformingGraphicUpdate = true;

    var graphicRebuildQueueCount = m_GraphicRebuildQueue.Count;
    for (var i = (int)CanvasUpdate.PreRender; i < (int)CanvasUpdate.MaxUpdateValue; i++)
    { 
   
        UnityEngine.Profiling.Profiler.BeginSample(m_CanvasUpdateProfilerStrings[i]);
        for (var k = 0; k < graphicRebuildQueueCount; k++)
        { 
   
            try
            { 
   
                var element = m_GraphicRebuildQueue[k];
                if (ObjectValidForUpdate(element))
                	//重建图形元素
                    element.Rebuild((CanvasUpdate)i);
            }
            catch (Exception e)
            { 
   
                Debug.LogException(e, m_GraphicRebuildQueue[k].transform);
            }
        }
        UnityEngine.Profiling.Profiler.EndSample();
    }

    for (int i = 0; i < graphicRebuildQueueCount; ++i)
        m_GraphicRebuildQueue[i].GraphicUpdateComplete();

    m_GraphicRebuildQueue.Clear();
    m_PerformingGraphicUpdate = false;
    UISystemProfilerApi.EndSample(UISystemProfilerApi.SampleType.Render);
}

Rebatch

什么是Rebatch

将多个对象合并渲染
https://zhuanlan.zhihu.com/p/340480771
作用
Rebatch(UI合批),当项目中的Drawcall数量过多时,就需要通过Rebatch来减少UIDrawcall的次数,常用的方法就是图集减少Drawcall的链接教程

Rebatch是怎么被触发的

Unity5.2后Rebatch的操作就放在了多线程操作(具体什么版本不清楚),所以Rebatch被触发的操作也找不到,

优化

减少Rebuild的消耗

引起元素Rebuild的操作就是发生了脏标记行为,所以减少脏标记行为就能减少Rebuild的消耗,当Canvas被标记为脏时就会重新计算(UWA:Canvas下的元素会先合并一个Sub-Mesh,最后合并成一个Canvas为单位的Mesh)

减少Rebatch的消耗

Rebatch会将Canvas下的元素进行提交渲染并缓存,直到Canvas下的某个元素发生了脏标记,那么就会去重新计算,所以减少脏标记的行为就可以减少Rebatch的消耗。

动静分离

Rebatch是以Canvas为单位提交的,所以当出现经常发生脏标记的元素可以单独放在一个Cnavas或Sub-Canvas下。这样可以减少Rebatch的消耗。
在这里插入图片描述

今天的文章recyclerview源码分析_react diff算法原理[通俗易懂]分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/88476.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注