Systrace 之 Vsync 解讀
和你一起終身學(xué)習(xí),這里是程序員 Android
本篇文章主要介紹 Android 開發(fā)中的部分知識點(diǎn),通過閱讀本篇文章,您將收獲以下內(nèi)容:
一、Vsync 概述二、Android 圖形數(shù)據(jù)流向三、Systrace 中的圖像數(shù)據(jù)流四、Vsync Offset五、HW_Vsync
一、Vsync 概述
Vsync 信號可以由硬件產(chǎn)生,也可以用軟件模擬,不過現(xiàn)在基本上都是硬件產(chǎn)生,負(fù)責(zé)產(chǎn)生硬件 Vsync 的是 HWC,HWC 可生成 VSYNC 事件并通過回調(diào)將事件發(fā)送到 SurfaceFlinger , DispSync 將 Vsync 生成由 Choreographer 和 SurfaceFlinger 使用的 VSYNC_APP 和 VSYNC_SF 信號
在 Android 基于 Choreographer 的渲染機(jī)制詳解 這篇文章里面,我們有提到 :Choreographer 的引入,主要是配合 Vsync,給上層 App 的渲染提供一個穩(wěn)定的 Message 處理的時機(jī),也就是 Vsync 到來的時候 ,系統(tǒng)通過對 Vsync 信號周期的調(diào)整,來控制每一幀繪制操作的時機(jī). 目前大部分手機(jī)都是 60Hz 的刷新率,也就是 16.6ms 刷新一次,系統(tǒng)為了配合屏幕的刷新頻率,將 Vsync 的周期也設(shè)置為 16.6 ms,每個 16.6 ms,Vsync 信號喚醒 Choreographer 來做 App 的繪制操作 ,這就是引入 Choreographer 的主要作用
渲染層(App)與 Vsync 打交道的是 Choreographer,而合成層與 Vsync 打交道的,則是 SurfaceFlinger。SurfaceFlinger 也會在 Vsync 到來的時候,將所有已經(jīng)準(zhǔn)備好的 Surface 進(jìn)行合成操作
下圖顯示在 Systrace 中,SurfaceFlinger 進(jìn)程中的 VSYNC_APP 和 VSYNC_SF 的情況
二、Android 圖形數(shù)據(jù)流向
首先我們要大概了解 Android 中的圖形數(shù)據(jù)流的方向,從下面這張圖,結(jié)合 Android 的圖像流,我們大概把從 App 繪制到屏幕顯示,分為下面幾個階段:
image
- 第一階段:App 在收到 Vsync-App 的時候,在主線程進(jìn)行 measure、layout、draw(構(gòu)建 DisplayList , 里面包含 OpenGL 渲染需要的命令及數(shù)據(jù)) 。這里對應(yīng)的 Systrace 中的主線程 doFrame 操作
- 第二階段:CPU 將數(shù)據(jù)上傳(共享或者拷貝)給 GPU, 這里 ARM 設(shè)備 內(nèi)存一般是 GPU 和 CPU 共享內(nèi)存。這里對應(yīng)的 Systrace 中的渲染線程的 flush drawing commands 操作
- 第三階段:通知 GPU 渲染,真機(jī)一般不會阻塞等待 GPU 渲染結(jié)束,CPU 通知結(jié)束后就返回繼續(xù)執(zhí)行其他任務(wù),使用 Fence 機(jī)制輔助 GPU CPU 進(jìn)行同步操作
- 第四 階段:swapBuffers,并通知 SurfaceFlinger 圖層合成。這里對應(yīng)的 Systrace 中的渲染線程的 eglSwapBuffersWithDamageKHR 操作
- 第五階段:SurfaceFlinger 開始合成圖層,如果之前提交的 GPU 渲染任務(wù)沒結(jié)束,則等待 GPU 渲染完成,再合成(Fence 機(jī)制),合成依然是依賴 GPU,不過這就是下一個任務(wù)了.這里對應(yīng)的 Systrace 中的 SurfaceFlinger 主線程的 onMessageReceived 操作(包括 handleTransaction、handleMessageInvalidate、handleMessageRefresh)SurfaceFlinger 在合成的時候,會將一些合成工作委托給 Hardware Composer,從而降低來自 OpenGL 和 GPU 的負(fù)載,只有 Hardware Composer 無法處理的圖層,或者指定用 OpenGL 處理的圖層,其他的 圖層偶會使用 Hardware Composer 進(jìn)行合成
- 第六階段 :最終合成好的數(shù)據(jù)放到屏幕對應(yīng)的 Frame Buffer 中,固定刷新的時候就可以看到了
下面這張圖也是官方的一張圖,結(jié)合上面的階段,從左到右看,可以看到一幀的數(shù)據(jù)是如何在各個進(jìn)程之間流動的
三、Systrace 中的圖像數(shù)據(jù)流
了解了 Android 中的圖形數(shù)據(jù)流的方向,我們就可以把上面這個比較抽象的數(shù)據(jù)流圖,在 Systrace 上進(jìn)行映射展示
上圖中主要包含 SurfaceFlinger、App 和 hwc 三個進(jìn)程,下面就來結(jié)合圖中的標(biāo)號,來進(jìn)一步說明數(shù)據(jù)的流向
- 第一個 Vsync 信號到來, SurfaceFlinger 和 App 同時收到 Vsync 信號
- SurfaceFlinger 收到 Vsync-sf 信號,開始進(jìn)行 App 上一幀的 Buffer 的合成
- App 收到 Vsycn-app 信號,開始進(jìn)行這一幀的 Buffer 的渲染(對應(yīng)上面的第一、二、三、四階段)
- 第二個 Vsync 信號到來 ,SurfaceFlinger 和 App 同時收到 Vsync 信號,SurfaceFlinger 獲取 App 在第二步里面渲染的 Buffer,開始合成(對應(yīng)上面的第五階段),App 收到 Vsycn-app 信號,開始新一幀的 Buffer 的渲染(對應(yīng)上面的第一、二、三、四階段)
四、Vsync Offset
文章最開始有提到,Vsync 信號可以由硬件產(chǎn)生,也可以用軟件模擬,不過現(xiàn)在基本上都是硬件產(chǎn)生,負(fù)責(zé)產(chǎn)生硬件 Vsync 的是 HWC,HWC 可生成 VSYNC 事件并通過回調(diào)將事件發(fā)送到 SurfaceFlinge , DispSync 將 Vsync 生成由 Choreographer 和 SurfaceFlinger 使用的 VSYNC_APP 和 VSYNC_SF 信號.
disp_sync_arch
其中 app 和 sf 相對 hw_vsync_0 都有一個偏移,即 phase-app 和 phase-sf,如下圖
image
Vsync Offset 我們指的是 VSYNC_APP 和 VSYNC_SF 之間有一個 Offset,即上圖中 phase-sf - phase-app 的值,這個 Offset 是廠商可以配置的。如果 Offset 不為 0,那么意味著 App 和 SurfaceFlinger 主進(jìn)程不是同時收到 Vsync 信號,而是間隔 Offset (通常在 0 - 16.6ms 之間)
目前大部分廠商都沒有配置這個 Offset,所以 App 和 SurfaceFlinger 是同時收到 Vsync 信號的.
可以通過 Dumpsys SurfaceFlinger 來查看對應(yīng)的值
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 nsGL early app phase: 2000000 ns GL early SF phase: 6000000 ns present offset: 0 ns VSYNC period: 16666666 ns
下面以 Systrace 為例,來看 Offset 在 Systrace 中的表現(xiàn)
1.Offset 為 0
首先說 Offset 為 0 的情況, 此時 App 和 SurfaceFlinger 是同時收到 Vsync 信號 , 其對應(yīng)的 Systrace 圖如下:
這個圖上面也有講解,這里就不再詳細(xì)說明,大家只需要看到,App 渲染好的 Buffer,要等到下一個 Vsync-SF 來的時候才會被 SurfaceFlinger 拿去做合成,這個時間大概在 16.6 ms。這時候大家可能會想,如果 App 的 Buffer 渲染結(jié)束,Swap 到 BufferQueue 中 ,就觸發(fā) SurfaceFlinger 去做合成,那豈不是省了一些時間(0-16.6ms )?
答案是可行的,這也就引入了 Offset 機(jī)制,在這種情況下,App 先收到 Vsync 信號,進(jìn)行一幀的渲染工作,然后過了 Offset 時間后,SurfaceFlinger 才收到 Vsync 信號開始合成,這時候如果 App 的 Buffer 已經(jīng) Ready 了,那么 SurfaceFlinger 這一次合成就可以包含 App 這一幀,用戶也會早一點(diǎn)看到。
2.Offset 不為 0
下圖中,就是一個 Offset 為 4ms 的案例,App 收到 Vsync 4 ms 之后,SurfaceFlinger 才收到 Vsync 信號
image
3.Offset 的優(yōu)缺點(diǎn)
Offset 的一個比較難以確定的點(diǎn)就在與 Offset 的時間該如何設(shè)置,這也是眾多廠商默認(rèn)都不進(jìn)行配置 Offset 的一個原因,其優(yōu)缺點(diǎn)是動態(tài)的,與機(jī)型的性能和使用場景有很大的關(guān)系
- 如果 Offset 配置過短,那么可能 App 收到 Vsync-App 后還沒有渲染完成,SurfaceFlinger 就收到 Vsync-SF 開始合成,那么此時如果 App 的 BufferQueue 中沒有之前累積的 Buffer,那么 SurfaceFlinger 這次合成就不會有 App 的東西在里面,需要等到下一個 Vsync-SF 才能合成這次 App 的內(nèi)容,時間相當(dāng)于變成了 Vsync 周期+Offset,而不是我們期待的 Offset
- 如果 Offset 配置過長,就起不到作用了
五、HW_Vsync
這里需要說明的是,不是每次申請 Vsync 都會由硬件產(chǎn)生 Vsync,只有此次請求 vsync 的時間距離上次合成時間大于 500ms,才會通知 hwc,請求 HW_VSYNC
以桌面滑動為例,看 SurfaceFlinger 的進(jìn)程 Trace 可以看到 HW_VSYNC 的狀態(tài)
后續(xù) App 申請 Vsync 時候,會有兩種情況,一種是有 HW_VSYNC 的情況,一種是沒有有 HW_VSYNC 的情況
1.不使用HW_VSYNC
2.使用 HW_VSYNC
HW_VSYNC 主要是利用最近的硬件 VSYNC 來做預(yù)測,最少要 3 個,最多是 32 個,實際上要用幾個則不一定, DispSync 拿到 6 個 VSYNC 后就會計算出 SW_VSYNC,只要收到的 Present Fence 沒有超過誤差,硬件 VSYNC 就會關(guān)掉,不然會繼續(xù)接收硬件 VSYNC 計算 SW_VSYNC 的值,直到誤差小于 threshold.關(guān)于這一塊的計算具體過程,可以參考這篇文章: S W-VS YN C 的生成與傳遞 ,關(guān)于這一塊的流程大家也可以參考這篇文章,里面有更細(xì)節(jié)的內(nèi)容,這里摘錄了他的結(jié)論
SurfaceFlinger 通過實現(xiàn)了 HWC2::ComposerCallback 接口,當(dāng) HW-VSYNC 到來的時候,SurfaceFlinger 將會收到回調(diào)并且發(fā)給 DispSync。DispSync 將會把這些 HW-VSYNC 的時間戳記錄下來,當(dāng)累計了足夠的 HW-VSYNC 以后(目前是大于等于 6 個),就開始計算 SW-VSYNC 的偏移 mPeriod。計算出來的 mPeriod 將會用于 DispSyncThread 用來模擬 HW-VSYNC 的周期性起來并且通知對 VSYNC 感興趣的 Listener,這些 Listener 包括 SurfaceFlinger 和所有需要渲染畫面的 app。這些 Listener 通過 EventThread 以 Connection 的抽象形式注冊到 EventThread。DispSyncThread 與 EventThread 通過 DispSyncSource 作為中間人進(jìn)行連接。EventThread 在收到 SW-VSYNC 以后將會把通知所有感興趣的 Connection,然后 SurfaceFlinger 開始合成,app 開始畫幀。在收到足夠多的 HW-VSYNC 并且在誤差允許的范圍內(nèi),將會關(guān)閉通過 EventControlThread 關(guān)閉 HW-VSYNC。