NGUI源码剖析之提高GPU性能

一.使用NGUI

NGUI和UGUI比起来我更喜欢NGUI,因为NGUI可将图片打成图集,而且它和texturepacker配合使用会十分的方便,高效。texturepacker导出一张合图和一个文本,使用NGUI创建一个Atlas,拖拽上去即可。

二.NGUI性能提升。

首先来一段源码:这是UIPanel里面的代码。

void FillAllDrawCalls ()
	{
		for (int i = 0; i < drawCalls.Count; ++i)
			UIDrawCall.Destroy(drawCalls[i]);
		drawCalls.Clear();

		Material mat = null;
		Texture tex = null;
		Shader sdr = null;
		UIDrawCall dc = null;
		int count = 0;

		if (mSortWidgets) SortWidgets();
		for (int i = 0; i < widgets.Count; ++i)	
		{
			UIWidget w = widgets[i];

			if (w.isVisible && w.hasVertices)//遍历所有的widget,如果可见就获取该widget的材质、图片、shader
			{
				Material mt = w.material;
				Texture tx = w.mainTexture;
				Shader sd = w.shader;
				if (mat != mt || tex != tx || sdr != sd)//如果当前的这三个属性和上次循环有一个不一样,说明是一个新的
				{//Atlas,并将上次新建的DrawCall对象添加到链表里面。
					if (dc != null && dc.verts.size != 0)
					{
						drawCalls.Add(dc);
						dc.UpdateGeometry(count);//更新刚刚添加的那个drawcall信息
						dc.onRender = mOnRender;//注册渲染回调
						mOnRender = null;
						count = 0;
						dc = null;//将dc赋值为空,很关键。
					}

					mat = mt;//给当前drawcall做标记,好做下一次循环判断
					tex = tx;
					sdr = sd;
				}

				if (mat != null || sdr != null || tex != null)
				{
					if (dc == null)
					{
						dc = UIDrawCall.Create(this, mat, tex, sdr);//如果dc为空,说明遇到新的图集,需新建一个drawcall
						dc.depthStart = w.depth;
						dc.depthEnd = dc.depthStart;
						dc.panel = this;
					}
					else
					{
						int rd = w.depth;//如果dc不为空,那就将他和上次循环那个共享一个
						if (rd < dc.depthStart) dc.depthStart = rd;//DrawCall,不需要新建。
						if (rd > dc.depthEnd) dc.depthEnd = rd;
					}

					w.drawCall = dc;

					++count;
					if (generateNormals) w.WriteToBuffers(dc.verts, dc.uvs, dc.cols, dc.norms, dc.tans);//将信息写入缓冲区
					else w.WriteToBuffers(dc.verts, dc.uvs, dc.cols, null, null);

					if (w.mOnRender != null)
					{
						if (mOnRender == null) mOnRender = w.mOnRender;
						else mOnRender += w.mOnRender;
					}
				}
			}
			else w.drawCall = null;
		}

		if (dc != null && dc.verts.size != 0)
		{
			drawCalls.Add(dc);//将最后一个drawcall添加到渲染列表。因为如果最后一个和上一个属于同一个图集,不会进入
			dc.UpdateGeometry(count);//下一个循环,也就不会将最后一个drawcall添加到渲染列表。
			dc.onRender = mOnRender;
			mOnRender = null;
		}
	}

1.通过阅读代码,我们发现NGUI的渲染算法是这样的。

比如我们的ui界面(6个Sprite)由下面几个图集组成的界面:(depth从小到大)

Atlas1/Atlas2/Atlas2/Atlas3/Atlas2/Atlas1

2.按照上面的算法,NGUI会生成多少个DrawCall对象呢(将UIDrawCall类里面的#define SHOW_HIDDEN_OBJECTS注释取消可以看到有多少个)?首先Atlas1是一个,到Atlas2时,由于两个图集使用的图片不一样,材质也不一样,因此NGUI会新建一个DrawCall对象,到了第三个,由于上一个也是Atlas2,就不需要新建一个DrawCall对象。因此就少一个Drawcall,这样下来,一共可以生成5个Drawcall对象。Drawcall对象越多,GPU消耗也就越多。所以我们可以通过以下这种排列来减少Drawcall对象:

Atlas1/Atlas1/Atlas2/Atlas2/Atlas2/Atlas3      

哦?这样好像只剩下3个DrawCall了。所以争取把同一个图集的图片让他们的depth相挨着是一个简单的解决办法。但是,我还有另外一个解决办法。

Atlas1/Atlas1/Atlas1/Atlas1/Atlas1/Atlas1

3.这个办法就是将一个界面下的ui图片打到同一个图集里面。这样就只有一个DrawCall对象了。但是,这样会有一个问题。如果另外一个界面用到了相同的图片,你需要将它复制到新的图集里面,这样会造成内存浪费。所以需要将通用的图片打到一起,组成一个CommonAtlas,比如说UI的一些九宫格的通用图片。其他小图片就复制就复制了,它无非就是在内存和渲染之间找临界点。一般一个界面下来5-10个DrawCall是比较正常的(如果图集较多时)。

三 总结。

      1.合理安排depth可以减少DrawCall数量。

     2.将一个界面的多个元素打到同一个图集里面可以减少DrawCall数量。

     3..不要将动态图和静态图安置到同一个DrawCall里面,因为动态图(比如Spine做的骨骼动画)经常需要传递给GPU运算,每次变化都要这样做,如果和静态图放到了同一个DrawCall,它会将静态UI的顶点、材质信息也传送给GPU。有种拖人(静态ui)下水的意思。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值