Unity UI優化小結

遊資網發表於2019-03-04


最近在做Unity的專案,負責UI相關的工作,學習了一下Unity UGUI更新的原理,以及優化相關的部分。本文主要參考UWA的分享,UWA專注效能優化,感覺有很多值得學習的文章,UWA-簡單優化、優化簡單,打好理論基礎,少走彎路,後面實際專案中就是儘可能去實現這些細節了。

目錄

•1.元素更新方式

•2.Draw Call合併規則

•3.網格更新機制

•4.降低介面的渲染開銷

•5.降低介面的更新開銷

1.元素更新方式

UGUI

public class VertexHelper:IDisposable
{
    private List<Vector3> m_Position = ListPool<Vector3>.Get();
    private List<Color32> m_Colors = ListPool<Color32>.Get();
    private List<Vector2> m_Uv0S = ListPool<Vector2>.Get();
    private List<Vector2> m_Uv1S = ListPool<Vector2>.Get();
    private List<Vector3> m_Normals = ListPool<Vector3>.Get();
    private List<Vector4> m_Tangents = ListPool<Vector4>.Get();
    private List<int> m_Indices = ListPool<int>.Get();
}

有這樣一個VertexHelper類,和UI元素有一一對應的關係,包含頂點資訊,UV,顏色,等等當UI元素髮生變化的時候,就會從位置,長寬等陣列填充這些list。

對製作的影響

當UI發生改變的時候,須要對陣列的元素進行更新,“動態元素”少用Outline,Tiled Sprite儘量減少“動態”長文字

Unity UI優化小結

如上圖Tiled生成了大量網格,在填充的時候耗時更長。OutLine,是通過把一個四邊形重複5次,畫出的OutLine的效果,會使文字的定點數乘以5,使更新的陣列過長。

更新方式

•UIPanel.LateUpdate

o輪詢

o UIPanel.UpdateWidgets

•Cavans.SendWillRenderCanvas

o佇列

o m_LayoutRebuildQueue

o m_GraphicRebuildQueue

NGUI每幀更新UIPanel,輪詢,不管發生變化與否,哪怕是靜態的,還是會有開銷

UGUI更新包含2個佇列,渲染之前在SendWillRenderCanvas的回掉裡面處理2個佇列的元素,如果大量靜態,消耗幾乎為0。

對動態HUD快取機制的影響

•NGUI

o適量元素:Color.a=0,移出

o大量元素:SetActive(false)

o Time+二級快取

•UGUI

o Scale=0,Alpha Group=0

如血條,傷害數字,經常會出現消失的UI元素,如果出現就建立,消失就destory,開銷會非常大。所以通常的做法通過快取,如果通過SetActive有時候會有額外的開銷,

UGUI通常的操作方式可以通過scale=0,或則Alpha Group為0,可以快速隱藏,不要直接alpha=0,在draw call上是沒變化的,實際上還是畫了個透明度為0的面片。

NGUI中和UGUI相反,如果設定alpha=0,是會把頂點移除掉,可以減少setActive的開銷。

2.DrawCall合併規則

渲染順序

•NGUI:Depth

o設定depth值,以UIPanel為單位,按照大小進行排序,相同材質進行合併

•UGUI:hierarchy

o重疊檢測

o分層合併

存在優勢,也有一些問題,UGUI的合併規則是進行重疊檢測,然後分層合併。下面的例子中,不同顏色代表不同圖集。

Unity UI優化小結

第一個圖,4種顏色,左邊和右邊數序相同,藍色是0層,白色都是1層,這樣會分層合批成4個DrawCall。

第二個圖,左邊的藍色是0層,右邊的黑色是0層藍色是1層,這種情況下不會合批,所以會是9個drawCall

第三個圖,把黑色延長到重疊的地方,黑色同處0層,所以DrawCall又降到了5。

所以在製作UI的時候,須要考慮層級關係,結合UGUI的合批規則,這樣可以達到對drawCall的優化,

除錯工具

•NGUI:DrawCall tool

•UGUI:Frame debugger

NGUI可以通過DrawCall tool看到多少個三角面,多少個widgets,通過觀察widgets的關係,對NGUI層級直接調整,來進行合批。

NGUI使用drawcall tool,通過調整index,把相同材質的放在同一層。

UGUI用frame Debug看每個drawcall繪製了哪些東西,再做調整

Unity UI優化小結

對介面的影響

•UGUI

o不規則圖示的擺放

o UI元素的旋轉

o動態遮擋

o 3D UI

•NGUI

o手動排序

UGUI中,對於不規則圖形,視覺上icon沒有重疊,但是UI層是包圍盒的形式,Icon重疊了,UGUI在判斷的時候沒辦法進行合併。

UGUI對於發生旋轉的UI,包圍盒是會發生重疊,會限制UGUI在合併DrawCall的操作。

如下圖:

Unity UI優化小結

NGUI把不同的元素設在一個圖集中,進行同批次繪製。

3.網格更新的機制

•UIPanel.LateUpdate兩種更新方式

o UIPanel.FillDrawCall更新單個DrawCall

o UIPanel.FillAllDrawCall更新所有DrawCall

•Canvas.BuildBatch更新所有DrawCall

o WaitingForJob子執行緒網格合併

o PutGeometryJobFence

o BatchRendere.Flush UI如果開多執行緒渲染,BatChRender.Flush會增高,主執行緒在等待子執行緒的結果時Flush會等待。

NGUI根據不同的DrawCall合併不同的網格UGUI以Canvas為單位,一個Canvas下的元素,合併成一個Mesh,不同的UI元素會以SubMeshes的形式存在。UGUI中如果一個Canvas中有很複雜的動態元素,儘量將靜態元素拆分出來,確保更新的效率。

Unity UI優化小結

優化方法:

•UGUI

o拆分Canvas

•NGUI

o控制FillAllDrawCalls

o拆分UIPanel

效能比較

•功能介面的DrawCall控制NGUI>UGUI(NGUI通過DC樹,通過調整Index進行調整)

•功能介面的網格更新機制NGUI>UGUI(UGUI更新任何一個UI,都會更新整個Canvas)

•動態HUD介面的網格更新機制UGUI>>NGUI(UGUI在處理動態UV的元素,如血條,動態UI會更有優勢)

•堆記憶體控制UGUI>>NGUI(NGUI堆記憶體佔用更高)

參考https://blog.uwa4d.com/archives/Implosion.html

4.降低介面的渲染開銷

•Profiling定位

•DrawCall控制

•Mesh.CreateVBO UI變化的網格開銷

•Overdraw UI比較容易產生Overdraw

Profiling

UGUI非多執行緒渲染Unity5.3主要集中在RenderSubBatch,

Unity UI優化小結

DrawCall控制

Z值!=0

合併時只會合併相鄰層級,相同圖集的元素

左邊的圖,4個血條紅色和白色的z值相同,共2個drawcall,但是右邊的圖,紅色和白色穿插,變成8個drawcall,在3D UI的時候尤其明顯,2DUI不要通過這種方法,改Z值,因為2D改了之後,

Unity UI優化小結

未“隱藏”的元素

包含Null Sprite,Color.a=0螢幕外

對於隱藏的元素,NGUI的image元件中,alpha為空和sprite為空,都是佔用drawcall渲染的,而且會打斷前後的drawcall,穿插在上下2個元素中間的時候。

Unity UI優化小結

Hierarchy穿插+重疊

如下圖紅點和Icon在不同圖集中,如果紅點稍微大一點,遮擋了旁邊的Icon,就不能合批,須要調整Icon和紅點的節點關係,4個Icons放在一個節點下,4個紅點放在一個借點下。在同步位置的時候可能稍微麻煩有點,須要寫個指令碼同步位置。

Unity UI優化小結

Unity UI優化小結

圖集分離

可能因為壓縮方式的不同,導致UI的sprite在不同圖集中,也會影響渲染開銷,不同圖集中無法進行合批

Unity UI優化小結

OverDraw

•減少UI層疊

•遮擋場景時,關閉場景相機

•不用Image檢測事件

參考:https://blog.uwa4d.com/archives/video_UI.html

5.降低介面的更新開銷

•動靜分離

•降低更新頻率

•避免“敏感”操作

•優化選項

動靜分離

在UGUI中細分Canvas下圖中,血量和經驗條會經常更新,如果在一個canvas中,PutGeometryJbFence和WaitngForJob,buildBatch出現的時候,表示更新的開銷在子執行緒中,主執行緒處在一個等待的狀態,差不多有5,6毫秒的等待。

Unity UI優化小結

拆分之後剛才的WaitingForJob等都沒有了,動態的canvas開銷就會很小。

Unity UI優化小結

降低更新的頻率

•設定移動閾值

•設定更新頻率

Unity UI優化小結

比如像小地圖這樣的介面,可能移動了一小段距離,小地圖上更新了也不明顯,可以通過設定閾值的方法,降低開銷,或者直接設定更新時間。

避免“敏感”操作

•元素的Position賦值->Canvas.BuildBatch

下面的一個例子是在Canvas中,所有元素基本是靜態的,但是有個元素,在Update中,會跟隨target的position,每次傳送改變的時候,會重建整個canvas,導致資源的浪費。

Unity UI優化小結

參考文獻:

https://blog.uwa4d.com/

[《聚爆Implosion》效能精析UI部分]

UGUI研究院之全面理解圖集與使用

專欄地址:https://zhuanlan.zhihu.com/p/43111806

相關文章