NGUI所见即所得之深入剖析UIPanel,UIWidget,UIDrawCall底层原理
NGUI所見(jiàn)即所得之深入剖析UIPanel,UIWidget,UIDrawCall底層原理
By D.S.Qiu
尊重他人的勞動(dòng),支持原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處:http.dsqiu.iteye.com
?
? ? ? ? 之前項(xiàng)目中用的NGUI的版本是3.0.7 f3,開(kāi)始的時(shí)候感覺(jué)沒(méi)有什么問(wèn)題,直達(dá)最近項(xiàng)目UI的完成度比較高時(shí),就突然出現(xiàn)掉幀很?chē)?yán)重的現(xiàn)象,即使只有一個(gè)UI打開(kāi)(其他都是active = false的情況下),打開(kāi)profier,發(fā)現(xiàn)UIPanel LateUpdate 竟然占了CPU使用率的50%左右,這太恐怖了,雖然之前看到過(guò)有吐槽NGUI的機(jī)制的,但是我覺(jué)得為了保證通用犧牲一些性能還是在所難免的,但是沒(méi)想到這個(gè)版本竟然這么廢。
? ? ? ? 之前雖然研究過(guò)NGUI的UIWidget, UIDrawCall,UIGeometry和 UIPanel等基礎(chǔ)腳本(NGUI所見(jiàn)即所得之UIWidget , UIGeometry & UIDrawCall,NGUI所見(jiàn)即所得之UIPanel),也大概清楚了NGUI的繪制原理。但對(duì)具體的邏輯還是不夠清楚,有點(diǎn)鳳毛麟角。為了更好的改進(jìn)NGUI的性能以及更加規(guī)范使用NGUI,只有把NGUI的底層吃透。
? ? ? ? 由于在之前的文章介紹了UIGeometry,UIDrawCall和UIWidget之間的關(guān)系,以及UIPanel的管理機(jī)制,所以本文主要剖析底層的原理,主要要弄清楚一下問(wèn)題:
?
? ? ? ? ? ? ? ?1. transform ,大小(size)的變化的底層繪制影響
? ? ? ? ? ? ? ?2.顏色(包括透明度)變化的底層繪制影響
? ? ? ? ? ? ? ?3.enable 和 disable 狀態(tài)變化底層的處理
? ? ? ? ? ? ? ?4.UIDrawCall 和 UIPanel 機(jī)制的細(xì)節(jié)
? ? ? ?
? ? ? ? 未免讀者理不順,先簡(jiǎn)單說(shuō)下UIGeometry,UIDrawCall和UIWidget的關(guān)系:UIWidget是UI的基礎(chǔ)組件(UILabel,UISprite)的基類,含有組件的基本信息(width,Height,color等),UIGeometry是UIWidget的幾何數(shù)據(jù),記錄了頂點(diǎn)坐標(biāo),貼圖的UVs和顏色等信息,UIDrawCall是將多個(gè)UIWidget的UIGeometry組合起來(lái)一起繪制,具體的UIWidget如果共用一個(gè)UIDrawCall由UIPanel控制,要想了解更多可以點(diǎn)擊上面的鏈接的文章查看。
? ? ? ? 雖然從人的求知欲角度,我們的疑問(wèn)是按照上面 1-4 排列的,但是下面卻是從 4開(kāi)始介紹,只要把4理解透了3,2,1就自然迎刃而解了。
UIDrawCall
? ? ? ? UIGeometry相對(duì)簡(jiǎn)單,這里就不再浪費(fèi)篇幅介紹了,UIDrawCall是繪制的基礎(chǔ)組件,還是有必要仔細(xì)介紹下。
1.成員變量
? ? ? ? 僅對(duì)幾個(gè)比較重要又搞不明白的變量進(jìn)行解析:
? ? ? ? a)List<UIDrawCall> mActiveList 和 mInactiveList : 為什么會(huì)有兩個(gè)List,mAcitveList 保持當(dāng)前激活的UIDrawCall, mInactiveList主要是用于回收UIDrawCall.Destroy()的UIDrawCall,以達(dá)到循環(huán)利用避免內(nèi)存的反復(fù)申請(qǐng)和釋放,減少GC的次數(shù)。這個(gè)機(jī)制前面介紹的 vp_Timer采用這個(gè)策略。
? ? ? ? b)Material mMaterial 和 mDynamicMat:不是講究節(jié)約內(nèi)存么,怎么會(huì)有兩個(gè)Material,mMaterial就是我們圖集的材質(zhì)Material,mDynamicMat是實(shí)際采用的Material,因?yàn)閁IPanel 的 Clipping有 AlphaClipp 和 SoftClip 這兩個(gè)是要通過(guò)切換Shader來(lái)實(shí)現(xiàn)的,所以需要對(duì)應(yīng)動(dòng)態(tài)創(chuàng)建一個(gè)Material,這個(gè)就是mDynamicMat的存在。
? ? ? ? c)bool mRebuildMat 和 isDirty:這兩者表示UIDrawCall所處的狀態(tài),當(dāng)改變UIDrawCall的 Material 和 Shader ,mRebuildMat就變?yōu)?true,就會(huì)引起 RebuildMaterial()的調(diào)用。isDirty若為 true ,表示UIDrawCall要進(jìn)行重寫(xiě)“填充”,調(diào)用Set函數(shù)
C#代碼??2.幾個(gè)重要的函數(shù)
? ? ? ? a)CreateMaterial, RebuildMaterial 和 UpdateMaterial,這是三個(gè)后面包含前面,總之就是完成材質(zhì)的創(chuàng)建或更新。
? ? ? ? b)Set (BetterList<Vector3> verts,BetterList<Vector3> norms,BetterList<Vector4> tans,BetterList<Vector2> uvs,BetterList<Color32> cols),根據(jù)verts,norms,tans,uvs,cols重新構(gòu)建Mesh,MeshRender
C#代碼??? ? ? c)OnEnable,Ondisable 和 OnDestroy:銷(xiāo)毀了mDynamicMat,可以看出Material比Mesh更簡(jiǎn)單,不用太考慮內(nèi)存問(wèn)題,然后OnDestroy()沒(méi)有發(fā)現(xiàn)調(diào)用。
C#代碼??? ? ? ?d)Create , Clear 和 Destroy:Create 先從mInactiveList中取出一個(gè),在附上屬性達(dá)到重復(fù)利用,Destroy是將沒(méi)用的UIDrawCall從mActiveList移到mInactiveList中:
C#代碼???
UIPanel
? ? ? ?之前就介紹過(guò)UIPanel,也畫(huà)了UIPanel主要函數(shù)的調(diào)用棧(點(diǎn)擊查看),這里也簡(jiǎn)單羅列下LateUpdate的函數(shù)調(diào)用:
?LateUpdate
? ? ? UpdateSelf
? ? ? ? ? ? ? ??UpdateTransformMatrix : 調(diào)整 worldToLocal 矩陣用于調(diào)整其管理的UIWidget的transform,并進(jìn)一步調(diào)整頂點(diǎn)信息,還調(diào)整clipOffset的變量
? ? ? ? ? ? ? ? UpdateLayers : 更新LayerMask
? ? ? ? ? ? ? ? UpdateWidgets : 調(diào)整UIWidget
? ? ? ? ? ? ? ? ? ? ? ? ? ? UIWidget.UpdateGeometry : 調(diào)整UIWidget的幾何(頂點(diǎn)等)信息
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??OnFill(geometry.verts, geometry.uvs, geometry.cols): 如果顏色(透明度)和大小等改變就重新填充頂點(diǎn)信息
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??geometry.ApplyTransform : transform發(fā)生改變,調(diào)整UIGeometry中頂點(diǎn)的位置(矩陣計(jì)算)
? ? ? ? ? ? ? ? FillAllDrawCalls ?or FillDrawCall : 重新構(gòu)建所有UIDrawCall (當(dāng)UIWdiget的depth發(fā)生變化),否則只調(diào)整有UIWidget的UIDrawCall
? ? ? UpdateDrawCalls : 調(diào)整UIPanel管理的UIDrawCall 的 transform 和 clip 等屬性
? ? ? 越來(lái)越覺(jué)得NGUI的代碼組件結(jié)構(gòu)越來(lái)越清晰,雖然篇幅很長(zhǎng)(有1600多行)但理解還是可以很簡(jiǎn)單的。
?
UIWidget
? ? ? ?UIWidget有一個(gè)變量 mChange 和一個(gè)函數(shù) MarkAsChange() 很重要,這兩個(gè)標(biāo)記UIWidget是否變化需要進(jìn)行調(diào)整的狀態(tài)。
? ? ? ? ? ? ? ? 1.當(dāng) Anchor , Pivot , Alpha 以及 UILabel 和 UISprite 的一些狀態(tài)的改變 mChange = true ,即會(huì)調(diào)整Geometry信息
? ? ? ? ? ? ? ? 2.MarkAsChange 會(huì)執(zhí)行?drawCall.isDirty = true; 這樣就會(huì)導(dǎo)致其所屬的 UIDrawCall 需要重寫(xiě)構(gòu)建?
?
針對(duì)前面 1-3 的疑問(wèn)進(jìn)行如下總結(jié):
? ? ? UIWidget(UILabel , UISprite)的任何變化(transform , drawSize , width , heigth , color , pivot ,anchor 等)變化都會(huì)引起繪制該UIWidget進(jìn)行重新構(gòu)建——對(duì)Mesh的頂點(diǎn)進(jìn)行刷新,尤其是depth的變化會(huì)使得所有UIDrawCall 進(jìn)行重寫(xiě)調(diào)整,這是非常耗性能的。
? ? ? ?
總結(jié):
? ? ? ?NGUI的好處就是:合并Mesh和圖集節(jié)省DrawCall,由于影響Mesh的因素太多了,所以會(huì)“牽一發(fā)而動(dòng)全身”,NGUI采取的一個(gè)通用的策略,沒(méi)有對(duì)不同的情況做不同的處理,都是采用某個(gè)UIDrawCall全部刷新甚至是全部UIDrawCall的刷新,這也是大家吐槽的“重中之重”。
? ? ? ?D.S.Qiu認(rèn)為針對(duì)不用的情況還是會(huì)有不少優(yōu)化的,比如改變alpha值,可以不需要重新調(diào)整頂點(diǎn)verts,而只需要單獨(dú)調(diào)整cols的alpha通道,改變depth也不需要全部調(diào)整UIDrawCall,這樣明顯是沒(méi)有做到嚴(yán)格的管理的。
? ? ? ?對(duì)此,D.S.Qiu提出2點(diǎn)使用NGUI制作UI的建議:
? ? ? ? ? ? ? ? 1)盡量是UIWidget靜動(dòng)分離,即靜止的盡量合成單獨(dú)一個(gè)UIPanel,會(huì)變化的就放在另外一個(gè)UIPanel
? ? ? ? ? ? ? ? 2)盡量控制UIPanel和UIDrawCall的數(shù)量,充分利用圖集的空間,對(duì)“夾層”的情況可以通過(guò)圖集的調(diào)整,使得UIDrawCall變得更少
?
? ? ? ? 由于時(shí)間關(guān)系(馬上2:30了),就只能寫(xiě)到這里,如果你有NGUI的任何問(wèn)題,歡迎和D.S.Qiu進(jìn)行交流討論。
?
? ? ? ? 如果您對(duì)D.S.Qiu有任何建議或意見(jiàn)可以在文章后面評(píng)論,或者發(fā)郵件(gd.s.qiu@gmail.com)交流,您的鼓勵(lì)和支持是我前進(jìn)的動(dòng)力,希望能有更多更好的分享。
? ? ? ? 轉(zhuǎn)載請(qǐng)?jiān)谖氖鬃⒚鞒鎏?#xff1a;http://dsqiu.iteye.com/blog/2025177
更多精彩請(qǐng)關(guān)注D.S.Qiu的博客和微博(ID:靜水逐風(fēng))
?
? ? ? ? ?
?
轉(zhuǎn)載于:https://www.cnblogs.com/123ing/p/3704964.html
總結(jié)
以上是生活随笔為你收集整理的NGUI所见即所得之深入剖析UIPanel,UIWidget,UIDrawCall底层原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: (C#) 调用执行批处理文件
- 下一篇: Rhythmk 学习 Hibernate