[GraphicRegistry]
GraphicRegistry管理所有Graphic,内部通过IndexedSet维护数据
public class GraphicRegistry
{
private readonly Dictionary<Canvas, IndexedSet<Graphic>> m_Graphics = new Dictionary<Canvas, IndexedSet<Graphic>>();
}
internal class IndexedSet<T> : IList<T>
{
//This is a container that gives:
// - Unique items
// - Fast random removal
// - Fast unique inclusion to the end
// - Sequential access
//Downsides:
// - Uses more memory
// - Ordering is not persistent
// - Not Serialization Friendly.
//We use a Dictionary to speed up list lookup, this makes it cheaper to guarantee no duplicates (set)
//When removing we move the last item to the removed item position, this way we only need to update the index cache of a single item. (fast removal)
//Order of the elements is not guaranteed. A removal will change the order of the items.
readonly List<T> m_List = new List<T>();
Dictionary<T, int> m_Dictionary = new Dictionary<T, int>();
}
[Graphic]
仅当OnEnable(),OnCanvasHierarchyChanged(),OnTransformParentChanged() 时触发查找第一个激活启用的Canvas,并以此为key将Graphic注册到GraphicRegistry。
当动画改变时触发SetAllDirty。
在组件激活(IsActive())的情况下,SetLayoutDirty触发LayoutRebuilder.MarkLayoutForRebuild(rectTransform),SetVerticesDirty触发CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this),SetMaterialDirty触发CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this)。
当RectTransform/Color/Material/Parent/Animation改变时会触发重建。
Rebuild主要是几何和材质重建。
public virtual void Rebuild(CanvasUpdate update)
{
if (canvasRenderer.cull)
return;
switch (update)
{
case CanvasUpdate.PreRender:
if (m_VertsDirty)
{
UpdateGeometry();
m_VertsDirty = false;
}
if (m_MaterialDirty)
{
UpdateMaterial();
m_MaterialDirty = false;
}
break;
}
}
protected virtual void UpdateGeometry()
{
if (useLegacyMeshGeneration)
DoLegacyMeshGeneration();
else
DoMeshGeneration();
}
[UpdateGeometry]
UI组件宽高非0才构建VertexHelper数据,各个组件重写OnPopulateMesh构建VertexHelper数据,遍历IMeshModifier组件(描边,投影等)修改VertexHelper数据,最终VertexHelper数据填充到workerMesh(static)然后交给canvasRenderer去渲染。
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);
for (var i = 0; i < components.Count; i++)
((IMeshModifier)components[i]).ModifyMesh(s_VertexHelper);
ListPool<Component>.Release(components);
s_VertexHelper.FillMesh(workerMesh);
canvasRenderer.SetMesh(workerMesh);
}
[UpdateMaterial]
获取材质后,经过挂载的所有IMaterialModifier组件(做一些材质特效)修改后,和mainTexture一起交给canvasRenderer渲染。
/// <summary>
/// Returns the material used by this Graphic.
/// </summary>
public virtual Material material
{
get
{
return (m_Material != null) ? m_Material : defaultMaterial;
}
set
{
if (m_Material == value)
return;
m_Material = value;
SetMaterialDirty();
}
}
public virtual Material materialForRendering
{
get
{
var components = ListPool<Component>.Get();
GetComponents(typeof(IMaterialModifier), components);
var currentMat = material;
for (var i = 0; i < components.Count; i++)
currentMat = (components[i] as IMaterialModifier).GetModifiedMaterial(currentMat);
ListPool<Component>.Release(components);
return currentMat;
}
}
/// <summary>
/// Update the renderer's material.
/// </summary>
protected virtual void UpdateMaterial()
{
if (!IsActive())
return;
canvasRenderer.materialCount = 1;
canvasRenderer.SetMaterial(materialForRendering, 0);
canvasRenderer.SetTexture(mainTexture);
}
[CanvasUpdateRegistry]
CanvasUpdateRegistry管理Layout重建队列和Graphic重建队列
public class CanvasUpdateRegistry
{
private readonly IndexedSet<ICanvasElement> m_LayoutRebuildQueue = new IndexedSet<ICanvasElement>();
private readonly IndexedSet<ICanvasElement> m_GraphicRebuildQueue = new IndexedSet<ICanvasElement>();
}
Canvas在渲染前会调用willRenderCanvases,执行PerformUpdate
Canvas.willRenderCanvases += PerformUpdate;
PerformUpdate执行流程
1.清除队列中空对象和已销毁的对象
2.Layout队列中元素根据其父节点个数排序
3.Layout队列中元素( Prelayout = 0,Layout = 1,PostLayout = 2)逐个Rebuild
4.LayoutComplete
5.ClipperRegistry做Cull裁剪
6.Graphic队列中元素(PreRender = 3,LatePreRender = 4,MaxUpdateValue = 5)逐个Rebuild
7.GraphicUpdateComplete
[ClipperRegistry]
ClipperRegistry维护裁剪者队列,IClipper是裁剪者,IClippable是可裁剪对象,ClipperRegistry
public class ClipperRegistry
{
readonly IndexedSet<IClipper> m_Clippers = new IndexedSet<IClipper>();
public void Cull()
{
for (var i = 0; i < m_Clippers.Count; ++i)
{
m_Clippers[i].PerformClipping();
}
}
}
public interface IClipper
{
void PerformClipping();
}
public interface IClippable
{
GameObject gameObject { get; }
void RecalculateClipping();
RectTransform rectTransform { get; }
void Cull(Rect clipRect, bool validRect);
void SetClipRect(Rect value, bool validRect);
}
[MaskableGraphic]
MaskableGraphic在Graphic基础上实现了被裁剪与遮罩
Cpu裁剪:RectMask2D实现PerformClipping方法,先通过Clipping.FindCullAndClipWorldRect计算出一个最小裁剪矩形(持续每帧计算子节点的裁剪区域),然后遍历所有IClippable去做裁剪,最后触发canvasRenderer.cull处理。
Gpu裁剪:Mask是通过创建新的材质来裁剪。
public abstract class MaskableGraphic : Graphic, IClippable, IMaskable, IMaterialModifier
{
}
public class RectMask2D : UIBehaviour, IClipper, ICanvasRaycastFilter
{
}
public class Mask : UIBehaviour, ICanvasRaycastFilter, IMaterialModifier
{
}