一负饲、Canvas Batch
一個Canvas繪制Mesh的過程中搀庶,如果每個UI元素都繪制一次(DrawCall)蜕径,則耗時會很長。因此Canvas會先對其UI元素按照材質和渲染順序排序懂从,將能夠一起繪制的UI元素合并到一個Mesh中,然后繪制蹲蒲。這樣可以有效減少DrawCall
二番甩、ReBuild
ReBuild是Canvns ReBatch過程中完成的
ReBuild是重建UI元素時的操作,它會改變UI的Mesh信息届搁,進而觸發(fā)ReBatch缘薛。當修改了UI元素的縮放,尺寸卡睦,材質宴胧,旋轉等信息時都會觸發(fā)重建
-
在WillRenderCanvases事件調用PerformUpdate::CanvasUpdateRegistry接口
- 通過ICanvasElement.Rebuild方法重新構建Dirty的Layout組件
- 通過ClippingRegistry.Cullf方法,任何已注冊的裁剪組件Clipping Compnents(Such as Masks)的對象進行裁剪剔除操作
- 任何Dirty的 Graphics Compnents都會被要求重新生成圖形元素
-
Layout Rebuild
- UI元素位置表锻、大小牺汤、顏色發(fā)生變化
- 優(yōu)先計算靠近Root節(jié)點,并根據(jù)層級深度排序
-
Graphic Rebuild
- 頂點數(shù)據(jù)被標記成Dirty
- 材質或貼圖數(shù)據(jù)被標記成Dirty
三浩嫌、Canvas和Graphic
- Canvas負責管理UGUI元素,負責UI渲染網(wǎng)格的生成與更新补胚,并向GPU發(fā)送DrawCall指令
- UI組件的基類是Graphic码耐,Graphic核心是組織Mesh和Material傳到底層(CanvasRenderer)
-
CanvasRenderer將Mesh和Material傳遞給Canvas,Canvas做合批溶其。CanvasRenderer的兩個核心SetMesh和SetMaterial骚腥。每個Graphic有一個CanvasRenderer,保存了保存當前元素的Mesh和材質瓶逃。一個Graphic為dirty束铭,Canvas就要重新合批
四、UGUI渲染細節(jié)
- UGUI中渲染是在Transparent半透明渲染隊列中完成的厢绝,半透明隊列的繪制順序是從后往前畫契沫,由于UI元素做Alpha Blend,我們在做UI時很難保障每一個像素不被重畫,UI的Overdraw太高昔汉,這會造成片元著色器利用率過高懈万,造成GPU負擔。
- UI SpriteAtlas圖集利用率不高的情況下靶病,大量完全透明的像素被采樣也會導致像素被重繪会通,造成片元著色器利用率過高;同時紋理采樣器浪費了大量采樣在無效的像素上娄周,導致需要采樣的圖集像素不能盡快的被采樣涕侈,造成紋理采樣器的填充率過低,同樣也會帶來性能問題煤辨。
五裳涛、Unity UI性能的四類問題
1.1 Canvas Re-batch 時間過長
指Canvas分析其UI節(jié)點信息木张,生成最優(yōu)批次的過程
耗時:節(jié)點數(shù)量,層級深度调违,元素重疊窟哺,材質數(shù)量等
觸發(fā)時機:當Canvas中的UI節(jié)點的Mesh改變時(SetActive,transform技肩,顏色……..)
觸發(fā)過程:根據(jù)UI元素深度關系進行排序且轨、檢查UI元素的覆蓋關系、檢查UI元素材質并進行合批
觸發(fā)結果:重新Batch一次虚婿,無論當前Mesh的改變是否影響了父節(jié)點(也就是說一個很小的變化旋奢,就會讓當前Canvas重新Batch一次)
-
解決方案:
- 可以用多個同級Canvas或者Canvas套Cnavas的方法,來將復雜層級劃分到不同Canvas中去然痊,需要注意的是每個Canvas都至少有一個DrawCall(當有子UI元素時)至朗,這樣可能會增加DrawCall
- 使用 動靜Canvas分離的方案可以將ReBatch的范圍限制在動態(tài)Canvas中
- 減少層級復雜度,少用嵌套的樹形UI結構剧浸,同時將材質相同的UI盡量放到同一層級
1.2 Canvas Over-dirty, Re-batch次數(shù)過多
- 每當UI元素的Mesh改變時锹引,都會觸發(fā)ReBatch來重新計算,因此我們要盡量減少UI元素Mesh的改變
-
兩個按鈕中不疊加時唆香,合批為2
-
當?shù)谝粋€按鈕文字和第二個按鈕背景重疊時嫌变,合批為4
1.3 生成網(wǎng)格頂點時間過長
-
可以看到Cumulative Vertex Count數(shù)量,越多耗時越久
1.4 片元利用率高 - Fill-rate overutilization
-
一個像素被多次繪制躬它,稱為片元利用率高腾啥,一次繪制中,如果一個像素被反復繪制N次冯吓,這樣會導致GPU利用率過高
六倘待、使用Canvas的基本準則
- 將所有可能打斷合批的層移到最下邊的圖層,盡量避免UI元素出現(xiàn)重疊區(qū)域
- 可以拆分使用多個同級或嵌套的Canvas來減少Canvas的Rebatch復雜度
- 拆分動態(tài)和靜態(tài)對象放到不同Canvas下组贺。
- 盡量不使用Layout組件
- Canvas的RenderMode盡量Overlay模式凸舵,減少Camera調用的開銷
七、Raycast優(yōu)化
- 必要的需要交互UI組件才開啟“Raycast Target”
- 開啟“Raycast Targets”的UI組件越少失尖,層級越淺贞间,性能越好
- 對于復雜的控件,盡量在根節(jié)點開啟“Raycast Target”
- 對于嵌套的Canvas雹仿,OverrideSorting屬性會打斷射線增热,可以降低層級遍歷的成本
八、UI字體
避免字體框重疊胧辽,造成合批打斷
-
字體網(wǎng)格重建
- UIText組件發(fā)生變化時
- 父級對象發(fā)生變化時
- UIText組件或其父對象enable/disable時
-
TrueTypeFontImporter
-
支持TTF和OTF字體文件格式導入
-
-
動態(tài)字體與字體圖集
- 運行時峻仇,根據(jù)UIText組件內容,動態(tài)生成字體圖集邑商,只會保存當前Actived狀態(tài)的 UIText控件中的字符
- 不同的字體庫維護不同的Texture圖集
- 字體Size摄咆、大小寫凡蚜、粗體、斜體等各種風格都會保存在不同的字體圖集中(有無必要吭从,影響圖集利用效率朝蜘,一些利用不多的特殊字體可以采用圖片代替或使用Custom Font,F(xiàn)ont Assets Creater創(chuàng)建靜態(tài)字體資源)
- 當前Font Texture不包含UIText需要顯示的字體時涩金,當前Font Texture需要重建
- 如果當前圖集太小谱醇,系統(tǒng)也會嘗試重建,并加入需要使用的字形步做,文字圖集只增不減
- 利用Font.RequestCharacterInTexture可以有效降低啟動時間
九副渴、UI控件優(yōu)化注意事項
- 不需要交互的UI元素一定要關閉Raycast Target選 項
- 如果是較大的背景圖的UI元素建議也要使用Sprite的九宮格拉伸處理,充分減小UI Sprite大小全度,提高UI Atlas圖集利用率
- 對于不可見的UI元素煮剧,一定不要使用材質的透明度控制顯隱,因為那樣UI網(wǎng)格依然在繪制将鸵,也不要采用active/deactive UI控件進行顯隱勉盅,因為那樣會帶來gc和重建開銷
- 使用全屏的UI界面時,要注意隱藏其背后的所有內容顶掉,給GPU休息機會草娜。
- 在使用非全屏但模態(tài)對話框時,建議使用OnDemandRendering接口一喘,對渲染進行降頻。
- 優(yōu)化裁剪UI Shader嗜暴,根據(jù)實際使用需求移除多余特性關鍵字凸克。
十、滾動視圖Scroll View優(yōu)化
- 使用RectMask2d組件裁剪
- 使用基于位置的對象池作為實例化緩存
參考文章
https://zhuanlan.zhihu.com/p/343524911
https://zhuanlan.zhihu.com/p/343978391