What is MGD
ARM? Mali? Graphics Debugger的作用之一是針對采用ARM? Mali? GPU的Android手機,進行app的GPU運行時調試和調優。
Why MGD
本文MGD主要用于性能調優。
關于GPU性能調優,定性不定量都是耍流氓。
猜是虛的、打嘴炮討論是無用的、運行時正則表達式disable對象是盲目的、Unity Profiler是難于真機調試的、GPA(Intel? Graphics Performance Analyzers)是不同平臺的、Unity FrameDebugger是從實現本質決定了不能定量到高于DrawCall精度的。
另外,本文假設已確定瓶頸出現在GPU。否則,應優先調優CPU性能。直接使用Unity真機調試是不錯的CPU性能調優手段。
Doc & Setup
閱讀[ARM? Mali? Graphics Debugger User Guide](http://malideveloper.arm.com/downloads/tools/mgd/1.3.2/Mali Graphics Debugger v1.3.2 User Guide.pdf)是了解MGD最全面的途徑,里面包括了如何設置MGD和使用MGD。
由于MGD里會直接出現大量OpenGL/OpenGL ES的API,所以也可按需查閱AW OpenGL ES 3.0 Programming Guide 2nd Edition。
本文以MGDv2.1版本為基礎進行編寫。編寫完畢后數天(約2015-08-11)得知MGDv3.0已發布,其改進了體驗和UI框架、FrameBuffer View、支持MRT/DepthBuffer/StencilBuffer的截取、更好的截取性能和截取文件大小優化。詳情可參考官網或[Mali Graphics Debugger 3.0.0 User Guide](http://malideveloper.arm.com/downloads/tools/mgd/3.0/Mali Graphics Debugger v3.0.0 User Guide.pdf)。
關于MGD設置,本文為了內容完整性也精簡地描述如下。(你若已設置好或暫不關心設置,也可跳過以下設置步驟,直接閱讀下面的操作步驟、思考過程和發現killer的問題及其以下章節。)
- 在電腦裝好MGD
- 準備好Mali GPU的Android手機。基于Mali的設備不少,比如Samsung Galaxy S3等基于Exynos SoC的手機。具體列表見Mali SoC implementation。
- 本文用已Root的Android的設備進行。但未Root也可用,但需要參考上面User Guide,以另外的方式進行。
- 在電腦上安裝好ADB(Android Debug Bridge),并且把ADB設置到操作系統(windows or OSX)的環境變量中。
- 連接Android設備和電腦。
- 打開命令行,輸入:
#拷貝mgd版OpenGL ES運行時庫和mgd后臺進程到手機sdcard文件夾
cd /your_computer/path_of_installed_mgd
adb push libGLES_mgd.so /sdcard/
adb push mgddaemon /sdcard/
#準備進入手機進行命令行操作
adb shell
#以下命令在手機中執行
su
#拷貝mgd版OpenGL ES庫和mgd后臺進程到手機system文件夾并且修改為可執行權限
cd /sdcard/cp mgddaemon /system/bin/mgddaemon
chmod 777 /system/bin/mgddaemon
cp libGLES_mgd.so /system/lib/egl/libGLES_mgd.so
chmod 777 /system/lib/egl/libGLES_mgd.so
#Android 4.2 和 4.3切換到mgd版OpenGL ES運行時庫:改egl.cfg配置方式
cp /system/lib/egl/egl.cfg /system/lib/egl/egl.cfg.bak
echo "0 0 mgd" > /system/lib/egl/egl.cfg
#Android 4.4 and 5.0切換到mgd版OpenGL ES運行時庫:文件鏈接方式
cd /system/lib/egl
ln -s libGLES_mgd.so libGLES.so
ln -s libGLES_mgd.so libEGL_mgd.so
ln -s libGLES_mgd.so libGLESv2_mgd.so
ln -s libGLES_mgd.so libGLESv1_CM_mgd.so
#設置mgddaemon,叫它只調試com.tencent.killer這個進程。這里應填入你自己的進程id
echo "com.tencent.killer" > /system/lib/egl/processlist.cfg
#先退回到電腦,可能不止一次exit
exit
#準備打通調試信息轉發通道
adb forward tcp:5002 tcp:5002
#再次進入手機,啟動mgd后臺進程
adb shell
su
mgddaemon
-
在電腦上打開MGD,如下圖點擊Connect按鈕
點擊Connect按鈕,連接MGD和設備 - 運行killer(或你自己的app)
- MGD成功自動收集killer的GPU運行時調試信息
至此,Setup操作已完畢。接下來,需要保持手機和電腦的USB連接,進行進一步的調試調優操作,亦可用通過菜單將當前調試數據保存到電腦。
操作步驟、思考過程和發現killer的問題
關于killer
killer(產品名字《獨立防線》)為我們項目組一款基于Unity的可FPS/TPS切換、可PVP/PVE的3D射擊游戲。
第一幀:主角第三人稱狀態,未露出FPS狀態精細武器、無特效
在游戲中操作,進入戰斗。并保持主角在主角第三人稱狀態,未露出FPS狀態精細武器、無特效——一個最純粹的戰斗情況。
從上圖發現MGD已自動收集GPU數據,很是煩人,此時,可點擊暫停按鈕,暫停游戲,從而暫停收集。
點擊Toggle Fragment Count按鈕(注意之前的操作步驟如并非MGD和設備先Connect好了之后才運行app,可能會導致本按鈕無法點擊),再點擊下一幀,可用收集下一幀的Fragment Shader的信息。這是因為Fragment Shader往往是性能消耗大戶,需優先關注。
點擊剛剛收集好Fragment Shader信息的第971幀,點擊Fragment Shaders,然后按指令周期比例排序,可立刻知道哪些shader占消耗大頭,比如圖中的第877號shader,名字是“Shader 879”。
雙擊該877號shader,觀察代碼,可知是用于lightmap場景的shader
所以,可輸出結論一和初步解決方案。
結論一:場景是GPU性能消耗第一大戶,需最優先關注。精簡、優化場景mesh和貼圖、精簡lightmap是可行方法。
再點擊下一個第619號Fragment Shader程序,
通過經驗可知,為渲染UI的Shader
所以,可輸出結論二和初步解決方案。
結論二:UI是GPU性能消耗第二大戶。留意到里面有discard語句,可能帶來性能影響,所以應編寫無clip版本的shader。并且精簡UI、或進行UI紋理打包。
再點擊下一個Shader,發現和UI Shader很相似,只是少了一個顏色的運算,所以也不用有mediump的color_2中間變量。可知是UGUI針對材質顏色為全白色時的優化版Shader
所以,有結論三。
結論三:UI中,不需用顏色的,應保持此材質顏色為白色。
至此,當前幀的Fragment Shader權重較高者,皆已列出,剩下的Fragment Shader已變得性價比不高。
需轉移目標,比如,Vertex Shader。
同理,切換至Vertex Shader,按cycle排序,找出性能消耗第一大戶,第880號Vertex Shader。通過里面的代碼出現“SH”,可知是球諧函數“Spherical Harmonics”,即用Light Probe進行渲染的主角Shader。
所以,有結論四。
結論四:主角頂點數過多(圖中為23469個頂點),是造成性能的瓶頸之一。
同時,留意到我們主角都是距離鏡頭很近的,所以,壓根不需要霧的運算,所以,有結論五。
結論五:主角的Shader,甚至一些在不遠處的敵兵shader,都需要去掉霧運算。
再觀察下一個877號Vertex Shader,留意到是用Light Map進行渲染的場景,所以。
同理,亦可以有結論六。
結論六:場景,如有可能,最好也去掉霧運算。
至此,第一幀分析基本完畢
第二幀:FPS狀態,露出精細武器,無槍火特效
游戲戰斗中是個多變的過程,不同時刻的兩個幀顯示的內容可能大相徑庭。
所以需要更換另一種情況進行繼續調優。比如FPS狀態,露出精細武器,無槍火特效。
在之前的暫停狀態下,取消點擊收集Fragment數量按鈕(Get the fragment count statistics),否則將會很卡,然后點擊播放按鈕(Resume Tracing),以和第一幀類似的方法進行數據收集。
從收集數據看來,結果和第一幀數據并無質變。但由于知道變化的因素是精細武器本身,所以應特定確定該Shader的消耗。
此時,如果對武器Shader不熟悉,可以打開Unity,在Editor重現該情況,在Editor直接閱讀Shader代碼,如下:
根據Shader特征Shininess
、Gloss
和Normalmap
等,回到MGD,也是按Cycle數從大到小逐個觀察(因為渲染武器的消耗肯定不會很小),確定為第871號Fragment Shader。
所以有結論七:
結論七:精細武器的Shader的Cycle比例(4.6%)和Shader指令,哪怕是用Surface Shader,也不算復雜,不是瓶頸。這可能是因為Unity的按平臺編譯Shader的優化結果。
第三幀:FPS狀態,露出精細武器,開火,播放開火特效
繼續采集數據。并且點擊開火,觸發開火特效,然后立刻點擊MGD暫停,并進行類似采集。發現有以下新的問題shader。
通過觀察Shader代碼,和槍火特效(還有額外的四周防護罩效果)的Shader一致,為
留意到該Shader并不復雜,但卻又霧的處理(理論上可以去掉一個Tint的顏色乘法),并且該特效在Unity觀察overdraw也合理,所以也可以有結論八:
結論八:槍火特效+滿屏的防護罩特效會導致有額外10%的cycle產生。需去除霧的處理。因為需求原因,占用屏幕面積較大,但Overdraw程度尚算合理。
至此,關于Shader的調優在本文暫告一段落。接下來需要更多實地考察。
另外,操作上還有小技巧,可以通過Frames with features enabled的過濾,剔除沒有詳細數據的幀
內存情況
內存不會直接影響幀率,但也是性能的一方面表現。過高的內存可能是加載緩慢的誘因、可能會提高app后臺后被系統kill掉的幾率、也可能直接導致內存不足crash。
點擊MGD里的Textures,并點擊Size進行排序。
當前在killer出現了不合理情況有:
結論九:主界面的圖標從戰斗外泄露到戰斗內了。需要注意是否戰斗內有錯誤的引用。可使用運行時檢測工具打包Sprite Packer的tag是否戰斗外、戰斗內互斥。亦需要注意資源模塊ResourcesManager是否還有資源引用,導致Resource.UnloadUnused()無法卸載主界面資源。
結論十:軍銜圖標可能從戰斗外泄露到戰斗內了,也可能是戰斗內就是需要用到。
結論十一:一個時刻很大幾率只出現一個的icon圖片可以不打包,放棄一定的DrawCall效率來換取內存,并且這樣哪怕泄露也可減緩泄露的后果。
尚未提及的更多性能評估點
本文著重從最根本的Shader的Cycles數量進行分析,但尚未提及的更多性能評估點包括:
- DrawCall是否合理,有否可能合并。Unity 3D游戲中,合并DrawCall會決定于LightMap,也會決定于UI層次間的atlas分布。
- 是否有過多的OpenGL狀態切換。
更多的優化細節,也可參考ARM? Mali? GPU Version: 3.0 OpenGL ES Application Optimization Guide,或ARM? Guide to Unity Version 2.1 Enhancing Your Mobile Games。
MGD(v2.1)尚待改進的地方
MGD(v2.1)尚待改進的地方有:
- 在本次調優過程中,不能對已Capture的幀進行逐DrawCall重畫(用戶手冊給出了原因);
- 不能方便觀察頂點模型;
- 不能以柱形圖直觀地列出時間或Cycles消耗。
上述部分問題已在MGDv3.0得以解決。
About
DonaldW,客戶端開發,現就職于騰訊魔方工作室群。
本文可隨意分發、分享。但請注明署名。