Runtime类图分析
CanvasUpdateRegistry
管理着两个队列:
private readonly IndexedSet<ICanvasElement> m_LayoutRebuildQueue = new IndexedSet<ICanvasElement>();
private readonly IndexedSet<ICanvasElement> m_GraphicRebuildQueue = new IndexedSet<ICanvasElement>();
m_LayoutRebuildQueue是通过RegisterCanvasElementForLayoutRebuild
和TryRegisterCanvasElementForLayoutRebuild方法添加元素。
m_GraphicRebuildQueue是通过RegisterCanvasElementForGraphicRebuild
和TryRegisterCanvasElementForGraphicRebuild方法添加元素。
二者通过UnRegisterCanvasElementForRebuild移除注册元素。
在构造函数中向Canvas的willRenderCanvases 事件注册了更新函数(PerformUpdate),以用来响应重建。
Canvas.willRenderCanvases += PerformUpdate;
Canvas在渲染前会调用willRenderCanvases,即执行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);
}
流程如下:
首先调用 CleanInvalidItems删除不可用(为null或者IsDestroyed)的元素。
布局更新开始设置标识位m_PerformingLayoutUpdate为true。
对m_LayoutRebuildQueue依据父对象的数量进行排序:m_LayoutRebuildQueue.Sort(s_SortLayoutFunction)。
分别以PreLayout,Layout,PostLayout的参数顺序调用每一个元素的Rebuild方法。
其中在Rebuild之前需要判断这个元素是否有效(不为空且为Unity的Object类型)。
调用所有元素的LayoutComplete方法。
清除布局重建序列中的所有元素m_LayoutRebuildQueue.Clear()。
布局更新结束设置标志位m_PerformingLayoutUpdate = false。
完成布局后,调用组件的修剪方法ClipperRegistry.instance.Cull()。
图形更新开始设置标识位m_PerformingGraphicUpdate为true。
以PreRender,LatePreRender,MaxUpdateValue的参数顺序调用每一个元素的Rebulid方法。
其中在Rebuild之前需要判断这个元素是否有效(不为空且为Unity的Object类型)。
调用所有元素的GraphicUpdateComplete方法。
清除图形重建序列中的所有元素m_GraphicRebuildQueue.Clear()。
图形更新结束设置标志位m_PerformingGraphicUpdate = false。
CanvasUpdate
定义重建时的优先级顺序:
public enum CanvasUpdate
{
/// <summary>
/// Called before layout.
/// </summary>
Prelayout = 0,
/// <summary>
/// Called for layout.
/// </summary>
Layout = 1,
/// <summary>
/// Called after layout.
/// </summary>
PostLayout = 2,
/// <summary>
/// Called before rendering.
/// </summary>
PreRender = 3,
/// <summary>
/// Called late, before render.
/// </summary>
LatePreRender = 4,
/// <summary>
/// Max enum value. Always last.
/// </summary>
MaxUpdateValue = 5
}
ICanvasElement
需要在Canvas绘制更新调用前重建的元素接口。
/// <summary>
/// This is an element that can live on a Canvas.
/// </summary>
public interface ICanvasElement
{
/// <summary>
/// Rebuild the element for the given stage.
/// </summary>
/// <param name="executing">The current CanvasUpdate stage being rebuild.</param>
void Rebuild(CanvasUpdate executing);
/// <summary>
/// Get the transform associated with the ICanvasElement.
/// </summary>
Transform transform { get; }
/// <summary>
/// Callback sent when this ICanvasElement has completed layout.
/// </summary>
void LayoutComplete();
/// <summary>
/// Callback sent when this ICanvasElement has completed Graphic rebuild.
/// </summary>
void GraphicUpdateComplete();
/// <summary>
/// Used if the native representation has been destroyed.
/// </summary>
/// <returns>Return true if the element is considered destroyed.</returns>
bool IsDestroyed();
}
Rebuild实际的重建方法。
LayoutComplete在布局重建完成后回调。
GraphicUpdateComplete在图形重建完成后回调。
IsDestroyed判断当前元素是否已经不需要参与重建了。
ClipperRegistry
内部维护着一个IClipper接口的列表
readonly IndexedSet<IClipper> m_Clippers = new IndexedSet<IClipper>();
提供Register方法加入一个IClipper到此列表中,
Unregister移除m_Clippers 列表中的指定IClipper,
在Cull方法中遍历此列表,分别调用它们的PerformClipping方法。
脏标记
标记延迟执行,优化重新渲染的手段。
详情请见:游戏设计模式:https://gpp.tkchu.me/dirty-flag.html
例如在Graphic 中存在三种脏标分别代表三种等待重建。
尺寸改变时(RectTransformDimensions):LayoutRebuild 布局重建;
尺寸、颜色改变时:Vertices to GraphicRebuild 图像重建;
材质改变时:Material to GraphicRebuild 图像重建。
层级改变(OnTransformParentChanged)、应用动画属性(OnDidApplyAnimationProperties) :All to Rebuild 重建所有,包括布局重建和图像重建。