Unity UI優化小結
來自專欄司虎虎的專欄45 人贊了文章
最近在做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 盡量減少「動態」長文本
如上圖Tiled生成了大量網格,在填充的時候耗時更長。 OutLine,是通過把一個四邊形重複5次,畫出的OutLine的效果,會使文本的定點數乘以5,使更新的數組過長。
更新方式
- UIPanel.LateUpdate
- 輪詢
- UIPanel.UpdateWidgets
- Cavans.SendWillRenderCanvas
- 隊列
- m_LayoutRebuildQueue
- m_GraphicRebuildQueue
NGUI每幀更新UIPanel,輪詢,不管發生變化與否,哪怕是靜態的,還是會有開銷
UGUI更新包含2個隊列,渲染之前在SendWillRenderCanvas的回掉裡面處理2個隊列的元素,如果大量靜態,消耗幾乎為0。
對動態HUD緩存機制的影響
- NGUI
- 適量元素:Color.a= 0,移出
- 大量元素:SetActive(false)
- Time + 二級緩存
- UGUI
- 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
- 設置depth值,以UIPanel為單位,按照大小進行排序,相同材質進行合併
- UGUI:hierarchy
- 重疊檢測
- 分層合併
存在優勢,也有一些問題,UGUI的合併規則是進行重疊檢測,然後分層合併。下面的例子中,不同顏色代表不同圖集。
第一個圖,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繪製了哪些東西,再做調整
對界面的影響
- UGUI
- 不規則圖標的擺放
- UI元素的旋轉
- 動態遮擋
- 3D UI
- NGUI
- 手動排序
UGUI中,對於不規則圖形,視覺上icon沒有重疊,但是UI層是包圍盒的形式,Icon重疊了,UGUI在判斷的時候沒辦法進行合併。
UGUI對於發生旋轉的UI,包圍盒是會發生重疊,會限制UGUI在合併DrawCall的操作。
如下圖:
NGUI把不同的元素設在一個圖集中,進行同批次繪製。
3.網格更新的機制
- UIPanel.LateUpdate 兩種更新方式
- UIPanel.FillDrawCall 更新單個DrawCall
- UIPanel.FillAllDrawCall 更新所有DrawCall
- Canvas.BuildBatch 更新所有DrawCall
- WaitingForJob 子線程網格合併
- PutGeometryJobFence
- BatchRendere.Flush UI如果開多線程渲染,BatChRender.Flush會增高,主線程在等待子線程的結果時Flush會等待。
NGUI根據不同的DrawCall 合併不同的網格 UGUI以Canvas為單位,一個Canvas下的元素,合併成一個Mesh,不同的UI元素會以SubMeshes的形式存在。UGUI中如果一個Canvas中有很複雜的動態元素,盡量將靜態元素拆分出來,確保更新的效率。
優化方法:
- UGUI
- 拆分Canvas
- NGUI
- 控制FillAllDrawCalls
- 拆分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,
DrawCall控制
Z值!=0
合併時只會合併相鄰層級,相同圖集的元素
左邊的圖,4個血條紅色和白色的z值相同,共2個drawcall,但是右邊的圖,紅色和白色穿插,變成8個drawcall,在3D UI的時候尤其明顯,2DUI不要通過這種方法,改Z值,因為2D改了之後,
未「隱藏」 的元素
包含 Null Sprite, Color.a = 0 屏幕外
對於隱藏的元素,NGUI的image組件中,alpha為空和sprite為空,都是佔用drawcall渲染的,而且會打斷前後的drawcall,穿插在上下2個元素中間的時候。
Hierarchy 穿插+重疊
如下圖紅點和Icon在不同圖集中,如果紅點稍微大一點,遮擋了旁邊的Icon,就不能合批,須要調整Icon和紅點的節點關係,4個Icons放在一個節點下,4個紅點放在一個借點下。在同步位置的時候可能稍微麻煩有點,須要寫個腳本同步位置。
圖集分離
可能因為壓縮方式的不同,導致UI的sprite在不同圖集中,也會影響渲染開銷,不同圖集中無法進行合批
OverDraw
- 減少UI層疊
- 遮擋場景時,關閉場景相機
- 不用Image檢測事件
參考: https://blog.uwa4d.com/archives/video_UI.html
5.降低界面的更新開銷
- 動靜分離
- 降低更新頻率
- 避免「敏感」操作
- 優化選項
動靜分離
在UGUI中細分Canvas 下圖中,血量和經驗條會經常更新,如果在一個canvas中,PutGeometryJbFence和WaitngForJob,buildBatch出現的時候,表示更新的開銷在子線程中,主線程處在一個等待的狀態,差不多有5,6毫秒的等待。
拆分之後剛才的WaitingForJob等都沒有了,動態的canvas開銷就會很小。
降低更新的頻率
- 設定移動閾值
- 設定更新頻率
比如像小地圖這樣的界面,可能移動了一小段距離,小地圖上更新了也不明顯,可以通過設定閾值的方法,降低開銷,或者直接設定更新時間。
避免「敏感」操作
- 元素的Position賦值->Canvas.BuildBatch
下面的一個例子是在Canvas中,所有元素基本是靜態的,但是有個元素,在Update中,會跟隨target的position,每次發送改變的時候,會重建整個canvas,導致資源的浪費。
參考文獻:
https://blog.uwa4d.com/
[《聚爆Implosion》性能精析 UI部分]
UGUI研究院之全面理解圖集與使用
推薦閱讀:
TAG:Unity遊戲引擎 |