赞
踩
直接分析Graphic源码不太直观 ,我们从顶层分析Text控件的运作机制这样可以知道Graphic的运作机制了,首先Text继承关系如下
public class Text : MaskableGraphic, ILayoutElement
public abstract class MaskableGraphic : Graphic, IClippable, IMaskable, IMaterialModifier
我们需要分析Text控件何时会刷新,可以肯定地是Graphic使用了脏标记模式(游戏编程
模式里面有这个设计模式),每当Text的一些属性发生改变后不会立即更新属性,而是将这些变化的属性打上标记,并在某一个时刻一起更新。
鉴于这些类比较复杂我们通过一个简单的行为去分析Text内部的运行机制。我们首先考虑当Text控件的内容发生变化时,Text控件到底执行了那些方法。通过下面代码可以看出,当Text控件的内容发生变化时会将顶点(vertex)以及布局(layout)打上标记。
- public virtual string text
- {
- get
- {
- return m_Text;
- }
- set
- {
- if (String.IsNullOrEmpty(value))
- {
- if (String.IsNullOrEmpty(m_Text))
- return;
- m_Text = "";
- SetVerticesDirty();
- }
- else if (m_Text != value)
- {
- m_Text = value;
- SetVerticesDirty();
- SetLayoutDirty();
- }
- }
- }
我们继续分析SetVerticesDirty方法。这个方法跳到Graphic类里面来了,这个方法首先设置标志位,然后CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild 方法会向一个队列中添加一个元素,到这里Text的部分就分析完了。但是不对啊,Text控件的内容什么时候渲染更新呢,我们继续分析CanvasUpdateRegistry
return m_GraphicRebuildQueue.AddUnique(element);
- public virtual void SetVerticesDirty()
- {
- if (!IsActive())
- return;
-
- m_VertsDirty = true;
- CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
-
- if (m_OnDirtyVertsCallback != null)
- m_OnDirtyVertsCallback();
- }
接下来我们分析 CanvasUpdateRegistry 类,他的构造函数如下会向Canvas中的willRenderCanvases静态事件添加一个回调,由于Canvas没有源码,这里我推测performUpdate这个回调会每一帧都执行更新。performUpdate这个回调的功能很简单就是根据从m_GraphicRebuildQueue以及m_LayoutRebuildQueue两个队列中取出元素,并执行每一个元素的Rebuild方法,Rebuild方法会更具不同的枚举类型进行不同的操作。Rebuild完成后执行LayoutComplete()与GraphicUpdateComplete()方法。
- public enum CanvasUpdate
- {
- Prelayout,
- Layout,
- PostLayout,
- PreRender,
- LatePreRender,
- MaxUpdateValue,
- }
-
- protected CanvasUpdateRegistry()
- {
- 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++)
- {
- UnityEngine.Profiling.Profiler.BeginSample(m_CanvasUpdateProfilerStrings[i]);
-
- for (int j = 0; j < m_LayoutRebuildQueue.Count; 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 < m_LayoutRebuildQueue.Count; ++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;
-
- for (var i = (int)CanvasUpdate.PreRender; i < (int)CanvasUpdate.MaxUpdateValue; i++)
- {
- UnityEngine.Profiling.Profiler.BeginSample(m_CanvasUpdateProfilerStrings[i]);
- for (var k = 0; k < m_GraphicRebuildQueue.Count; 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 < m_GraphicRebuildQueue.Count; ++i)
- m_GraphicRebuildQueue[i].GraphicUpdateComplete();
-
- m_GraphicRebuildQueue.Clear();
- m_PerformingGraphicUpdate = false;
- UISystemProfilerApi.EndSample(UISystemProfilerApi.SampleType.Render);
经过上面的分析我们继续来看,Text的Rebuild方法,首先判断脏标记,并执行相应的更新。
- public virtual void Rebuild(CanvasUpdate update)
- {
- if (canvasRenderer == null || canvasRenderer.cull)
- return;
-
- switch (update)
- {
- case CanvasUpdate.PreRender:
- if (m_VertsDirty)
- {
- UpdateGeometry();
- m_VertsDirty = false;
- }
- if (m_MaterialDirty)
- {
- UpdateMaterial();
- m_MaterialDirty = false;
- }
- break;
- }
- }
综上所述,我们可以很清楚的知道Text控件某些属性改变后的更新过程。
1.当某些属性改变时,可能时图像(Graphic)或者布局(layout)Text会进行脏标记,并向 CanvasUpdateRegistry 类中的相应队列中添加一个元素(把Text的引用添加进去,Text继承了ICanvasElement接口)。
2.CanvasUpdateRegistry 每一帧会调用PerformUpadate方法,该方法会遍历队列中的元素,并执行ICanvasElement接口的rebuild方法(Text重写了rebuild方法实际执行的时Text的rebuild方法)
3.最后在Text的rebuild方法中会根据脏标记,去更新Text的一些属性。
到这里Text的更新循环已经分析完毕了,现在继续分析Text是如何更新自己的属性,并且渲染到屏幕上。我们只分析Text的Geometry(几何可以说就是顶点)部分的更新,在rebuild中执行了UpdateGeometry方法,这个方法调用几个方法后,最终执行了DoMeshGeneration
方法。s_VertexHelper是一个工具类的实例,可以帮助生成UI的Mesh。然后DoMeshGeneration方法会执行OnPopulateMesh方法向s_VertexHelper更新Mesh,最后通过CanvasRenderer(可以在unity面板找到)组件进行渲染。
- 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);
- }
-
-
- protected override void OnPopulateMesh(VertexHelper toFill)
- {
- if (font == null)
- return;
-
- // We don't care if we the font Texture changes while we are doing our Update.
- // The end result of cachedTextGenerator will be valid for this instance.
- // Otherwise we can get issues like Case 619238.
- m_DisableFontTextureRebuiltCallback = true;
-
- Vector2 extents = rectTransform.rect.size;
-
- var settings = GetGenerationSettings(extents);
- cachedTextGenerator.PopulateWithErrors(text, settings, gameObject);
-
- // Apply the offset to the vertices
- IList<UIVertex> verts = cachedTextGenerator.verts;
- float unitsPerPixel = 1 / pixelsPerUnit;
- int vertCount = verts.Count;
-
- // We have no verts to process just return (case 1037923)
- if (vertCount <= 0)
- {
- toFill.Clear();
- return;
- }
-
- Vector2 roundingOffset = new Vector2(verts[0].position.x, verts[0].position.y) * unitsPerPixel;
- roundingOffset = PixelAdjustPoint(roundingOffset) - roundingOffset;
- toFill.Clear();
- if (roundingOffset != Vector2.zero)
- {
- for (int i = 0; i < vertCount; ++i)
- {
- int tempVertsIndex = i & 3;
- m_TempVerts[tempVertsIndex] = verts[i];
- m_TempVerts[tempVertsIndex].position *= unitsPerPixel;
- m_TempVerts[tempVertsIndex].position.x += roundingOffset.x;
- m_TempVerts[tempVertsIndex].position.y += roundingOffset.y;
- if (tempVertsIndex == 3)
- toFill.AddUIVertexQuad(m_TempVerts);
- }
- }
- else
- {
- for (int i = 0; i < vertCount; ++i)
- {
- int tempVertsIndex = i & 3;
- m_TempVerts[tempVertsIndex] = verts[i];
- m_TempVerts[tempVertsIndex].position *= unitsPerPixel;
- if (tempVertsIndex == 3)
- toFill.AddUIVertexQuad(m_TempVerts);
- }
- }
-
- m_DisableFontTextureRebuiltCallback = false;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。