日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

NGUI所见即所得之深入剖析UIPanel,UIWidget,UIDrawCall底层原理

發布時間:2025/3/14 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NGUI所见即所得之深入剖析UIPanel,UIWidget,UIDrawCall底层原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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使用率的50%左右,這太恐怖了,雖然之前看到過有吐槽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<Color32> cols),根據verts,norms,tans,uvs,cols重新構建Mesh,MeshRender

    C#代碼??
  • mMesh.vertices?=?verts.buffer;??
  • mMesh.uv?=?uvs.buffer;??
  • mMesh.colors32?=?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的函數調用:

    ?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的代碼組件結構越來越清晰,雖然篇幅很長(有1600多行)但理解還是可以很簡單的。

    ?

    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:30了),就只能寫到這里,如果你有NGUI的任何問題,歡迎和D.S.Qiu進行交流討論。

    ?

    ? ? ? ? 如果您對D.S.Qiu有任何建議或意見可以在文章后面評論,或者發郵件(gd.s.qiu@gmail.com)交流,您的鼓勵和支持是我前進的動力,希望能有更多更好的分享。

    ? ? ? ? 轉載請在文首注明出處:http://dsqiu.iteye.com/blog/2025177

    更多精彩請關注D.S.Qiu的博客和微博(ID:靜水逐風)

    ?

    ? ? ? ? ?

    ?

    轉載于:https://www.cnblogs.com/123ing/p/3704964.html

    總結

    以上是生活随笔為你收集整理的NGUI所见即所得之深入剖析UIPanel,UIWidget,UIDrawCall底层原理的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。