小哈接觸unity3d也有一段時間了,專案組在ui解決方案的選型一直是用的原生的ugui,因此本人也是使用了一段時間的ugui,在ugui的使用方面積累了一些自己的經驗,在此進行乙個記錄與總結。
本文接下來將會對ugui的runtime效能進行著重討論,其它的因素也很多而且很重要,但是一篇文章講清楚一件事就好了,文後會提供ugui的最佳實踐與一些使用技巧,不想看全文的建議直接到最下面看杰倫,啊,不對,是結論。
遊戲中的ui與其它遊戲中的元素本質上是一樣的,相對來說的不同點在於,ui通常是由2d的組合而成,會包含較多的透明元素與漸變元素,而且一般來說會顯示在螢幕的最頂層。
因此,從共同點上說,ui的runtime效能消耗也可以劃分為cpu消耗、gpu消耗與記憶體消耗。其中對於每一部分的具體的消耗以及優化,有諸多大神在網路上發表過文章,比如深入淺出聊unity3d專案優化:從draw calls到gc。但是,總有乙個大家都繞不開的點,那就是drawcall,drawcall數量直接影響了遊戲的幀率,解決了drawcall問題,應該算是解決了80%的問題,所以接下來就著重針對ugui的特性講一講ui系統的drawcall。
unity 5.0在drawcall檢視方面有乙個非常有用的工具,frame debugger,通過[window->frame debugger]開啟。
使用該工具時,遊戲會暫停,然後unity會將當前正在執行的一幀的內容快取下來,其中所有drawcall你都可以進行前進與後退操作,從而能夠從drawcall級別分析開銷。所以沒有公升級5.0的小夥伴趕緊公升級啊。
此外,在用fd看ui效能時,有乙個小竅門就是新開乙個空的scene,然後將你的ui prefab拖到該空場景中,此時就不會受場景中其它物體的影響而只顯示ui的drawcall了。
講了那麼多,開始進入正題。
在降低drawcall方面,乙個非常重要的概念就是batch,因為一次drawcall相當於cpu與gpu進行一次溝通的成本,如果cpu能一次多打包一些資訊給gpu,那麼drawcall數量自然就下來了,這個打包傳輸資訊給gpu的過程就叫做batch,批處理。那麼什麼情況下這些資訊可以打包呢?從ugui的角度,如果你的ui中元件的材質與紋理均相同,這幾個元件就可以被batch。在image元件中,材質對應source image,紋理則對應material;在text元件中材質對應font,紋理也是material。以上對應大部分情況適用,在少部分特殊shader下會失效(待深入研究)。
原理是這樣,但是實際用起來還需要一些技巧,遵循unity的一些渲染次序的規則,才能真正的實現效能優化。以下就一一進行討論。
上面有提到source image圖集,所謂的圖集,就是將好多張零碎的2d小通過unity自帶的sprite packer或第三方的texture packer合併到一張大圖,這樣做有幾大好處,
尺寸為2的次冪時,gpu處理起來會快很多,小圖自己是做不到每張圖都是2的次冪的,但打成一張大圖就可以(浪費一點也無所謂);
cpu在傳送資源資訊給gpu時,只需要傳一張大圖就可以了,因為gpu可以在這張圖中的不同區域進行取樣,然後拼出對應的介面。注意,這就是為什麼需要用同乙個source image圖集的原因,是batch的關鍵,因為乙個drawcall就把所有原材料傳過去了,gpu你畫去吧。
但是顯然把所有打成一張圖集是不合理的,因為這張圖可能非常大,所以就要按照一定規則將進行分類。在分類思路上,我們希望做到drawcall盡可能少,同時資源量也盡可能少(多些重用),但這兩者某種程度上是互斥的,所以折衷一下,可以遵循以下思路:
p.s. 如果你用unity自帶的sprite packer去打包圖集,那麼你可能要在執行模式下才能看到效果。
ugui的層疊順序是按照hierarchy中的順序從上往下進行的,也就是越靠上的元件,就會被畫在越底部。所以ui就是這樣一層一層地疊上去畫出來的。當然這樣乙個乙個地畫效率肯定是不能接受的,所以要合併,要batch,unity自身就提供了乙個演算法去決定哪些層應該合併到一起,並以什麼樣的順序進行繪製。所有相鄰層的可batch的ui元素將會在乙個drawcall完成。接下來就來討論一下unity的層級合併與計算演算法。
unity的ui渲染順序的確定有2個步驟,第一步計算每個ui元素的層級號;第二步合併相同層級號中可以batch的元素作為乙個批次,並對批次進行排序;
先從直觀的角度來解釋計算層級號的演算法:如果有乙個ui元素,它所佔的螢幕範圍內(通常是矩形),如果沒有任何ui在它的底下,那麼它的層級號就是0(最底下);如果有乙個ui在其底下且該ui可以和它batch,那它的層級號與底下的ui層級一樣;如果有乙個ui在其底下但是無法與它batch,那它的層級號為底下的ui的層級+1;如果有多個ui都在其下面,那麼按前兩種方式遍歷計算所有的層級號,其中最大的那個作為自己的層級號。
function callayer(list uielelst)
if(uielelst.count == 0 ) return;
//initial the first ui element as layer 0
uielelst[0].layer = 0;
for(i = 1 ~ uielelst.count)
else}}
//if not collide with any elements beneath, set layer to 0
if(!iscollidewithelements)
}
有了層級號之後,就要合併批次了,此時,unity會將每一層的所有元素進行乙個排序(按照材質、紋理等資訊),合併掉可以batch的元素成為乙個批次,目前已知的排序規則是,text元件會排在image元件之前渲染,而同一類元件的情況下排序規則未知(好像並沒什麼規則)。經過以上排序,就可以得到乙個有序的批次序列了。這時,unity會再做乙個優化,即如果相鄰間的兩個批次正好可以batch的話就會進行batch。舉個栗子,乙個層級為0的imagea,乙個層級為1的imageb(2個image可batch)和乙個層級為0的textc,unity排序後的批次為textc->imagea->imageb,後兩個批次可以合併,所以是2個drawcall。再舉個栗子,乙個層級為0的textd,乙個層級為1的texte(2個text可batch)和乙個層級為0的imagef,unity排序後的批次為textd->imagef->texte,這時就需要3個drawcall了!(是不是有點暈,再回顧下黑體字)
以下的偽**有些偷懶,實在懶得寫排序、合併之類的,一長串也不好讀,幾個步驟列一下,其它諸位看上面那段文字腦補下吧...
function
mergebatch(list uielelst)
);//merge the ui elements with same layer and batch-key as a batch
var batchlst = uielelst.mergesameelementsasbatch();
//make adjacent batches with same batch-key merged
batchlst.mergeadjacentbatches();
return batchlst;
}
根據以上規則,就可以得出一些「擺ui」的技巧:
mask對於ugui效能來說是噩夢一般的存在,因為很可能因為這個東西,導致drawcall數量成倍增長。
mask實現的具體原理是乙個drawcall來建立stencil mask(來做畫素剔除),然後畫所有子ui,再在最後乙個drawcall移掉stencil mask。這頭尾兩個drawcall無法跟其他ui操作進行batch,所以表面上看加個mask就會多2個drawcall,但是,因為mask這種類似「漢堡包式」的渲染順序,所有mask的子節點與其他ui其實已經處在兩個世界了,上面提到的層級合併規則只能分別作用於這兩個世界了,所以很多原本可以合併的ui就無法合併了。
所以,在使用ugui時,有一些建議:
Unity GUI uGUI 使用心得與效能總結
小哈接觸unity3d也有一段時間了,專案組在ui解決方案的選型一直是用的原生的ugui,因此本人也是使用了一段時間的ugui,在ugui的使用方面積累了一些自己的經驗,在此進行乙個記錄與總結。本文接下來將會對ugui的runtime效能進行著重討論,其它的因素也很多而且很重要,但是一篇文章講清楚一...
使用HtmlParser使用心得
最近因工作的需要,需要檢查html那些不合理或則什麼沒有閉合。在網上找了很久都沒有找到比較合適的工具。於是句試著搞搞htmlparser。獲取html的 string getcontentfromurl string url content sb.tostring response.close re...
frameset 使用心得
欲明白本篇 html徹底剖析 之標記分類,請看 標記一覽 也請先明白圍堵標記與空標記的分別,請看 html概念 框架概念 謂框架便是網頁畫面分成幾個框窗,同時取得多個 url。只需要 即可,面所有框架標記需要放在乙個總起的 html 檔,這個檔案只記錄了該框架如何分割 不會顯示任何資料,所以不必放入...