重建与合批
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