OnRenderImage 的性能問題
在我們看到的后處理教程,或者后處理插件中,通常的處理方式是,在OnRenderImage方法中,處理后處理。
在我剛開始整合后處理的過程中,發現,即使不做任何后處理,僅僅一句Graphics.Blit(source, destination),也會導致嚴重的掉幀,這看起來是不符合邏輯的。在google后,找到問題的說明Post Process Mobile Performance : Alternatives To Graphics.Blit , OnRenderImage,原因如下
我的解決方案如下:
也就是,在OnPreRender中,獎rendertexture賦值給camera,在OnPostRender中,處理后處理渲染。
通過這種方案,能大幅度減少掉幀。我們測試過,用同樣的后處理(例如bloom)在使用OnRenderImage的時候,從60 FPS 掉到40 FPS左右,改用pre post方法后,從60 FPS,掉到55 FPS左右,改善明顯(用中低端手機測試效果明顯些,我們是用的360手機。不同手機,改善的程度略有差異,但是還是能看到至少幾幀的提升)。
相信這個優化方案,有不少同學之前已經看到過,並已經在使用。
這個方案,略有麻煩的一點是,當我們的camera開啟MSAA或者HDR的時候,會導致后處理不起效果。我猜測應該是MSAA和HDR會激活unity內部的渲染流程必須走OnRenderImage。
(這里要特別注意的一點是,我們用的是Gamma color Space,如果我們要HDR的效果,最好不要用pre post這個優化方式,因為經過測試下來,會導致負優化,幀數反而下降,目前我還沒找到好的辦法,這里我們按照不需要HDR效果來說)
我們還是可以支持MSAA,解決方案是,根據QualitySettings.antiAliasing和我們的來創建temp 的rendertexture,同時,我們需要關閉攝像機的MSAA和HDR選項。
https://forum.unity.com/threads/onrenderimage-is-slow-when-msaa-is-on.427006/
這里要注意處理的邏輯是:當我們在游戲設置界面開關后處理的時候,要配對地處理camera的MSAA 選項和rendertexture 的創建參數,以免出現后處理不起作用,或者關閉后處理后,抗鋸齒沒有正確開啟的問題。
這應該是一個能立竿見影的優化,相對需要注意處理好一些細節和各種設置切換的處理,做好測試。
合並多個后處理效果
當我們使用unity早期的Image Effect,或者一些單個效果后處理插件的時候,他們通常沒有考慮整合的效率問題。以OnRenderImage的做法舉例:通常是每個效果是一個腳本,他有自己的OnRenderImage,如果我們有4個效果,那就是4個單獨的OnRenderImage,這在代碼層面的簡潔性和易擴展性上,當然是有優勢的。但是,這樣做,性能是有問題的,我們需要盡量將各種后處理效果,整合到同一個OnRenderImage(或pre post方法)中,這樣能帶來一些性能提升,雖然不如上面的pre post效果明顯,但是優化是一點點積累的,也是值得做。
具體方法,Unity-Technologies/PostProcessing這個就比較有代表性,它將所有的效果,整合到同一個OnRenderImage,同一個shader中處理,只是通過材質的EnableKeyword來開關對應的功能。這塊我就不展開講了,大家看鏈接里的代碼,最實在。
這里提一句,如果用pre post方式優化,將不能和OnRenderImage方式在同一個camera下混用,這里整合的時候,要根據項目的需求,處理好。我們現在是用unity 那個 post processing方案,改為pre post的方式。
同時,大家使用各種后處理插件,效果,要注意根據需求做一些裁剪,某些不需要的效果,盡量注釋或刪除,讓整合的代碼,更加清晰可讀,也減少一些額外的性能消耗(shaderlab內存等)。
修改材質屬性,不要使用string
通常的例子代碼中,會使用string作為key的方式來修改mat的屬性,很多shader的插件內,也是這么用的。
string的方式
這里,我們通過反編譯可以看到,string的方式,實際上會調用Shader.PropertyToID,所以,我們應該將整個id cache下來,通過id的方式來調用
這也是一個很小的點,但是如果是后處理這種,可能每幀都會調用的地方,可能帶來的優化,還是很有意義的。
還有一種優化,比如減少rendertexture的尺寸等,在網上很多關於優化的文章中都有提到,就不具體說了,網上能搜到很多。
總結一下,主要的優化,就是OnRenderImage轉換為pre post的方式,能大大地改善后處理的渲染效率(opengles2 和 opengles3都測試過)。我們使用的Unity版本是5.6.4,其他版本未測試,如果用其他版本,大家需要自己先測試一下。
無論優化多好,后處理,始終還是對性能影響很大,特別是手機電量不足或者發熱導致手機降頻的時候,后處理將會導致掉幀加重。如果必須要使用后處理(策划,美術要求),那么做好性能開關,優化好后處理的性能,是必須要做的。我們也在進一步優化中,大家有好的優化心得和方案,也可以在評論里回復,謝謝大家。
最后說明一點:pre post的優化方案,只有在不需要HDR效果下,才有優化效果(基於我的測試),如果需要HDR效果,還是用OnRenderImage來做吧。這塊優化,需要多多測試,不注意就容易出問題哦。圖形渲染方面是我的弱項,希望大家指正。