Unity移動端性能優化
01-28
1.渲染
- 利用reflect probe代替反射、折射,盡量不用RTT、GrabPass、RenderWithShader、CommandBuffer.Blit (BuiltinRenderTextureType.CurrentActive...)
- 建立統一後處理框架(bloom、hdr、DOF等)代替多後處理,可以共用模糊函數,減少多次blit;另外要注意RTT的尺寸。
- 空氣折射、熱浪扭曲等使用GrabPass不是所有硬體都支持,改為RTT或者後處理來優化。
- 建立統一shader材質代替單一shader,充分利用shader_feature、multi_compile,並將宏開關顯示於界面。
- 圖像混合代替多通道紋理,陰影投射、陰影接收、MetaPass、forwardadd 等pass不需要時要剔除。
- 少用alpha test、discard、clip、Alpha Converage等,因為會影響Early-Z Culling、HSR的優化。
- 避免Alpha Blend穿透問題(權重混合、深度剝離等透明排序方法代價太大了)。
- 光照貼圖代替動態陰影、盡量不用實時光;陰影貼圖、環境貼圖用16位代替32位;利用projector+rtt或者光圈代替實時陰影。
- 將環境參數(風、雨、太陽)等shader全局參數統一管理。
- 非主角可以用matcap代替pbr、無金屬不一定要用pbr,仔細選擇物理渲染所用的FDG(F:schlick、cook-torrance、lerp、要求不高用4次方,D:blinn-phong、beckmann、GGX、GGX Anisotropic,G:neumann、cook-torrance、Kelemen、SmithGGX;standard shader要注意選擇BRDF1-BRDF3),渲染要求不高時不用GGX;可以用LH來優化GGX。
- 用fixed、half代替float,建立shader統一類型(fixed效率是float的4倍,half是float的2倍),小心選擇shader變數的修飾(uniform、static、全局),選擇Mobile或Unlit目錄下shader
- 使用高低配渲染,內存足夠時可以考慮開啟mipmap
- 使用surface shader注意關掉不用的功能,比如:noshadow、noambient、novertexlights、nolightmap、nodynlightmap、nodirlightmap、nofog、nometa、noforwardadd等
- standard shader的變體太多(3萬多),導致編譯時間較長,內存佔用也很驚人(接近1G),如果使用要關掉沒用的shader_feature,比如:_PARALLAXMAP、SHADOWS_SOFT、DIRLIGHTMAP_COMBINED DIRLIGHTMAP_SEPARATE、_DETAIL_MULX2、_ALPHAPREMULTIPLY_ON;另外要去掉多餘的pass
- shaderforge、Amplify Shader Editor生成的shader有多餘代碼要程序專門優化,Amplify Shader Editor功能更強大一些,而且開源,建議學習。
- 不要用unity自帶terrian,因為即使只用3張splat圖,shader也是對應4個的,建議T4M或者轉為mesh。
- 模型和材質相同且數量巨大時用Instance來優化,比如草。
- 利用查找紋理(LUT)來優化複雜的光照渲染,比如:皮膚、頭髮、噴漆等。
- 盡量不要使用Procedural Sky,計算瑞麗散射和米氏散射效率比較低。
- 盡量不要使用speedtree,改為模型加簡單樹葉動畫,不過SpeedTreeWind.cginc裡面的動畫函數很豐富,TerrianEngine中的SmoothTriangleWave很好用。
- 多用調試工具檢查shader性能,常用工具有:FrameDebug、Nsight、RenderDoc 、AMD GPU ShaderAnalyzer / PVRShaderEditor、Adreno Profiler 、騰訊Cube、UWA等;另外可以內置GM界面,比如開關陰影,批量替換shader等方便真機調試。
2.腳本
- 減少GetComponent、find等查找函數在Update等循環函數中的調用、go.CompareTag代替go.tag 、
- 減少SendMessage等同步函數調用;減少字元串連接;for代替foreach,5.5以後版本foreach已經優化過了;少用linq;
- 大資源改為非同步載入
- 合理處理協程調用
- 將AI、網路等放在單獨線程
- 發布優化:關閉log、剔除代碼
- 偽隨機
- 腳本掛載類改為Manager等全局類實現
- lua中盡量不實現update、fixedupdate等循環函數,lua和csharp互調用的效率比較低。
3.內存管理
- 池子管理粒子、float UI等小資源,頻繁地GC會造成卡頓
- 必要時主動調用GC.Collect()
- 按照不同資源、不同設備管理資源生命周期,Resources.Load和Assetbundle統一介面,利用引用計數來管理生命周期,並列印和觀察生命周期。保證資源隨場景而卸載,不常駐內存,確定哪些是預載入,哪些泄漏。
- 內存泄漏(減少駐留內存):Container內資源不remove掉用Resources.UnloadUnusedAssets是卸載不掉的;對於這種情況,建議直接通過Profiler Memory中的Take Sample來對其進行檢測,通過直接查看WebStream或SerializedFile中的AssetBundle名稱,即可判斷是否存在「泄露」情況;通過Android PSS/iOS Instrument反饋的App線程內存來查看;
- 堆內存過大:避免一次性堆內存的過大分配,Mono的堆內存一旦分配,就不會返還給系統,這意味著Mono的堆內存是只升不降的。常見:高頻調用new;log輸出;
- CPU佔用高:NGui的重建網格導致UIPanel.LateUpdate(按照靜止、移動、高頻移動來切分);NGUI錨點自身的更新邏輯也會消耗不少CPU開銷。即使是在控制項靜止不動的情況下,控制項的錨點也會每幀更新(見UIWidget.OnUpdate函數),而且它的更新是遞歸式的,使CPU佔用率更高。因此我們修改了NGUI的內部代碼,使錨點只在必要時更新。一般只在控制項初始化和屏幕大小發生變化時更新即可。不過這個優化的代價是控制項的頂點位置發生變化的時候(比如控制項在運動,或控制項大小改變等),上層邏輯需要自己負責更新錨點。 載入用協程; 控制同一個UIPanel中動態UI元素的數量,數量越多,所創建的Mesh越大,從而使得重構的開銷顯著增加。比如,戰鬥過程中的HUD血條可能會大量出現,此時,建議研發團隊將運動血條分離成不同的UIPanel,每組UIPanel下5~10個動態UI為宜。這種做法,其本質是從概率上儘可能降低單幀中UIPanel的重建開銷。
- 資源冗餘:AssetBundle打包打到多份中;動態修改資源導致的Instance拷貝多份(比如動態修改材質,Renderer.meterial,Animation.AddClip)。
- 磁碟空間換內存:對於佔用WebStream較大的AssetBundle文件(如UI Atlas相關的AssetBundle文件等),建議使用LoadFromCacheOrDownLoad或CreateFromFile來進行替換,即將解壓後的AssetBundle數據存儲於本地Cache中進行使用。這種做法非常適合於內存特別吃緊的項目,即通過本地的磁碟空間來換取內存空間
4.美術
- 建立資源審查規範和審查工具:PBR材質貼圖製作規範、場景製作資源控制規範、角色製作規範、特效製作規範;利用AssetPostprocessor建立審查工具。
- 壓縮紋理、優化精靈填充率、壓縮動畫、壓縮聲音、壓縮UI(九宮格優於拉伸);嚴格控制模型面數、紋理數、角色骨骼數。
- 粒子:錄製動畫代替粒子、減少粒子數量、粒子不要碰撞
- 角色:啟用Optimize Game Objects減少節點,使用(SimpleLOD、Cruncher)優化面數。
- 模型:導入檢查Read/Write only、Optimize Mesh、法線切線、color、禁用Mipmap
- 壓縮紋理問題:壓縮可能導致色階不足;無透明通道用ETC1,現在安卓不支持ETC2已不足5%,建議放棄分離通道辦法。
- UI:儘可能將動態UI元素和靜態UI元素分離到不同的UIPanel中(UI的重建以UIPanel為單位),從而儘可能將因為變動的UI元素引起的重構控制在較小的範圍內; 儘可能讓動態UI元素按照同步性進行劃分,即運動頻率不同的UI元素儘可能分離放在不同的UIPanel中; 儘可能讓動態UI元素按照同步性進行劃分,即運動頻率不同的UI元素儘可能分離放在不同的UIPanel中;
- ugui:可以充分利用canvas來切分不同元素。
- 大貼圖會導致卡頓,可以切分為多個載入。
- iOS使用mp3壓縮、Android使用Vorbis壓縮
5.批次
- 開啟static batch
- 開啟dynamic batch:要求模型小於900頂點,用法線小於300,用切線小於180,縮放不一致、使用lightmap、多通道材質等會使dynamic batch無效。
- 減少GameObject,場景模型數量對fps影響巨大。
- 批次不是越少越好,過大的渲染數據會給匯流排傳輸帶來壓力。
6.物理
- 不需要移動的物體設為Static
- 不要用Mesh碰撞,角色不用碰撞體
- 觸發器邏輯優化
- 尋路頻率、AI邏輯頻率 、Fixed Timestep、降幀到30
- 出現卡頓的複雜計算,例如尋路、大量資源載入 可以用分幀或者協成非同步來處理
推薦閱讀:
※Unity3D 正六邊形,環狀擴散,緊密分布,的程序
※從零開始學基於ARKit的Unity3d遊戲開發系列11
※Unity 內存優化 和 內存池使用實踐
※Unity3D熱更新LuaFramework入門實戰(2)——資源熱更新