關於Unity渲染優化,你可能遇到這些問題

截止今天,UWA的【厚積薄發】欄目中已經沉澱了近200個性能優化的問題。所謂他山之石可以攻玉,不管是項目開發還是性能調優,無論是工作需要還是自我充電,此番集錦一定是你通往Unity大神之路上的靈丹妙藥。準備好了嗎?渲染篇開啟了!

關鍵字

Draw Call

半透明物體渲染

多層紋理渲染

Graphics.PresentAndSync

VBO

相機後處理特效

一、Draw Call相關

Q1:移動遊戲場景中,相同的怪物,Draw Call會動態合併嗎?如下設置可行嗎?

默認情況下,帶蒙皮的Mesh是不支持動態合批的。如果場景中相同材質的蒙皮網格數量很多,可以考慮通過插件MeshBaker來進行合併,具體方法大家可以參考《好插件讓你事半功倍!》

Q2:Draw Call和Setpass Call,這兩個指標主要是看哪一個?關於這點眾說紛紜,很多地方都是說看SetPass Call,但是在UWA的性能測試中,還是把Draw Call當成唯一指標。

在 Unity 5.x 中,SetPass Call與 Draw Call相比,SetPass Call的指標與性能相關性更大(比如Static Batching的開啟不影響Draw Call數,而SetPass Call通常會明顯下降)。但 SetPass Call在某些情況下也同樣存在問題,比如往一個場景中添加任意個相鄰且材質相同的大網格物體(使Dynamic Batching失效)時,SetPass Call並不會變化。因此在UWA中,我們所使用的是類似Profiler 中 Total Batches 這一項指標,通常該數值與 Frame Debugger 中的數值基本一致,因此可以通過該工具來查看每個Batch的內容,從而更有針對性地進行優化。

Q3:我UWA報告中「渲染模塊」界面中看到大部分場景中的三角面片數是正常的,但在某一幀時DrawCall、Traingle、蒙皮網格數驟然提升,請問這可能是什麼原因導致的?這三個參數的變化曲線是否有規律?為什麼我在切換場景的時候也會有400多的DrawCall呢?

從圖中看,這種情況發生在場景切換處。這種情況的發生原因很可能為一次性動態載入大量GameObejct,然後再手動Deactive當前並不需要使用的GameObject。研發團隊可以查看該峰值處的場景名稱和相關截圖,從而來進一步定位發生該問題的根本原因。

Q4:關於Static Batching, 場景中物件組合成大的Mesh,那麼判斷子Mesh要合入大的Mesh中的依據是什麼?材質?勾選Static?

我有一個模型A並勾選Static,使用材質A,怎麼看到也和其他材質的Mesh合併到一塊去了(Combined Mesh)?

勾選Static的GameObject下的Mesh都會被合入CombineMesh(無論什麼材質),且每個Mesh都作為SubMesh存在。在Unity 5.3之前,對於渲染順序相鄰且材質相同的SubMesh則會動態將其索引數組拼合,從而合成一個Draw Call。而Unity 5.3之後則不再拼合索引數組,因為在不切換材質時產生多個Draw Call的開銷並不大,而這多個Draw Call會被統計為一個Batch。

Q5:Unity對Dynamic Batching的數量是否有限制?或者說對Saved by Batch的數量是否有限制?

Unity對於任何Mesh的面片都有65536的個數限制,拼合後的面片數也是如此。

Q6:請教,角色分部件換裝可行嗎?比如衣服褲子分開,都是用Skinned Mesh Render,有沒有辦法合併降低Draw Call?

可以通過合併網格的方式來達到降低Draw Call的效果,具體可查看Asset Store中的換裝例子:Character Customization。但是,在角色換裝時需要注意以下幾點:

(1)裝備與角色必須是共用一套骨骼的;

(2)各裝備之間所用的材質必須相同。

開發者需要注意,只有同時滿足以上兩個條件時,才能達到只使用少量Draw Call來進行動態換裝的效果。

Q7:請問,Canvas里的東西移出了屏幕後,DrawCall沒降低,那麼它還會每幀去繪製嗎?

DrawCall沒降低,說明CPU依然將這部分的網格提交到了GPU。因此雖然UI元素已經不可見,但其CPU開銷(包括切換渲染狀態,提交VBO等)依然是在的,只是對GPU不會造成明顯影響,因為最終並沒有進行像素的渲染。

Q8:能否對提升NGUI的渲染效率提供一些思路?

開發團隊可以從以下幾點入手:

  1. 通常一個Panel會產生1個或多個Draw Call,以一個Panel為單位,Draw Call 的數量通常由當前 Panel 中使用的Atlas、Font的數量所決定。
  2. 要降低UI渲染時的 Draw Call數量則需要對 Atlas 的製作進行合理的規劃,即在保證使用較少的 Atlas 的同時,還需要保證 Atlas之間不會存在交叉遮擋。
  3. 要注意UI Texture的使用,每個UITexture自身會佔用一個Draw Call,同時如果其Depth值穿插在了其他來自相同Atlas的UISprite中,還會導致Draw Call的打斷,造成不必要的額外Draw Call。
  4. 另外還可以藉助Panel Tool和Draw Call Tool來對UI部分的Draw Call進行分析,前者可以顯示每個UIPanel包含了多少個Draw Call,而後者可以顯示每個Draw Call由哪些UIWidget組成。"

Q9:關於場景中玩家和NPC名字的DrawCall的問題。我們項目中是使用TextMesh掛到場景單位上,但是這樣每個名字就佔了一個DrawCall,請問有沒有好的辦法優化呢?

遊戲中的HUD的做法一般有兩種,一種是如上的做法,另一種則是通過NGUI/UGUI來製作HUD。第二種的實現方法大致如下:

  1. 計算屏幕中角色在屏幕中的位置;
  2. 根據屏幕中的位置來計算各自HUD的位置,並根據HUD的數量分別放置在一個或幾個Panel/Canvas下。

    第二種方法的優勢是儘可能用少的Draw Call數來渲染角色的HUD。開發團隊可以就該方法來進行嘗試。

二、半透明物體渲染相關

Q1:這個批渲染是什麼?好像開銷很高 。

從圖中看出,這是場景中不透明物體的渲染開銷。建議研發團隊對當時場景中的不透明物體(地形、建築等)進行進一步檢測,主要查看其三角面片數是否過高、Shader是否過於複雜等。

三、多層紋理渲染相關

Q1:我們遊戲用的是T4M,4層Tilling貼圖+1層融合貼圖,發現手機發熱現象嚴重,影響性能的表現,請問有什麼標準或者參考數據嗎?

對於中低端機器來說,我們建議地形紋理所刷的層數要儘可能小於3層。在中低端設備中,紋理採樣次數越多,則GPU的壓力越大,發熱效果也就越明顯。

在UWA性能測評報告中,我們加入了針對Graphics.PresentAndSync的統計,從而讓大家來看到項目運行過程中,GPU的壓力情況。同時,在設備的溫度顯示中,建議大家關注溫度的走勢圖,看看是否存在大幅向下回落的情況,如果存在,則很可能是設備因為過熱而主動降頻。

Graphics.PresentAndSync耗時統計

設備溫度走勢

Graphics.PresentAndSync相關

Q1:我們在編譯安卓版本時,在某些設備上(Sony L36h,小米4)調試時,發現有一個時間消耗項叫Graphics.PresentAndSync,該函數對性能的消耗會特別誇張(渲染20毫秒,這個能達到50毫秒)。查了相關文檔,發現該函數好像和安卓設備的垂直同步有關,但是大部分安卓設備的垂直同步是不可以關閉的。請問有什麼好的辦法解決嗎?

概括來說,該值很高表示 GPU 負擔很重,可以從降面,或者簡化shader入手。

Graphics.PresentAndSync 是指主線程進行Present時的等待時間和等待垂直同步的時間。該參數在Profiler中CPU佔用通常較高,且僅在發布版本中可以看到。究其原因,其實是CPU和GPU之間的垂直同步(VSync)導致的,主要是與項目是否開啟多線程渲染有關。當項目開啟多線程渲染時,你看到的則是Gfx.WaitForPresent;當項目未開啟多線程渲染時,看到的則是Graphics.PresentAndSync。

其中的原理,可以參照我們之前對其函數的詳細解釋:扒一扒Profiler中這幾個「占坑鬼」。

VBO相關

Q1:我們現在有一個場景,Draw Call和面數都在正常範圍內,Camera的距離也和其他場景一樣,但是VBO卻非常高。下圖是該場景的數據情況,該場景下有很多復用的模型,如果不勾選Static那VBO會下降,但是Draw Call會上升。那我能否通過合併模型的方式把VBO降下來呢?

當你勾選Static時,Unity 會將其進行 Static Batching,進而將生成一個較大的VBO來進行渲染,同時降低Draw Call。而如果不勾選時,則引擎無法對其進行Batch,所以VBO會降低,而Draw Call升高。

對此,我們的建議如下:

1、一般來講,降低Draw Call的意義大於降低VBO;

2、在Draw Call較低的情況下,比如當前的50+,可以考慮適當增加一些Draw Call來降低VBO的佔用,一方面可以降低帶寬的壓力,另一方面也可以適當降低一些內存。

相機後處理特效相關

Q1:關於抗鋸齒和BLOOM,有什麼好的優化方案或者優秀插件推薦?

通常在中低端的設備上,抗鋸齒並沒有比較高效的方案;而對於中高端的設備,可嘗試直接使用 Unity 內置的 MSAA 功能,但也只推薦使用 2x。

關於Bloom效果,以下是適用於移動端,且評價較好的一款插件:BloomPro

Q2:如何在移動設備上,對Bloom和全屏抗鋸齒進行優化?Unity標準資源裡面自帶的效率比較低(已經嘗試過Bloom(Optimized))。

建議使用Asset Store上適合移動端的Bloom Shader插件,比如FxPro: Bloom&DOF和BloomPro等。

對於AA,目前在移動設備上並沒有特別優化的方法,僅能建議在低端設備上關閉AA功能,而在高端設備上可嘗試開啟較低倍數(2x)的MSAA。

Shader解析相關

Q1:圖中的Material.SetPassFast佔用很高,這是我在第一次實例化一個特效,但是第二次實例化就不會出現高值了,請問能怎麼優化嗎?

該過程是在處理Shader,Unity 5.3以後在第一次顯示時才會將Shader進行Warmup,所以就會造成一次峰值卡頓。研發團隊可以參考我們之前的分享:Unity載入模塊深度解析之Shader篇以加深理解。

Q2:Shader.Parse 和 Shader.CreateGpuProgram 到底是做什麼的?它們什麼時候執行?

Shader.Parse體現的是Shader的載入和解析, Shader.CreateGpuProgram 是將Shader傳入GPU的一次提交,GPU驅動會對其進行編譯,以適應於特定的設備或平台。在Unity 5.x版本中,Shader.Parse在Shader資源載入時進行執行,而 Shader.CreateGpuProgram在所在GameObject第一渲染時進行執行。

其他

Q1:如下圖,我們發現WaitingForJob這個函數消耗過高導致了卡頓,請問該卡頓是否由於渲染壓力過大導致?

從圖中看,該線程最後是在等待 Canvas.sortjob,而這是 UI 排序造成的開銷(自Unity5.2版本開始,UGUI的部分計算已經移出了主線程)。

詳情參考:blogs.unity3d.com/2015/

因此理論上,這是 UI 的 canvas.sortjob 在指定的時間上沒有完成,從而使得渲染線程等待,且最終導致主線程進行等待而造成的開銷。

推薦閱讀:

#每天一個小目標#Unity技術分享(九)
Cloud Performance Platform:基於PProf的性能分析雲平台
UWA優化日廈門站回顧 | 資深TA教你如何將藝術概念轉化為渲染代碼
你應該知道的AssetBundle管理機制
千億特徵流式學習在大規模推薦排序場景的應用

TAG:Unity游戏引擎 | 性能优化 | 手机游戏开发 |