UIGU source code analysis 12: Graphic

Source code 12: Graphic

Grahic is the core component of UGUI, responsible for displaying images, and inherits UIBehaviour and ICanvasElement. It is an abstract class and is the base class of MaskableGraphic (maskable image), which is the base class of RawImage, Image and Text

GraphicRegistry

The main function of GraphicRegistry is to record the activated Graphic on each Canvas canvas.

public class GraphicRegistry
{
	private static GraphicRegistry s_Instance;
	
	//每个Canvas对应一个IndexedSet,这个数据结构能够保证元素唯一且有序。
	private readonly Dictionary<Canvas, IndexedSet<Graphic>> m_Graphics = new Dictionary<Canvas, IndexedSet<Graphic>>();
	//可进行射线检测的
    private readonly Dictionary<Canvas, IndexedSet<Graphic>> m_RaycastableGraphics = new Dictionary<Canvas, IndexedSet<Graphic>>();
    
    
    protected GraphicRegistry()
    {
    // Avoid runtime generation of these types. Some platforms are AOT only and do not support
    // JIT. What's more we actually create a instance of the required types instead of
    // just declaring an unused variable which may be optimized away by some compilers (Mono vs MS).

// See: 877060

		System.GC.KeepAlive(new Dictionary<Graphic, int>());
		System.GC.KeepAlive(new Dictionary<ICanvasElement, int>());
		System.GC.KeepAlive(new Dictionary<IClipper, int>());
}
        
    ...
}

Register, remove and remove the Graphic on the IndexedSet structure corresponding to the Canvas

public static void RegisterGraphicForCanvas(Canvas c, Graphic graphic)
{
	...
}

public static void UnregisterGraphicForCanvas(Canvas c, Graphic graphic)
{
	...
}

Get all Graphic elements corresponding to Canvas

public static IList<Graphic> GetGraphicsForCanvas(Canvas canvas)
{
    IndexedSet<Graphic> graphics;
    if (instance.m_Graphics.TryGetValue(canvas, out graphics))
        return graphics;

    return s_EmptyList;
}

Graphic

public abstract class Graphic
    : UIBehaviour,
      ICanvasElement
{
		[FormerlySerializedAs("m_Mat")]
        [SerializeField] protected Material m_Material;

        [SerializeField] private Color m_Color = Color.white;

        [NonSerialized] protected bool m_SkipLayoutUpdate;
        [NonSerialized] protected bool m_SkipMaterialUpdate;
         [SerializeField]
        private Vector4 m_RaycastPadding = new Vector4();
        
        [NonSerialized] private RectTransform m_RectTransform;
        [NonSerialized] private CanvasRenderer m_CanvasRenderer;
        [NonSerialized] private Canvas m_Canvas;

        [NonSerialized] private bool m_VertsDirty;
        [NonSerialized] private bool m_MaterialDirty;

        [NonSerialized] protected UnityAction m_OnDirtyLayoutCallback;
        [NonSerialized] protected UnityAction m_OnDirtyVertsCallback;
        [NonSerialized] protected UnityAction m_OnDirtyMaterialCallback;

        [NonSerialized] protected static Mesh s_Mesh;
        [NonSerialized] private static readonly VertexHelper s_VertexHelper = new VertexHelper();

        [NonSerialized] protected Mesh m_CachedMesh;
        [NonSerialized] protected Vector2[] m_CachedUvs;
        
        ...
	
}

Graphic inherits UIBehaviour and ICanvasElement. As mentioned earlier with CanvasUpdateRegistry, any component that needs to take effect on Canvas must implement the ICanvasElement interface.

Each Graphic corresponds to a CanvasRenderer. When adding a Graphic component to an object, the CanvasRenderer will also be added to the object. CanvasRenderer will be responsible for the rendering of Graphics

When the Graphic is affected by the layout, the display content changes, the material changes and other related content will be marked as Dirty and then reconstructed.

//标记布局重建 注册到LayoutRebuilder中 等待Rebuilt (注册到LayoutRebuilder中 后面讲)
public virtual void SetLayoutDirty()
{
    if (!IsActive())
    	return;

    LayoutRebuilder.MarkLayoutForRebuild(rectTransform);

    if (m_OnDirtyLayoutCallback != null)
    	m_OnDirtyLayoutCallback();
}
        
//标记顶点Dirty  注册到CanvasUpdateRegistry  中等待Rebuilt  
public virtual void SetVerticesDirty()
{
	if (!IsActive())
        return;

	m_VertsDirty = true;
	CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);

	if (m_OnDirtyVertsCallback != null)
	m_OnDirtyVertsCallback();
}
    

//标记材质Dirty  注册到CanvasUpdateRegistry  中等待Rebuilt  
public virtual void SetMaterialDirty()
{
	if (!IsActive())
        return;

	m_MaterialDirty = true;
	CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);

	if (m_OnDirtyMaterialCallback != null)
	m_OnDirtyMaterialCallback();
}    

Rebuild process of ICanvasElement

    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;
        }
    }

UpdateGeometry updates vertices

      protected virtual void UpdateGeometry()
        {
        	//此情况子类基本不存在就不分析
            if (useLegacyMeshGeneration)
            {
                DoLegacyMeshGeneration();
            }
            else
            {
                DoMeshGeneration();
            }
        }
        
        
           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);
        }
  • OnPopulateMesh saves vertices and other related data to s_VertexHelper
  • Get the corresponding IMeshModifier type component and call the ModifyMesh method to modify the Mesh information.
  • Fill the modified information into workerMesh
  • Finally apply workerMesh to canvasRenderer

UpdateMaterial updates the material

  protected virtual void UpdateMaterial()
    {
        if (!IsActive())
            return;

        canvasRenderer.materialCount = 1;
        canvasRenderer.SetMaterial(materialForRendering, 0);
        canvasRenderer.SetTexture(mainTexture);
    }
    
    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;
        }
	}

When setting the material for canvasRenderer, all IMaterialModifier type components will be traversed and the IMaterialModifier.GetModifiedMaterial method will be called to obtain the modified Material when reconstructing the image to achieve the masking effect.


Replenish

OnEnable method

The CacheCanvas method is called, the Canvas list is obtained from the parent node, the first activated and available Canvas is assigned to m_Canvas, and m_Canvas is registered in the GraphicRegistry (a registry where the Graphic contained in the specified Canvas can be obtained from m_Graphics. If m_Graphics If not included, add the current Graphic and Canvas registration to m_Graphics), then set s_WhiteTexture (corresponding attribute MainTexture, used to draw the texture of graphics), and finally SetAllDirty (set Layout layout, Vertices vertices and Material material to Dirty respectively).

OnDisable method

Unregister from GraphicRegistry and CanvasUpdateRegistry, clear canvasRenderer, and notify LayoutRebuilder to rebuild Layout.

OnRectTransformDimensionsChange method (when RectTransform dimensions change), if in reconstruction, only SetVerticesDirty is set, otherwise SetVerticesDirty and SetLayoutDirty are called.

OnBeforeTransformParentChanged method , unregisters from GraphicRegistry and notifies LayoutRebuilder to rebuild Layout.

OnTransformParentChanged method , set m_Canvas to empty, and call the CacheCanvas method. Get the Canvas list from the parent node, get the first activated and available Canvas and assign it to m_Canvas, register m_Canvas in GraphicRegistry, and finally SetAllDirty (set Layout layout, Vertices vertices and Material material to Dirty respectively).

OnDidApplyAnimationProperties method , call SetAllDirty (set Layout layout, Vertices vertices and Material material to Dirty respectively).

OnCanvasHierarchyChanged method , called when the state of the parent Canvas changes (this function is called when the parent Canvas is enabled, disabled, or the OverrideSorting of the nested Canvas is changed), sets m_Canvas to currentCanvas, sets m_Canvas to empty, and calls the CacheCanvas method . Get the Canvas list from the parent node, get the first activated and available Canvas and assign it to m_Canvas. If currentCanvas is different from m_Canvas, deregister it from GraphicRegistry. If Graphic is activated and available, re-register it to GraphicRegistry.

Raycast method

Determine whether the ray is valid in the component through the IsRaycastLocationValid method of ICanvasRaycastFilter (Image inherits this interface). This method is also used in the Raycast method of GraphicRaycaster to filter out Graphics illuminated by rays. GraphicRaycaster inherits from BaseRaycaster, and the input module obtains the affected object through Raycast.

Guess you like

Origin blog.csdn.net/NippyLi/article/details/123468201