開啟硬件加速導致動畫掉幀分析

背景

最近碰到一個動畫卡頓的問題,花了比較多的精力進行解決,并從中總結出來一些分析的套路,特此進行分享。
卡頓掉幀的現象:


gte96-fhw59.gif

正常的現象:


94njj-wcbhv.gif

從現象可以看出,應用信息展開的時候,有一定的卡頓。

繪制原理

image.png

要在屏幕上顯示,其實要經過一系列的過程,Android 應用程序把經過測量、布局、繪制后的 surface 緩存數據,通過進程通信的方式,把數據給到SurfaceFlinger ,SurfaceFlinger這把這些圖層的數據根據surface的排序進行混合,最后把混合之后的數據渲染到顯示屏幕上, 通過 Android 的刷新機制來刷新數據。也就是說應用層負責繪制,系統層負責渲染,通過進程間通信把應用層需要繪制的數據傳遞到系統層服務,系統層服務通過刷新機制把數據更新到屏幕上。


image.png

surfaceflinger在初始化時設置VnSync信號周期,一般為16ms,之后監聽Vnsync信號,在收到信號之后,重新走混合渲染流程。
開發app的性能目標就是保持60fps,這意味著每一幀你只有16ms≈1000/60的時間來處理所有的任務。Android系統每隔16ms發出VSYNC信號,觸發對UI進行渲染,如果每次渲染都成功,這樣就能夠達到流暢的畫面所需要的60fps。


image.png

如果你的某個操作花費時間是24ms,系統在得到VSYNC信號的時候就無法進行正常渲染,這樣就發生了丟幀現象。那么用戶在32ms內看到的會是同一幀畫面。
image.png

丟幀情況 卡頓情況
0-10幀 流暢
10-20幀 較卡
20-40幀 很卡
40-60幀 卡死了

卡頓分析

通過systrace抓取launcher應用卡頓時候的信息,獲取的traceview,掉幀的的部分如下:


捕獲1.PNG

線程在traceview中的信息包括兩部分,線程運行狀態,和線程運行方法棧。
通過顏色判斷線程運行狀態,各個顏色的含義為:
灰色:sleeping,線程休眠狀態
藍色:runnable,線程就緒狀態
綠色:running,線程運行狀態
從掉幀的的線程的運行狀態判斷,在掉幀的時候,UI線程狀態色顏色為長時間灰色,處于sleeping狀態。直接原因找到了,找到導致卡頓的元兇近了一步。
導致UI線程掛起的線程,一般也是將UI線程喚醒的線程。
在traceview中雙擊UI線程結束休眠后,變成就緒的第一個狀態,也就是從灰色變成藍色的第一個狀態,可以發現,導致掛起的線程的線程號為27005。


捕獲2.PNG

在traceview中搜索27005,可以知道此線程名為RenderThread,也就是硬件加速專門用來渲染的線程。


捕獲3.PNG

并且從圖中可看出在UI線程被喚醒前,RenderThread線程剛剛執行完syncFrameState方法。


捕獲4.PNG

RenderThread的工作流程

RenderThread的工作流程可以參考 http://www.lxweimin.com/p/bc1c1d2fadd1
簡單的來說RenderThread的工作流程為:
1.將Main Thread維護的Display List同步到Render Thread維護的Display List去。這個同步過程由Render Thread執行,但是Main Thread會被阻塞住。
2.如果能夠完全地將Main Thread維護的Display List同步到Render Thread維護的Display List去,那么Main Thread就會被喚醒,此后Main Thread和Render Thread就互不干擾,各自操作各自內部維護的Display List;否則的話,Main Thread就會繼續阻塞,直到Render Thread完成應用程序窗口當前幀的渲染為止。
3.Render Thread在渲染應用程序窗口的Root Render Node的Display List之前,首先將那些設置了Layer的子Render Node的Display List渲染在各自的一個FBO上,接下來再一起將這些FBO以及那些沒有設置Layer的子Render Node的Display List一起渲染在Frame Buffer之上,也就是渲染在從Surface Flinger請求回來的一個圖形緩沖區上。這個圖形緩沖區最終會被提交給Surface Flinger合并以及顯示在屏幕上。

注意第二步:“如果能夠完全地將Main Thread維護的Display List同步到Render Thread維護的Display List去,那么Main Thread就會被喚醒”,這個就驗證了我們從traceview中看到的在RenderThread完成syncFrameState之后,UI線程被喚醒。

綜述

掉幀的根本原因是RenderThread的繪制時間過長。UI線程在Draw方法中,完成將數據放入displaylist之后會掛起,等待Render Thread將displaylist的數據同步,也就是完成syncFrameState方法。如果RenderThread一直在繪制,那么UI線程掛起的時間就會很長,整個繪制市場就會超過16ms,最終導致掉幀。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 轉載:http://tech.meituan.com/hardware-accelerate.html 在手機客戶...
    kkgo閱讀 691評論 0 1
  • 1 CALayer IOS SDK詳解之CALayer(一) http://doc.okbase.net/Hell...
    Kevin_Junbaozi閱讀 5,184評論 3 23
  • 頁面渲染背景知識: 頁面渲染時,被繪制的元素最終轉換為矩陣像素點(多維數組的形式),才能被顯示器顯示 頁面由各種基...
    三十二蟬閱讀 1,711評論 0 3
  • 今晚的作業,因為三次謊話而屢遭停滯,娃上床已經快11點了,我這會兒還在洗衣服, 允許事情在變好之前出現糟糕的狀況,...
    冰糖誠閱讀 142評論 0 0
  • 活著浪費空氣,死了浪費土地,半死不活浪費人民幣。 猛的一看你不怎么樣,仔細一看還不如猛的一看。 ...
    禁錮流星雨閱讀 714評論 2 0