本文為霍格沃茲測試學院學員學習筆記,進階學習文末加群。
FPS 和丢幀率可以在一定程度上作為 APP 流暢度的一項衡量标準,本文介紹利用
adb shell dumpsys gfxinfo
指令擷取軟體渲染加載過程的資料,進行計算進而擷取測試結果。
前置業務知識
在此之前,需要先了解螢幕展示繪制過程及 Android 的 VSync 機制
VSync 全稱是 Vertical Synchronization(垂直同步),在 Android 4.1 中引入 Android 系統(同時引入的一個概念是 Triple Buffering)。
學計算機的經常聽到 Buffer 的概念(生活中也碰到過很多),起到的都是一個類似的作用。用來協調兩個不同速度的東西工作。
舉個執行個體,假設顯示内容和繪制使用的是用一塊記憶體,那可能會出現下面的問題。顯示有截斷的異常(圖中的Tear Point #1和Tear Point #2)。
為什麼會這樣呢?因為 CPU/GPU 處理和螢幕展示的速度不一樣但是卻使用的是同一塊記憶體。
怎麼解決呢?可以将 CPU/GPU 處理和螢幕展示分開,CPU/GPU 在背景處理,處理完一幀的資料以後才交給螢幕展示(這樣可能導緻另外的問題是,如果 CPU/GPU 處理很慢,那麼螢幕可能會一直展示某一幀的資料,下面主要分析這個問題的處理)。
繪制過程中的兩個概念
- 手機螢幕重新整理率:手機硬體每秒重新整理螢幕的次數,機關 HZ。一般是一個固定值,例如 60HZ。
- FPS:畫面每秒傳輸幀數,通俗來講就是指動畫或視訊的畫面數。機關 HZ。
手機螢幕重新整理率是固定的,FPS 則是一直變化的,怎麼才能保證能夠運作流暢呢?從幾個例子來看吧。
先解釋圖檔代表的意思:最下面黑線代表的是時間,黃色代表螢幕展示,綠色代表 GPU 處理,藍色代表 CPU 處理。Jank 代表的是重複展示上一幀的異常。下面會從螢幕展示的每一幀開始分析:
沒有引入 VSync 機制
上圖是沒有引入VSync 機制的處理流程。
- Display 展示第0幀資料,這時 CPU/GPU 會去處理第1幀的資料。
- Display 展示第1幀資料(此時螢幕顯示是正常的),這時 CPU/GPU 可能處理其他任務導緻很晚才去處理繪制。
- 因為 CPU/GPU 沒處理好第2幀的資料,是以 Display 還是展示第1幀資料(此時螢幕顯示是異常的),CPU/GPU 處理完第2幀沒有處理完的資料然後繼續處理第3幀的資料。 …
- 上圖中一個很明顯的問題是,隻要一次 CPU/GPU 處理出現異常就可能導緻後面的一系列的處理出現異常。
引入 VSync 機制
VSync 可以簡單的認為是一種定時中斷,系統在每次需要繪制的時候都會發送VSync Pulse 信号,CPU/GPU 收到信号後馬上處理繪制。
正常情況
在4.1以後引入VSync 機制。
在 FPS < 手機螢幕重新整理率的情況下,一切運作完美。
Double Buffering 異常情況
VSync 機制下 Double Buffering 時 FPS > 手機螢幕重新整理率的情況。
- Display 展示第A 幀資料,CPU/GPU 收到 VSync Pulse 信号馬上處理B 幀的資料,但是由于計算太多,導緻沒有在一個 VSync 間隔内處理完。
- 由于第B 幀資料沒有處理好,Display 繼續展示第A 幀資料(此時螢幕顯示是異常的)。由于系統中隻存在一塊記憶體給 CPU/GPU 處理繪制,是以在這個 VSync 間隔内cpu 不處理任何事。
- Display 展示第 B 幀資料,CPU/GPU 收到 VSync Pulse 信号馬上處理即将展示A 幀的資料,由于計算太多,導緻沒有在一個 VSync 間隔内處理完。
需要展示的A 幀資料沒有處理好,Display 繼續展示第 B 幀資料(此時螢幕顯示是異常的)。由于系統中隻存在一塊記憶體給 CPU/GPU 處理繪制,是以在這個 VSync 間隔内 CPU 不處理任何事。 … 上圖中一個很明顯的問題是,隻要出現一次Jank
就會影響下一次的VSync(cpu 不能工作)。
Triple Buffering 異常情況
Triple Buffering 的引入。
Display 展示第A 幀資料,CPU/GPU 收到VSync Pulse 信号馬上處理B 幀的資料,但是由于計算太多,導緻沒有在一個VSync 間隔内處理完。 由于第B 幀資料沒有準備好,Display 繼續展示第A
幀資料(此時螢幕顯示是異常的)。此時雖然B 被gpu 在使用,但是cpu 可以處理Buffer C(因為有3個緩沖)。
- Display 展示第B 幀資料,gpu 繼續處理上一步驟的C,cpu 則處理A。 後續過程出錯的情況被降低了…
擷取資料并計算結果
1.運作指令
"adb -s
" +
deviceName
+
" shell dumpsys gfxinfo "
packageName
擷取基礎資料,我們會獲得很多資料,這裡截取需要進行分析的部分:
注:如果運作完指令發現無上圖中的4個參數,則很可能是手機的“GPU呈現模式分析”未打開;
在手機的開發者選項中,找到“GPU呈現模式分析”,選擇“在adb shell dumpsys gfxinfo中”,如果是華為或榮耀的手機,則選擇“在螢幕上顯示為線型圖”:
2.如上圖資訊表示了每一幀在安卓系統中的四個階段:
- Draw: 表示在Java中建立顯示清單部分中,OnDraw()方法占用的時間
- Prepare: 準備時間
- Process:表示渲染引擎執行顯示清單所花的時間,view越多,時間就越長
- Execute:表示把一幀資料發送到螢幕上排版顯示實際花費的時間,其實是實際顯示幀資料的背景緩存區與前台緩沖區交換後并将前台緩沖區的内容顯示到螢幕上的時間
- 将上面的四個時間加起來就是繪制一幀所需要的時間,如果超過了16.67就表示掉幀了
說明
Android 定義了流暢度的資料标準,以 60FPS 為标準(FPS 為每秒繪制的幀數),幀數過小就會出現卡頓感。
每一幀在安卓系統中分4個階段,4個階段的總和超過16.67(1秒60幀,算下來平均1幀的間隔就約是16.67ms)就認為丢幀。
這個定義在 Android6.0 以前是一定的,但是現在已經沒有固定的标準了,因為目前安卓系統有3層緩存機制,加上硬體上的進步,即使超過16.67,也不一定會出現卡頓感。是以這個資料在測試時作為一種對比和相對衡量标準,也可根據需求自定義标準。
計算結果
通過以上資料,就可以擷取到每一幀的時間、總幀數;進而就可以計算出 jank 數、vsync 數,進而就可以得到最終的 FPS 和丢幀率資料。
當然,手工計算無疑效率低,出錯率大,是以這裡的計算過程最好還是以腳本形式,讓代碼幫我們去計算,具體代碼計算原理與專項自動化過程後續探讨。