Android Systrace 基礎知識(7) - Vsync 解讀

本文是 Systrace 系列文章的第七篇,主要是是介紹 Android 中的 Vsync 機制。文章會從 Systrace 的角度來看 Android 系統如何基於 Vsync 每一幀的展示。Vsync 是 Systrace 中一個非常關鍵的機制,雖然我們在操作手機的時候看不見,摸不着,但是在 Systrace 中我們可以看到,Android 系統在 Vsync 信號的指引下,有條不紊地進行者每一幀的渲染、合成操作,使我們可以享受穩定幀率的畫面。

本系列的目的是通過 Systrace 這個工具,從另外一個角度來看待 Android 系統整體的運行,同時也從另外一個角度來對 Framework 進行學習。也許你看了很多講 Framework 的文章,但是總是記不住代碼,或者不清楚其運行的流程,也許從 Systrace 這個圖形化的角度,你可以理解的更深入一些

系列文章目錄

  1. Systrace 簡介
  2. Systrace 基礎知識 - Systrace 預備知識
  3. Systrace 基礎知識 - Why 60 fps ?
  4. Systrace 基礎知識 - SystemServer 解讀
  5. Systrace 基礎知識 - SurfaceFlinger 解讀
  6. Systrace 基礎知識 - Input 解讀
  7. Systrace 基礎知識 - Vsync 解讀
  8. Systrace 基礎知識 - Vsync-App :基於 Choreographer 的渲染機制詳解
  9. Systrace 基礎知識 - MainThread 和 RenderThread 解讀
  10. Systrace 基礎知識 - Binder 和鎖競爭解讀
  11. Systrace 基礎知識 - Triple Buffer 解讀
  12. Systrace 基礎知識 - CPU Info 解讀

正文

Vsync 信號可以由硬件產生,也可以用軟件模擬,不過現在基本上都是硬件產生,負責產生硬件 Vsync 的是 HWC,HWC 可生成 VSYNC 事件並通過回調將事件發送到 SurfaceFlinge , DispSync 將 Vsync 生成由 Choreographer 和 SurfaceFlinger 使用的 VSYNC_APP 和 VSYNC_SF 信號

vsync 的產生
vsync 的產生

Android 基於 Choreographer 的渲染機制詳解 這篇文章裏面,我們有提到 :Choreographer 的引入,主要是配合 Vsync,給上層 App 的渲染提供一個穩定的 Message 處理的時機,也就是 Vsync 到來的時候 ,系統通過對 Vsync 信號週期的調整,來控制每一幀繪製操作的時機. 目前大部分手機都是 60Hz 的刷新率,也就是 16.6ms 刷新一次,系統爲了配合屏幕的刷新頻率,將 Vsync 的週期也設置爲 16.6 ms,每個 16.6 ms,Vsync 信號喚醒 Choreographer 來做 App 的繪製操作 ,這就是引入 Choreographer 的主要作用

渲染層(App)與 Vsync 打交道的是 Choreographer,而合成層與 Vsync 打交道的,則是 SurfaceFlinger。SurfaceFlinger 也會在 Vsync 到來的時候,將所有已經準備好的 Surface 進行合成操作

下圖顯示在 Systrace 中,SurfaceFlinger 進程中的 VSYNC_APP 和 VSYNC_SF 的情況

SurfaceFlinger- Vsync
SurfaceFlinger- Vsync

Android 圖形數據流向

首先我們要大概瞭解 Android 中的圖形數據流的方向,從下面這張圖,結合 Android 的圖像流,我們大概把從 App 繪製到屏幕顯示,分爲下面幾個階段:

Android 圖形數據流向
Android 圖形數據流向
  1. 第一階段:App 在收到 Vsync-App 的時候,在主線程進行 measure、layout、draw(構建 DisplayList , 裏面包含 OpenGL 渲染需要的命令及數據) 。這裏對應的 Systrace 中的主線程 doFrame 操作
  2. 第二階段:CPU 將數據上傳(共享或者拷貝)給 GPU, 這裏 ARM 設備 內存一般是 GPU 和 CPU 共享內存。這裏對應的 Systrace 中的渲染線程的 flush drawing commands 操作
  3. 第三階段:通知 GPU 渲染,真機一般不會阻塞等待 GPU 渲染結束,CPU 通知結束後就返回繼續執行其他任務,使用 Fence 機制輔助 GPU CPU 進行同步操作
  4. 第四 階段:swapBuffers,並通知 SurfaceFlinger 圖層合成。這裏對應的 Systrace 中的渲染線程的 eglSwapBuffersWithDamageKHR 操作
  5. 第五階段:SurfaceFlinger 開始合成圖層,如果之前提交的 GPU 渲染任務沒結束,則等待 GPU 渲染完成,再合成(Fence 機制),合成依然是依賴 GPU,不過這就是下一個任務了.這裏對應的 Systrace 中的 SurfaceFlinger 主線程的 onMessageReceived 操作(包括 handleTransaction、handleMessageInvalidate、handleMessageRefresh)SurfaceFlinger 在合成的時候,會將一些合成工作委託給 Hardware Composer,從而降低來自 OpenGL 和 GPU 的負載,只有 Hardware Composer 無法處理的圖層,或者指定用 OpenGL 處理的圖層,其他的 圖層偶會使用 Hardware Composer 進行合成
  6. 第六階段 :最終合成好的數據放到屏幕對應的 Frame Buffer 中,固定刷新的時候就可以看到了

下面這張圖也是官方的一張圖,結合上面的階段,從左到右看,可以看到一幀的數據是如何在各個進程之間流動的

file:///Users/gaojack/blog/source/images/15751536775887.jpg 數據流動

Systrace 中的圖像數據流

瞭解了 Android 中的圖形數據流的方向,我們就可以把上面這個比較抽象的數據流圖,在 Systrace 上進行映射展示

Systrace 中的圖像數據流
Systrace 中的圖像數據流

上圖中主要包含 SurfaceFlinger、App 和 hwc 三個進程,下面就來結合圖中的標號,來進一步說明數據的流向

  1. 第一個 Vsync 信號到來, SurfaceFlinger 和 App 同時收到 Vsync 信號
  2. SurfaceFlinger 收到 Vsync-sf 信號,開始進行 App 上一幀的 Buffer 的合成
  3. App 收到 Vsycn-app 信號,開始進行這一幀的 Buffer 的渲染(對應上面的第一、二、三、四階段)
  4. 第二個 Vsync 信號到來 ,SurfaceFlinger 和 App 同時收到 Vsync 信號,SurfaceFlinger 獲取 App 在第二步裏面渲染的 Buffer,開始合成(對應上面的第五階段),App 收到 Vsycn-app 信號,開始新一幀的 Buffer 的渲染(對應上面的第一、二、三、四階段)

Vsync Offset

文章最開始有提到,Vsync 信號可以由硬件產生,也可以用軟件模擬,不過現在基本上都是硬件產生,負責產生硬件 Vsync 的是 HWC,HWC 可生成 VSYNC 事件並通過回調將事件發送到 SurfaceFlinge , DispSync 將 Vsync 生成由 Choreographer 和 SurfaceFlinger 使用的 VSYNC_APP 和 VSYNC_SF 信號.

disp_sync_arch
disp_sync_arch

其中 app 和 sf 相對 hw_vsync_0 都有一個偏移,即 phase-app 和 phase-sf,如下圖

offset
offset

Vsync Offset 我們指的是 VSYNC_APP 和 VSYNC_SF 之間有一個 Offset,即上圖中 phase-sf - phase-app 的值,這個 Offset 是廠商可以配置的。如果 Offset 不爲 0,那麼意味着 App 和 SurfaceFlinger 主進程不是同時收到 Vsync 信號,而是間隔 Offset (通常在 0 - 16.6ms 之間)

目前大部分廠商都沒有配置這個 Offset,所以 App 和 SurfaceFlinger 是同時收到 Vsync 信號的.

可以通過 Dumpsys SurfaceFlinger 來查看對應的值

Offset 爲 0:(sf phase - app phase = 0)

Sync configuration: [using: EGL_ANDROID_native_fence_sync EGL_KHR_wait_sync]
DispSync configuration: 
          app phase 1000000 ns,              sf phase 1000000 ns 
    early app phase 1000000 ns,        early sf phase 1000000 ns 
 early app gl phase 1000000 ns,     early sf gl phase 1000000 ns 
     present offset 0 ns                      refresh 16666666 ns

Offset 不爲 0 (SF phase - app phase = 4 ms)

Sync configuration: [using: EGL_ANDROID_native_fence_sync EGL_KHR_wait_sync]

VSYNC configuration:
         app phase:   2000000 ns          SF phase:   6000000 ns
   early app phase:   2000000 ns    early SF phase:   6000000 ns
GL early app phase:   2000000 ns GL early SF phase:   6000000 ns
    present offset:         0 ns      VSYNC period:  16666666 ns

下面以 Systrace 爲例,來看 Offset 在 Systrace 中的表現

Offset 爲 0

首先說 Offset 爲 0 的情況, 此時 App 和 SurfaceFlinger 是同時收到 Vsync 信號 , 其對應的 Systrace 圖如下:

Offset 爲 0
Offset 爲 0

這個圖上面也有講解,這裏就不再詳細說明,大家只需要看到,App 渲染好的 Buffer,要等到下一個 Vsync-SF 來的時候纔會被 SurfaceFlinger 拿去做合成,這個時間大概在 16.6 ms。這時候大家可能會想,如果 App 的 Buffer 渲染結束,Swap 到 BufferQueue 中 ,就觸發 SurfaceFlinger 去做合成,那豈不是省了一些時間(0-16.6ms )?

答案是可行的,這也就引入了 Offset 機制,在這種情況下,App 先收到 Vsync 信號,進行一幀的渲染工作,然後過了 Offset 時間後,SurfaceFlinger 才收到 Vsync 信號開始合成,這時候如果 App 的 Buffer 已經 Ready 了,那麼 SurfaceFlinger 這一次合成就可以包含 App 這一幀,用戶也會早一點看到。

Offset 不爲 0

下圖中,就是一個 Offset 爲 4ms 的案例,App 收到 Vsync 4 ms 之後,SurfaceFlinger 才收到 Vsync 信號

Offset 不爲 0
Offset 不爲 0

Offset 的優缺點

Offset 的一個比較難以確定的點就在於 Offset 的時間該如何設置,這也是衆多廠商默認都不進行配置 Offset 的一個原因,其優缺點是動態的,與機型的性能和使用場景有很大的關係

  1. 如果 Offset 配置過短,那麼可能 App 收到 Vsync-App 後還沒有渲染完成,SurfaceFlinger 就收到 Vsync-SF 開始合成,那麼此時如果 App 的 BufferQueue 中沒有之前累積的 Buffer,那麼 SurfaceFlinger 這次合成就不會有 App 的東西在裏面,需要等到下一個 Vsync-SF 才能合成這次 App 的內容,時間相當於變成了 Vsync 週期+Offset,而不是我們期待的 Offset
  2. 如果 Offset 配置過長,就起不到作用了

HW_Vsync

這裏需要說明的是,不是每次申請 Vsync 都會由硬件產生 Vsync,只有此次請求 vsync 的時間距離上次合成時間大於 500ms,纔會通知 hwc,請求 HW_VSYNC

以桌面滑動爲例,看 SurfaceFlinger 的進程 Trace 可以看到 HW_VSYNC 的狀態

HW_Vsync
HW_Vsync

後續 App 申請 Vsync 時候,會有兩種情況,一種是有 HW_VSYNC 的情況,一種是沒有有 HW_VSYNC 的情況

不使用HW_VSYNC

不使用HW_VSYNC
不使用HW_VSYNC

使用 HW_VSYNC

file:///Users/gaojack/blog/source/images/15751538247774.jpg 使用 HW_VSYNC

HW_VSYNC 主要是利用最近的硬件 VSYNC 來做預測,最少要 3 個,最多是 32 個,實際上要用幾個則不一定, DispSync 拿到 6 個 VSYNC 後就會計算出 SW_VSYNC,只要收到的 Present Fence 沒有超過誤差,硬件 VSYNC 就會關掉,不然會繼續接收硬件 VSYNC 計算 SW_VSYNC 的值,直到誤差小於 threshold.關於這一塊的計算具體過程,可以參考這篇文章: SW-VSYNC 的生成與傳遞 ,關於這一塊的流程大家也可以參考這篇文章,裏面有更細節的內容,這裏摘錄了他的結論

SurfaceFlinger 通過實現了 HWC2::ComposerCallback 接口,當 HW-VSYNC 到來的時候,SurfaceFlinger 將會收到回調並且發給 DispSync。DispSync 將會把這些 HW-VSYNC 的時間戳記錄下來,當累計了足夠的 HW-VSYNC 以後(目前是大於等於 6 個),就開始計算 SW-VSYNC 的偏移 mPeriod。計算出來的 mPeriod 將會用於 DispSyncThread 用來模擬 HW-VSYNC 的週期性起來並且通知對 VSYNC 感興趣的 Listener,這些 Listener 包括 SurfaceFlinger 和所有需要渲染畫面的 app。這些 Listener 通過 EventThread 以 Connection 的抽象形式註冊到 EventThread。DispSyncThread 與 EventThread 通過 DispSyncSource 作爲中間人進行連接。EventThread 在收到 SW-VSYNC 以後將會把通知所有感興趣的 Connection,然後 SurfaceFlinger 開始合成,app 開始畫幀。在收到足夠多的 HW-VSYNC 並且在誤差允許的範圍內,將會關閉通過 EventControlThread 關閉 HW-VSYNC。

關於我 && 博客

  1. 關於我 , 非常希望和大家一起交流 , 共同進步 .
  2. 博客內容導航
  3. 優秀博客文章記錄 - Android 性能優化必知必會

一個人可以走的更快 , 一羣人可以走的更遠

參考

  1. VSYNC
  2. https://juejin.im/post/5b6948086fb9a04fb87771fb
  3. http://gityuan.com/2017/02/05/graphic_arch/
  4. SW-VSYNC 的生成與傳遞
  5. http://echuang54.blogspot.com/2015/01/dispsync.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章