2017年3月6日 星期一

最佳實踐 - 了解Unity效能 - 資源審查

作者:Ian 原文

翻譯:Kelvin Lo / 海龜



Asset auditing - 資源審查

許多專案發生效能問題的真正原因只是由於人員操作不當或是試東試西,而不小心改到匯入設定影響到匯入的資源。(例如最近的gitlab慘案)

對於較大規模的專案,最好準備一道自動的防線防範人為失誤。例如寫一段簡單的檢查程式確保沒有任何人能在專案加入一張沒壓縮的 4K 貼圖。

或許你會覺得不可能,但這問題我們真的很常見。沒有壓縮的 4K 貼圖會占用大約 60mb 的記憶體空間,在低端的手機設備(例如 iPhone 4s)上,整個專案用掉超過 180mb~200mb 就會很危險。有時候你的遊戲在好的手機上跑沒問題,在差的手機上跑會當機,不一定是硬體的問題。如果犯這種錯誤,這張貼圖會無端占用應用程式四分之一到三分之一的可用記憶體,造成很難追蹤的記憶體不足錯誤。

雖然上面有提到可以用 Unity 5.3 的 Memory Profiler 來追蹤這樣的問題,但最好養成良好開發習慣排除這樣的可能。 


使用資源後處裡器(Asset Postprocessors)


Unity 編輯器裡的 AssetPostprocessor 類別可以用在 Unity 專案上實行某些基本限制。這個類別在資源匯入時會收到一個回呼。使用方法即繼承 AssetPostprocessor 並實作一個或多個 OnPreprocess 方法,重要的包含:

  • OnPreprocessTexture 
  • OnPreprocessModel 
  • OnPreprocessAnimation 
  • OnPreprocessAudio 

請查詢 AssetPostprocessor API 文件以了解更多關於 OnPreprocess 方法的資料。

public class ReadOnlyModelPostprocessor : AssetPostprocessor { public void OnPreprocessModel() { ModelImporter modelImporter = (ModelImporter)assetImporter; if(modelImporter.isReadable) { modelImporter.isReadable = false; modelImporter.SaveAndReimport(); } } }

這是一個簡單的 AssetPostprocessor 限制規則範例

每當匯入模型到專案或模型的匯入設定(Import settings)被修改時會呼叫這個類別,這裡程式只是檢查可否讀寫模型的旗標 isReadable 屬性,如果是 true 就會改為 false,存檔後重新匯入資源。

請注意,呼叫 SaveAndReimport 會導致這段程式會被再次呼叫!但由於旗標已經被改為 false,所以不會無窮遞迴下去。

至於為何要拿掉寫入的旗標會在底下的模型章節解釋。

常見資源規則

Textures - 材質

關閉 Read/Write Enabled 旗標

這個 Read/Write Enabled 的旗標會造成貼圖在記憶體裡變成兩份,一份在 GPU 上一份在 CPU 可以定址的記憶體上。這是因為大多數平台,把資料從 GPU 記憶體讀回 CPU 很慢。從 GPU 記憶體讀取一張貼圖到暫存區給 CPU 程式用(例如:Texture.GetPixel)會導致效能很差。這個設定在 Unity 裡預設是關閉的,但要避免誤勾這個選項。

Read/Write Enabled 只有在 Shader 以外的地方存取貼圖資料(例如:Texture.GetPixel 和Texture.SetPixel 這樣的 API)時才會需要,但儘量避免使用這個功能比較好。

如果可以,儘量不要用 Mipmaps 

對於物件相對於鏡頭之間的 Z 軸深度不太會有變化的物件,關閉 Mipmaps 可以省下約三分之一的記憶體空間,但如果物件在鏡頭看出去的深度會有變化,關閉 Mipmaps 會影響到 GPU 貼圖取樣的效率。

一般來說,關閉這個選項對於 UI 和不太會變大變小的貼圖很有幫助。

壓縮全部貼圖

針對各個不同的平台選用該平台適合的壓縮格式壓縮貼圖,這是節省記憶體的首要之務。

如果選到目標平台不支援的格式,Unity 會在 CPU 上解壓縮貼圖,消耗 CPU 時間和很多記憶體。這類問題比較常發生在 Android 上,開發者得注意 Android 裝置上各種不同的晶片各支援哪種壓縮格式。

強制貼圖大小

這雖然是件小事但很多人會忘記調整貼圖大小或不小心從改到匯入設定裡的貼圖大小上限。先決定各種貼圖的合理大小上限然後用程式去強制限制它。

大多數的手機遊戲用 2048x2048 或 1024x1024 來存放圖集(Atlas)就夠了,3D 手機遊戲的模型貼圖用到 512x512 應該就夠好了。

Models - 模型

關閉 Read/Write Enabled 旗標

這個選項的運作原理和貼圖一樣,只不過預設是打開的。

當專案執行時想用程式來修改 Mesh,或者如果 Mesh 要用作 MeshCollider 的話,這裡需要打勾。反之如果模型沒用在 MeshCollider,也沒用程式來修改 Mesh 的話,關閉這裡可以省下一半的記憶體。

非角色模型關閉骨架功能

在預設情況下,Unity 會幫非角色的模型放入一個通用骨架(Generic rig),如果這個模型執行時被實例化,會導致被加上一個 Animator 元件。如果這個物件沒有用到 Mecanim 動畫系統就會產生不必要的開銷,因為所有啟動中的 Animator 每幀都會被觸發一次。

關閉沒有動畫的物件的骨架來確保他們不會在執行期間被加上 Animator 元件,造成不必要的消耗。

幫帶有動畫的物件開啟 Optimize Game Objects 選項

Optimize Game Objects 選項對於帶動畫的模型有明顯的效能影響。如果這裡沒打勾,在初始化模型時 Unity 會把整個模型的骨架結構對應的 GameObject 階層全部產生出來。這個巨大的 Transform 元件結構更新起來自然很耗效能,尤其是如果結構帶有其他元件(例如粒子系統或碰撞體)。它也會拉低 Unity 多執行緒對蒙皮(Mesh skinning)和骨架動畫的計算能力。

打勾後所有的骨架對應的 Transform 結構都會被移除,如果模型骨架結構中有特定的部位需要露出方便控制(例如模型的手部要用來握住武器),則可以把它列在“ExtraTransforms”白名單中。

更多資料可以參閱官方手冊關於 Model Importer 的部分。

如果可以,儘量使用Mesh壓縮

開啟 Mesh compression 選項會縮短用來表示模型資料不同通道的浮點數位元長度,這會移除一定的精確度並可能造成可見的變化,使用這個之前最好先讓美術檢查過這種損失在允許範圍內。

各個壓縮等級使用的位元長度在 ModelImporterMeshCompression 腳本裡有介紹。

請記得,可以針對不同通道使用不同等級的壓縮,所以專案可以只針對切線向量(Tangent)和法向量(Normal)壓縮但不壓縮 UV 和頂點位置。

追記:Mesh Renderer 的設定

當加入一個 Mesh Renderer 到 Prefab 或 GameObjec t時,請小心上面的設定。預設情況下 Unity 會開啟 Shadow casting、Shadow receiving、Light Probe 取樣、Reflection Probe 取樣和動態向量(Motion Vector)的計算。

假如專案不需要上述的功能,記得寫個編輯器腳本關掉素材上的選項,執行期用程式加 MeshRenderer 到物件上也要記得執行相同的規則。

對於一個2D遊戲來說,不小心加了一個帶有 Shadow casting 的 MeshRenderer 到場景裡會觸發整個陰影計算循環,這通常是浪費效能。

Audio - 音效

採用平台支援的壓縮設定

採用硬體支援的音源壓縮格式。所有的iOS設備都有 MP3 硬體解壓縮能力,而大多數的 Android 設備都有支援 Vorbis。
此外,可以直接將未壓縮的音效檔匯入 Unity 裡,因為 Unity 會在打包專案時會重新壓縮。所以不需要先壓縮再匯入 Unity,這只會降低音效品質。

強制音效用單聲道

只有少數的手機裝置真的有立體聲喇叭,而將音效強制設定為單聲道能讓記憶體的消耗減半。就算遊戲會輸出部份的立體聲,有些單聲道像是 UI 音效還是可以開啟這個選項。

降低音頻的取樣

調低取樣能進一步降低記憶體消耗和最終專案的大小,可以和音效設計師協調找出最小最能接受的音源品質。參考 SetCompressionBitrate。


Unity最佳化 - 目錄


  1. 分析
  2. 記憶體
  3. 協同
  4. 資源審查
  5. GC和Managed Heap
  6. 字串和Text
  7. Resources目錄和一般最佳化
  8. 特別最佳化

沒有留言:

張貼留言

著作人

網誌存檔