【NGUI源码剖析】深入理解NGUI的drawcall_ngui drawcall_coffeecato的博客-程序员秘密

技术标签: Unity3d  NGUI  UIDrawCall  NGUI源码及应用  游戏开发  ngui  

引言

上篇【NGUI源码剖析】NGUI的drawcall简单认识了NGUI中的drawcall。借着近来工作更替的间隙,本文在之前的基础上做一点深入的分析:

  • UIPanel工作流程图
  • 如何生成drawcall
  • widgets的生成及作用
  • drawcall的合并

UIPanel的工作流程
无论是UIDrawCall还是UIWidget的流程,都离不开UIPanel的工作流。“吾尝终日而思矣,不如须臾之所学也”看了几天NGUI的源代码,才发觉抓住主线才不会跑偏,而这幅图就是打开NGUI秘密的金钥匙。
image


如何生成drawcall
这里还是要从UIPanel中的数据drawCalls说起

	/// <summary>
	/// List of draw calls created by this panel. Do not attempt to modify this list yourself.
	/// </summary>

	[System.NonSerialized]
	public List<UIDrawCall> drawCalls = new List<UIDrawCall>();

可以看到drawCalls.Add()的调用全都在FillAllDrawCalls()内部:

	/// <summary>
	/// Fill the geometry fully, processing all widgets and re-creating all draw calls.
	/// </summary>

	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)
			{
				Material mt = w.material;
				
				if (onCreateMaterial != null) mt = onCreateMaterial(w, mt);

				Texture tx = w.mainTexture;
				Shader sd = w.shader;

                 // 判断widgets[i-1]与widgets[i]的mateial,texture,shader是否一致
				if (mat != mt || tex != tx || sdr != sd)
				{
					if (dc != null && dc.verts.Count != 0)
					{
                        drawCalls.Add(dc);          //缓存widgets[i-1]的drawcall
						dc.UpdateGeometry(count);
						dc.onRender = mOnRender;
						mOnRender = null;
						count = 0;
                        dc = null;                  //缓存drawcall后,重置drawcall
					}
                    //缓存widgets[i]的material,texture,shader
					mat = mt;
					tex = tx;
					sdr = sd;
				}

				if (mat != null || sdr != null || tex != null)
				{
                    // 创建新的drawcall,设置drawcall相关的数据
					if (dc == null)
					{
						dc = UIDrawCall.Create(this, mat, tex, sdr);
						dc.depthStart = w.depth;
						dc.depthEnd = dc.depthStart;
						dc.panel = this;
						dc.onCreateDrawCall = onCreateDrawCall;
					}
                    //widgets[i-1]与widgets[i]的material,shader,texture相同时,共用一个drawcall
					else
					{
                        // 更新drawcall的深度范围
						int rd = w.depth;
						if (rd < dc.depthStart) dc.depthStart = rd;
						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, generateUV2 ? dc.uv2 : null);
					else w.WriteToBuffers(dc.verts, dc.uvs, dc.cols, null, null, generateUV2 ? dc.uv2 : null);

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

        //缓存widgets[widgets.Count-1]的drawcall
		if (dc != null && dc.verts.Count != 0)
		{
			drawCalls.Add(dc);
			dc.UpdateGeometry(count);
			dc.onRender = mOnRender;
			mOnRender = null;
		}
	}

从代码中可以看到,新增drawcall主要通过缓存widgets[i-1]的material,texture,shader与widgets[i]进行对比。如果使用相同的material,texture,shader则共用同一个drawcall,否则新创建一个drawcall。
其中都是在遍历widgets进行操作,那么widgets从哪里来?又要到哪里去?


widgets的生成及作用

  • UIWidget的定义

image
UIPanel与UIWidget的对应关系是一对多的,而UISprite,UI2DSprite,UITexture,UILabel等控件本质上都是UIWidget。


  • widgets的生成
    widgets.Add的调用有两处:
    1.改变UIWidget的深度时
	/// <summary>
	/// Depth controls the rendering order -- lowest to highest.
	/// </summary>

	public int depth
	{
		get
		{
			// Experiment with a transform-based depth, uGUI style
			//if (mDepth == int.MinValue)
			//{
			//    int val = cachedTransform.GetSiblingIndex();
			//    UIWidget pt = parent as UIWidget;
			//    if (pt != null) val += pt.depth;
			//    return val;
			//}
			return mDepth;
		}
		set
		{
			if (mDepth != value)
			{
				if (panel != null) panel.RemoveWidget(this);
				mDepth = value;
				
				if (panel != null)
				{
					panel.AddWidget(this);
					
					...

2.创建UIPanel时

	/// <summary>
	/// Ensure we have a panel referencing this widget.
	/// </summary>

	public UIPanel CreatePanel ()
	{
		if (mStarted && panel == null && enabled && NGUITools.GetActive(gameObject))
		{
			panel = UIPanel.Find(cachedTransform, true, cachedGameObject.layer);

			if (panel != null)
			{
				mParentFound = false;
				panel.AddWidget(this);
				CheckLayer();
				Invalidate(true);
			}
		}
		return panel;
	}

drawcall的合并
从FillAllDrawCalls函数中看drawcall的生成过程,可以肯定的是

widgets.Count >= drawcalls.Count

如果符合【NGUI源码剖析】NGUI如何优化drawcall数量中drawcall的合并条件,这两个List变动的数量就是drawcall合并的数量。

参考文章:
NGUI所见即所得之深入剖析UIPanel,UIWidget,UIDrawCall底层原理

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u010832643/article/details/74932303

智能推荐

为什么全网都在劝你学Java、Python,而不是C++?_轮子厂长的博客-程序员秘密

编程初学者究竟应该怎么选择语言?仅仅因为“Java就业前景好?”、“Python实用性高?”一位在北京工作7年的程序员亲戚跟我聊起过:“并不是所有的程序员都吃青春饭,我身边有很多牛逼的资...

stm32从bootloader跳转到app不进中断问题分析_桉恺的博客-程序员秘密

1、设置VECT_TAB_OFFSET,在system_stm32fxxx.c中。在keil中这样设置,可以通过target的设置自动设置VECT_TAB_OFFSET大小。extern int Image$$ER_IROM1$$Base;#define VECT_TAB_OFFSET ((uint32_t)&amp;Image$$ER_IROM1$$Base)2、bootloader中开了某个中断,但在app中没有开或没有相应的中断入口。3、bootloader在跳转前关闭总中断,app中忘记

Visual Studio 2017 第一个C++程序_疯狂java杰尼龟的博客-程序员秘密

Visual Studio 2017 第一个C++程序第一步、点击创建新项目第二步、创建C++文件第三步、新建项第四步、创建cpp文件第五步、第一个C++程序第六步、运行第一步、点击创建新项目第二步、创建C++文件第三步、新建项第四步、创建cpp文件第五步、第一个C++程序第六步、运行开启新世界~hhh...

configure: error: /usr/include/openssl is a bad --with-openssl prefix_柳鲲鹏的博客-程序员秘密

正确写法如下:# 应该有/usr/include/openssl这个目录./configure --with-openssl=/usr

lua require绝对路径文件时 module not found解决方法_lua module not found_Mr_老冷的博客-程序员秘密

lua进行require绝对路径时,会从package.path中进行遍历print(package.path)会得到类似下面的结果:--&amp;gt; &quot;lualibs/p4ulibs/?.lua;lualibs/?.lua;lualibs/?/?.lua;lualibs/?/init.lua;&quot;故我们可以通过对package.path修改, 来让lua对我们的个人路径进行包含 假...

游戏开发 XNAGame-022 显示立方体,使用键盘控制旋转立方体_虾米大王的博客-程序员秘密

form1.csusing System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows.Forms;using Microsoft.DirectX;using

随便推点

Mybatis-plus主键生成策略无法生成主键_年轻的猴的博客-程序员秘密

记录一次事故,事情说这样的,我的mybatis-plus一切主键都能正常操作,唯独到了自动填充这里出现了问题,我的主键并不能生成雪花算法,而一直都是0,我刚开始以为是版本问题紧接着,我就自己写主键生成策略,希望知道到底出现什么问题:还是出错,正当我难受的时候,我重写了一次pojo类,发现了问题我的Long原本居然是long,真尴尬,关键其他功能一点没问题,也没报错过。。。...

C# 中的数组类_public abstract class array_陈言必行的博客-程序员秘密

数组是 n(n≥1)个相同数据类型的数据元素的有限序列。一维数组可以看作是一个线性表,二维数组可以看作是“数据元素是一维数组”的一维数组,三维数组可以看作是“数据元素是二维数组”的一维数组,依次类推。C#支持一维数组、多维数组及交错数组(数组的数组)。所有的数组类型都隐含继承自 System.Array。Array 是一个抽象类,本身又继承自 System.Object。所以,数组总是在托管堆上分配

ubuntu循环登陆解决办法_妖妖灵誓言的博客-程序员秘密

这个博主成功解决了我的问题,感谢:https://www.jianshu.com/p/4ee56646aa77

P哥的桶_p哥的桶程序_青烟绕指柔!的博客-程序员秘密

题目背景P哥在IOI取得了金牌,现在他开始找女朋友了!题目描述P哥现在有nn个桶,他们排成了一排,这些桶可以装下任意多个女朋友。每个女朋友有一个固定的颜值P哥时不时地会找新女朋友,并把新找的女朋友丢进某个桶里面。我们用1;k;x1kx来表示P哥找了一个颜值为xx的女朋友,并且丢进了kk号桶里面P哥每天晚上需要在特定的桶里面找一些女朋友观赏。我们用2;l;r2lr来表示P哥在ll号桶到rr...

剑指offer——树的子结构_Eartha1995的博客-程序员秘密

题目描述输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

推荐文章

热门文章

相关标签