NGUI的原理机制

深入剖析UIPanel,UIWidget,UIDrawCall底层原理

低头干活,也要抬头看世界。

NGUI3.6.8 unity版本4.6.5f1,


上图是UIWidget,UIGeometry&UIDrawCall的关系图,UIWidget用于UIDrawcall mDrawCall和UIGeometry mGeo两个成员变量,其中UIGeometry就是对UIWidget的顶点vertices,uvs和color进行存储和更新,UIDrawCall就是根据提供的数据(统一在UIPanel指派)进行渲染绘制。

UIGeometry完全由UIWidget维护,首先UILabel,UISprite,UITexture对UIWidget的OnFill进行重写——初始化mGeo的verts,uvs,cols的BetterList。然后UIWidget的UpdateGeometry函数对UIGeometry的ApplyTransform()和WriteToBuffer()调用进行更新。

 

         每一个UIWidget都有一个UIGeometry,但是并不都有一个UIDrawCall,而是要通过Batch合并达到减少DrawCall的数量,UIDrawCall是由UIPanel生成的。至于什么是DrawCall,因为没有3D引擎经验,只能从只言片语中拾获一点理解:

       

             “Unity(或者说基本所有图形引擎)生成一帧画面的处理过程大致可以这样简化描述:引擎首先经过简单的可见性测试,确定摄像机可以看到的物体,然后把这些物体的顶点(包括本地位置、法线、UV等),       索引(顶点如何组成三角形),变换(就是物体的位置、旋转、缩放、以及摄像机位置等),相关光源,纹理,渲染方式(由材质/Shader决定)等数据准备好,然后通知图形API——或者就简单地看作是通知GPU       ——开始绘制,GPU基于这些数据,经过一系列运算,在屏幕上画出成千上万的三角形,最终构成一幅图像。 在Unity中,每次引擎准备数据并通知GPU的过程称为一次Draw Call。这一过程是逐个物体进行的,对       于每个物体,不只GPU的渲染,引擎重新设置材质/Shader也是一项非常耗时的操作。因此每帧的Draw Call次数是一项非常重要的性能指标。”

     

       NGUI被说的最多的优点就是:减少DrawCall数量。但现在为了解决sandwiching issues和Z/depth issues,对DrawCall进行split。


UIPanel继承UIRect

<span style="white-space:pre">	</span>/// <summary>
	/// Set anchor rect references on start.
	/// </summary>

	protected void Start ()
	{
		mStarted = true;
		OnInit();
		OnStart();
	}
UIPanel的Onlnit()=>FindParent()=>UpdateDrawCalls()=>LateUpdate();


<span style="white-space:pre">	</span>/// <summary>
	/// Update all draw calls associated with the panel.
	/// </summary>

	void UpdateDrawCalls ()
	{
		Transform trans = cachedTransform;
		bool isUI = usedForUI;

		if (clipping != UIDrawCall.Clipping.None)
		{
			drawCallClipRange = finalClipRegion;
			drawCallClipRange.z *= 0.5f;
			drawCallClipRange.w *= 0.5f;
		}
		else drawCallClipRange = Vector4.zero;

		// Legacy functionality
		if (drawCallClipRange.z == 0f) drawCallClipRange.z = Screen.width * 0.5f;
		if (drawCallClipRange.w == 0f) drawCallClipRange.w = Screen.height * 0.5f;

		// DirectX 9 half-pixel offset
		if (halfPixelOffset)
		{
			drawCallClipRange.x -= 0.5f;
			drawCallClipRange.y += 0.5f;
		}

		Vector3 pos;

		// We want the position to always be on even pixels so that the
		// panel's contents always appear pixel-perfect.
		if (isUI)
		{
			Transform parent = cachedTransform.parent;
			pos = cachedTransform.localPosition;

			if (parent != null)
			{
				float x = Mathf.Round(pos.x);
				float y = Mathf.Round(pos.y);

				drawCallClipRange.x += pos.x - x;
				drawCallClipRange.y += pos.y - y;

				pos.x = x;
				pos.y = y;
				pos = parent.TransformPoint(pos);
			}
			pos += drawCallOffset;
		}
		else pos = trans.position;

		Quaternion rot = trans.rotation;
		Vector3 scale = trans.lossyScale;

		for (int i = 0; i < drawCalls.Count; ++i)
		{
			UIDrawCall dc = drawCalls[i];

			Transform t = dc.cachedTransform;
			t.position = pos;
			t.rotation = rot;
			t.localScale = scale;

			dc.renderQueue = (renderQueue == RenderQueue.Explicit) ? startingRenderQueue : startingRenderQueue + i;
			dc.alwaysOnScreen = alwaysOnScreen &&
				(mClipping == UIDrawCall.Clipping.None || mClipping == UIDrawCall.Clipping.ConstrainButDontClip);
			dc.sortingOrder = mSortingOrder;
		}
	}

UIDrawCall

1.成员变量

        仅对几个比较重要又搞不明白的变量进行解析:

        a)List<UIDrawCall> mActiveList 和 mInactiveList : 为什么会有两个List,mAcitveList 保持当前激活的UIDrawCall, mInactiveList主要是用于回收UIDrawCall.Destroy()的UIDrawCall,以达到循环利用避免内存的反复申请和释放,减少GC的次数。这个机制前面介绍的 vp_Timer采用这个策略。

        b)Material mMaterial 和 mDynamicMat:不是讲究节约内存么,怎么会有两个Material,mMaterial就是我们图集的材质Material,mDynamicMat是实际采用的Material,因为UIPanel 的 Clipping有 AlphaClipp 和 SoftClip 这两个是要通过切换Shader来实现的,所以需要对应动态创建一个Material,这个就是mDynamicMat的存在。

        c)bool mRebuildMat 和 isDirty:这两者表示UIDrawCall所处的状态,当改变UIDrawCall的 Material 和 Shader ,mRebuildMat就变为 true,就会引起 RebuildMaterial()的调用。isDirty若为 true ,表示UIDrawCall要进行重写“填充”,调用Set函数。

2.几个重要的函数

        a)CreateMaterial, RebuildMaterial 和 UpdateMaterial,这是三个后面包含前面,总之就是完成材质的创建或更新。

        b)Set (BetterList<Vector3> verts,BetterList<Vector3> norms,BetterList<Vector4> tans,BetterList<Vector2> uvs,BetterList<Color32> cols),根据verts,norms,tans,uvs,cols重新构建Mesh,MeshRender。

c)OnEnable,Ondisable 和 OnDestroy:销毁了mDynamicMat,可以看出Material比Mesh更简单,不用太考虑内存问题,然后OnDestroy()没有发现调用。

  d)Create , Clear 和 Destroy:Create 先从mInactiveList中取出一个,在附上属性达到重复利用,Destroy是将没用的UIDrawCall从mActiveList移到mInactiveList中:

参考:

http://www.cnblogs.com/zhibolife/p/3642000.html

http://dsqiu.iteye.com/blog/1973651

http://dsqiu.iteye.com/blog/1965340

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值