開發與維運

專項測試實戰 | 如何測試 App 流暢度(基於 FPS 和丟幀率)

本文為霍格沃茲測試學院學員學習筆記,進階學習文末加群。

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 處理和屏幕展示的速度不一樣但是卻使用的是同一塊內存。

image.png

怎麼解決呢?可以將 CPU/GPU 處理和屏幕展示分開,CPU/GPU 在後臺處理,處理完一幀的數據以後才交給屏幕展示(這樣可能導致另外的問題是,如果 CPU/GPU 處理很慢,那麼屏幕可能會一直展示某一幀的數據,下面主要分析這個問題的處理)。

繪製過程中的兩個概念

  • 手機屏幕刷新率:手機硬件每秒刷新屏幕的次數,單位 HZ。一般是一個固定值,例如 60HZ。
  • FPS:畫面每秒傳輸幀數,通俗來講就是指動畫或視頻的畫面數。單位 HZ。

手機屏幕刷新率是固定的,FPS 則是一直變化的,怎麼才能保證能夠運行流暢呢?從幾個例子來看吧。

先解釋圖片代表的意思:最下面黑線代表的是時間,黃色代表屏幕展示,綠色代表 GPU 處理,藍色代表 CPU 處理。Jank 代表的是重複展示上一幀的異常。下面會從屏幕展示的每一幀開始分析

沒有引入 VSync 機制
image.png

上圖是沒有引入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 機制。

image.png

在 FPS < 手機屏幕刷新率的情況下,一切運行完美。

Double Buffering 異常情況

VSync 機制下 Double Buffering 時 FPS > 手機屏幕刷新率的情況。

image.png

  • 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 的引入。

image.png

  • 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 獲取基礎數據,我們會獲得很多數據,這裡截取需要進行分析的部分:
image.png

注:如果運行完命令發現無上圖中的4個參數,則很可能是手機的“GPU呈現模式分析”未打開;

在手機的開發者選項中,找到“GPU呈現模式分析”,選擇“在adb shell dumpsys gfxinfo中”,如果是華為或榮耀的手機,則選擇“在屏幕上顯示為線型圖”:
image.png
image.png

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 和丟幀率數據。

當然,手工計算無疑效率低,出錯率大,所以這裡的計算過程最好還是以腳本形式,讓代碼幫我們去計算,具體代碼計算原理與專項自動化過程後續探討。

更多技術文章分享及測試資料

Leave a Reply

Your email address will not be published. Required fields are marked *