位置: 编程技术 - 正文

NGUI所见即所得之深入剖析UIPanel,UIWidget,UIDrawCall底层原理(所见即所得出自哪里)

编辑:rootadmin

推荐整理分享NGUI所见即所得之深入剖析UIPanel,UIWidget,UIDrawCall底层原理(所见即所得出自哪里),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:什么叫所见即所得,所见即所得?,所见即所得是啥意思,所见即所得的下一句,所见即所得 所想即所得,所见即所得的下一句,所见即所得的下一句,所见即所得的下一句,内容如对您有帮助,希望把文章链接给更多的朋友!

By D.S.Qiu

尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com

之前项目中用的NGUI的版本是3.0.7 f3,开始的时候感觉没有什么问题,直达最近项目UI的完成度比较高时,就突然出现掉帧很严重的现象,即使只有一个UI打开(其他都是active = false的情况下),打开profier,发现UIPanel LateUpdate 竟然占了CPU使用率的%左右,这太恐怖了,虽然之前看到过有吐槽NGUI的机制的,但是我觉得为了保证通用牺牲一些性能还是在所难免的,但是没想到这个版本竟然这么废。

之前虽然研究过NGUI的UIWidget, UIDrawCall,UIGeometry和 UIPanel等基础脚本(NGUI所见即所得之UIWidget , UIGeometry & UIDrawCall,NGUI所见即所得之UIPanel),也大概清楚了NGUI的绘制原理。但对具体的逻辑还是不够清楚,有点凤毛麟角。为了更好的改进NGUI的性能以及更加规范使用NGUI,只有把NGUI的底层吃透。

由于在之前的文章介绍了UIGeometry,UIDrawCall和UIWidget之间的关系,以及UIPanel的管理机制,所以本文主要剖析底层的原理,主要要弄清楚一下问题:

1. transform ,大小(size)的变化的底层绘制影响

2.颜色(包括透明度)变化的底层绘制影响

3.enable 和 disable 状态变化底层的处理

4.UIDrawCall 和 UIPanel 机制的细节

未免读者理不顺,先简单说下UIGeometry,UIDrawCall和UIWidget的关系:UIWidget是UI的基础组件(UILabel,UISprite)的基类,含有组件的基本信息(width,Height,color等),UIGeometry是UIWidget的几何数据,记录了顶点坐标,贴图的UVs和颜色等信息,UIDrawCall是将多个UIWidget的UIGeometry组合起来一起绘制,具体的UIWidget如果共用一个UIDrawCall由UIPanel控制,要想了解更多可以点击上面的链接的文章查看。

虽然从人的求知欲角度,我们的疑问是按照上面 1-4 排列的,但是下面却是从 4开始介绍,只要把4理解透了3,2,1就自然迎刃而解了。

UIDrawCall

UIGeometry相对简单,这里就不再浪费篇幅介绍了,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函数

C#代码 public Material baseMaterial { get{return mMaterial;} set { if (mMaterial != value) { mMaterial = value; mRebuildMat = true; } } } public Shader shader { get{ return mShader;} set { if (mShader != value) { mShader = value; mRebuildMat = true; } } }

2.几个重要的函数

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

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

C#代码 mMesh.vertices = verts.buffer; mMesh.uv = uvs.buffer; mMesh.colors = cols.buffer; if (norms != null) mMesh.normals = norms.buffer; if (tans != null) mMesh.tangents = tans.buffer;

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

C#代码 void OnEnable () { mRebuildMat = true; } void OnDisable () { depthStart = int.MaxValue; depthEnd = int.MinValue; panel = null; manager = null; mMaterial = null; mTexture = null; NGUITools.DestroyImmediate(mDynamicMat); mDynamicMat = null; } void OnDestroy () { NGUITools.DestroyImmediate(mMesh); }

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

C#代码 static UIDrawCall Create (string name) { //省略其他处理 if (mInactiveList.size > 0) { UIDrawCall dc = mInactiveList.Pop(); mActiveList.Add(dc); if (name != null) dc.name = name; NGUITools.SetActive(dc.gameObject, true); return dc; } //省略其他处理 // Create the draw call mActiveList.Add(newDC); return newDC; } static public void Destroy (UIDrawCall dc) { if (dc) { if (Application.isPlaying) { if (mActiveList.Remove(dc)) { NGUITools.SetActive(dc.gameObject, false); mInactiveList.Add(dc); } } else { mActiveList.Remove(dc); NGUITools.DestroyImmediate(dc.gameObject); } } }

UIPanel

之前就介绍过UIPanel,也画了UIPanel主要函数的调用栈(点击查看),这里也简单罗列下LateUpdate的函数调用:

NGUI所见即所得之深入剖析UIPanel,UIWidget,UIDrawCall底层原理(所见即所得出自哪里)

LateUpdate

UpdateSelf

UpdateTransformMatrix : 调整 worldToLocal 矩阵用于调整其管理的UIWidget的transform,并进一步调整顶点信息,还调整clipOffset的变量

UpdateLayers : 更新LayerMask

UpdateWidgets : 调整UIWidget

UIWidget.UpdateGeometry : 调整UIWidget的几何(顶点等)信息

OnFill(geometry.verts, geometry.uvs, geometry.cols): 如果颜色(透明度)和大小等改变就重新填充顶点信息

geometry.ApplyTransform : transform发生改变,调整UIGeometry中顶点的位置(矩阵计算)

FillAllDrawCalls or FillDrawCall : 重新构建所有UIDrawCall (当UIWdiget的depth发生变化),否则只调整有UIWidget的UIDrawCall

UpdateDrawCalls : 调整UIPanel管理的UIDrawCall 的 transform 和 clip 等属性

越来越觉得NGUI的代码组件结构越来越清晰,虽然篇幅很长(有多行)但理解还是可以很简单的。

UIWidget

UIWidget有一个变量 mChange 和一个函数 MarkAsChange() 很重要,这两个标记UIWidget是否变化需要进行调整的状态。

1.当 Anchor , Pivot , Alpha 以及 UILabel 和 UISprite 的一些状态的改变 mChange = true ,即会调整Geometry信息

2.MarkAsChange 会执行 drawCall.isDirty = true; 这样就会导致其所属的 UIDrawCall 需要重写构建

针对前面 1-3 的疑问进行如下总结:

UIWidget(UILabel , UISprite)的任何变化(transform , drawSize , width , heigth , color , pivot ,anchor 等)变化都会引起绘制该UIWidget进行重新构建——对Mesh的顶点进行刷新,尤其是depth的变化会使得所有UIDrawCall 进行重写调整,这是非常耗性能的。

总结:

NGUI的好处就是:合并Mesh和图集节省DrawCall,由于影响Mesh的因素太多了,所以会“牵一发而动全身”,NGUI采取的一个通用的策略,没有对不同的情况做不同的处理,都是采用某个UIDrawCall全部刷新甚至是全部UIDrawCall的刷新,这也是大家吐槽的“重中之重”。

D.S.Qiu认为针对不用的情况还是会有不少优化的,比如改变alpha&#;,可以不需要重新调整顶点verts,而只需要单独调整cols的alpha通道,改变depth也不需要全部调整UIDrawCall,这样明显是没有做到严&#;的管理的。

对此,D.S.Qiu提出2点使用NGUI制作UI的建议:

1)尽量是UIWidget静动分离,即静止的尽量合成单独一个UIPanel,会变化的就放在另外一个UIPanel

2)尽量控制UIPanel和UIDrawCall的数量,充分利用图集的空间,对“夹层”的情况可以通过图集的调整,使得UIDrawCall变得更少

由于时间关系(马上2:了),就只能写到这里,如果你有NGUI的任何问题,欢迎和D.S.Qiu进行交流讨论。

如果您对D.S.Qiu有任何建议或意见可以在文章后面评论,或者发邮件(gd.s.qiu@gmail.com)交流,您的鼓励和支持是我前进的动力,希望能有更多更好的分享。

转载请在文首注明出处:

【VR】Leap Motion 官网文档 HandModel(手部模型) 前言:本系列译文是为迎合UnityVR的热潮与大家的学习需要,推出的针对Unity方向的LeapMotion官方文档中英对照翻译。本篇为第六篇《HandModel(手部模型)

unity3d 在shader中创建及使用Texture3D 代码来自ArasTexture3D必须要在脚本中创建c#:spanstyle=font-size:px;usingUnityEngine;publicclassCreate3DTex:MonoBehaviour{publicTexture3Dtex;publicintsize=;voidStart(){tex=newTexture3D(s

[Unity3D]UI方案及制作细节(NGUI/EZGUI/原生UI系统) 转载请留下本文原始链接,谢谢。本文会不定期更新维护,最近更新于..。

标签: 所见即所得出自哪里

本文链接地址:https://www.jiuchutong.com/biancheng/369301.html 转载请保留说明!

上一篇:NGUI字体图集导致Label文字破碎的BUG以及解决方案

下一篇:【VR】Leap Motion 官网文档 HandModel(手部模型)(vr moke)

  • 自查补报以前年度收入可以补在当前属期吗
  • 提供劳务代扣的发票
  • 增值税纳税义务发生时间的规定
  • 所得税预缴怎么做分录
  • 以前未入账固定资产补入账涉及的房产税
  • 研发费用直接计入成本吗
  • 快递公司增值税怎么算
  • 进项和销项必须在一个月吗
  • 发票验证码和校验码是一样的吗
  • 申报附加税增加一行如何增加
  • 外包工加工工资怎么结算
  • 房地产公司产品无偿提供他人使用 税
  • 小规模纳税人可以抵扣进项税吗
  • 财务离开公司需要交接哪些资料?
  • 小规模纳税人发票图片
  • 佣金是否要交所得税
  • 社保岗位补贴条件
  • 房地产公司收到房款的会计分录
  • 增值税专用发票验票
  • 企业为员工缴付养老保险
  • 代收代付业务是什么意思
  • 出口退税换汇率多少是正常
  • 培训费增值税专用发票
  • 汽车罚款怎么进账
  • 进项税借方有余额要怎么处理
  • 费用开两次发票怎么入账?
  • 当月有进项税额转出怎么结转未交增值税
  • 门诊医保报销流程详细步骤
  • powershell不显示用户名
  • Windows10如何解压rar
  • 关于其他应收款账户的说法
  • 电脑进步了系统
  • 如何计算旧城拆迁面积
  • 未结算未取得发票怎么办
  • os x 10.10 yosemite自动纠正怎么关?os x yosemite自动纠正功能关闭教程
  • yii gridview
  • javascript语言基础
  • 市职工报销比例多少
  • vue封装组建
  • 小轿车折旧年限
  • 待抵扣进项税额的分录
  • 其他应付款包括的内容
  • Python的aiohttp模块如何使用?
  • 定期存款一次性可以存多少钱
  • 筹建期所得税年度申报如何填列
  • 管理费用和研发费用的关系
  • 计提工会经费是什么工资项目
  • 对账小技巧
  • 借调是原单位发工资吗
  • MSSQL2005 INSERT,UPDATE,DELETE 之OUTPUT子句使用实例
  • 小规模增值税申报未开票收入怎么填
  • 公租房特许经营权转让
  • 增值税发票联次及其作用
  • 库存股属于什么类科目,借贷方向
  • 抵扣联明细没认证如何申报
  • 房地产企业土地增值税预缴
  • 退货属于什么物流
  • 往来的款项性质有哪些
  • 货币资金核算内容
  • 详解标准mysql(x64) Windows版安装过程
  • sqlserver删除重复
  • centos7 nis
  • xp系统怎么关闭更新系统
  • Linux桌面环境安装
  • 进程mmc.exe
  • win7系统桌面图标设置
  • 如何刷硬盘使用时间
  • VMware Workstation虚拟机网络连接不可用
  • 经过整理的一些成语
  • android真机调试闪退
  • python内置函数什么用来返回序列中的最大元素
  • android 测试工具
  • 怎样用python
  • node.js web
  • jquery鼠标移入移出切换图片
  • python如何编程
  • 广东高速公路电子发票服务来平台
  • 安徽省合肥市地震多少级
  • 河南电子发票申请流程
  • 纳税服务的背景是什么
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设