Android 圖形顯示流程簡介

注:本文縮寫說明

圖片

本文代碼都是基於 Android S

一、概述

本文將對從 App 畫出一幀畫面到這幀畫面是如何到達屏幕並最終被人眼看到的這一過程進行簡要分析,並將這其中涉及到的各個流程與其在 systrace 上的體現對應起來,期望最終能夠讓讀者對 Android 系統下的畫面顯示流程有一個宏觀的認識。

圖片

上圖爲 Android 的圖形顯示系統框架圖,首先上層應用通過 ViewRoot 的 scheduleTraversals 函數發起繪製任務,並通過 HWUI 調用 OpenGL 接口將繪製數據傳遞給 GPU 處理;SF 會接收所有應用更新的繪製數據,並根據 Z-Order、透明度、大小、位置等參數計算出每個應用圖層在最終合成圖像中的位置;SF 完成圖層處理後會把所有的應用圖層提供給 HWC,由 HWC 來決定這些圖層的合成策略並調用屏顯驅動做合成;最後屏顯驅動會將合成的最終畫面送到硬件屏幕顯示。

Android 系統通過 buffer 緩衝區來保存圖形信息,這個 buffer 的內存空間是有 SF 向圖形內存分配器 Gralloc 申請的,而 Gralloc 是通過 ION Driver 從 kernel 中開闢出一塊共享內存。在整個圖形顯示流程中,buffer 緩衝區會在 App、SF、HWC 之間來回流轉,本文將通過 buffer 的數據流向來詳細闡述 Android 的畫面顯示過程。

二、App 到 SF

應用界面發生變化也就是 view 數據發生了變化,會引起 ViewRootImpl 的 requestLayout 調用,進而調用 scheduleTraversals 方法。scheduleTraversals 方法內會先向消息隊列發送一個同步屏障,然後執行 Choreographer 回調,註冊 CALLBACK_TRAVERSAL 類型的監聽。Choreographer 在監聽到下一次 Vsync 信號來了後,會執行 mTraversalRunnable 的 run 方法,該方法內會調用 doTraversal 方法,移除消息隊列中的同步屏障,並通過 performTraversals 方法觸發佈局繪製流程。

圖片

Choreographer 和 Vsync 是在 Android 4.1 上黃油計劃中引入的兩個重要元素,最初的 Vsync 信號是由硬件屏幕根據系統幀率以固定的頻率(60/90/120fps)向系統發送的時間脈衝信號,Choreographer 和 SurfaceFlinger 都會監聽這個信號來做繪製和合成。後來爲了進一步提升界面流暢性體驗,Android 4.4 上又引入了 Vsync 虛擬化,通過 DispSyncThread 把 Vsync 虛擬化成 Vsync-app 和 Vsync-sf,Vsync-app 和 Vsync-sf 之間有固定的時間偏移,各自分別掌控着 App 和 SurfaceFlinger 的工作節奏,他們一前一後保持着繪製任務的流水節奏。

當 Choreographer 監聽到 Vsync 信號後會回調 doFrame 方法,該方法主要完成兩件事情:確認當前幀執行的時間戳和渲染當前幀數據。渲染數據時會順序執行輸入 (Input)、動畫(Animation)、繪製(TRAVERSAL)、提交(Commit) 這 4 種類型的回調函數,而在執行 TRAVERSAL 類型的 callback 方法時就會調用到上面提到的 doTraversal 下的 performTraversals 方法觸發佈局繪製流程。

圖片

下圖的 systrace 展示了 UI 數據從 App 到 SurfaceFlinger 的處理流程。從 systrace 上可以看到應用端有兩個線程 UI Thread 和 Render Thread 用於處理應用想要更新的 UI 數據,其中 UI Thread 通過 Choreographer 配合 Vsync 信號有節奏的控制應用的繪製任務,並將所有要繪圖的信息以繪圖指令的形式記錄到 DisplayList 裏同步給 Render Thread,Render Thread 則將 UI Thread 傳遞過來的繪圖數據送到 GPU 去做渲染。一旦同步工作完成,UI Thread 就可以繼續等下一個 Vsync-app 信號的到來,接着做下一幀的繪製。

Render Thread 在做 GPU 渲染之前還需要申請一塊 buffer 緩衝區用於存儲 GPU 渲染的數據,這個 buffer 是應用通過 Surface 接口的 dequeueBuffer 方法向 BufferQueue 申請的,拿到 buffer 之後應用會調用 OpenGL 接口將繪製指令傳遞給 GPU 進行渲染,然後再通過 queueBuffer 函數將保存有渲染數據的 buffer 提交到 SF 作圖層處理。

圖片

值得注意的是在 Android S 以前,buffer 狀態都是由 BufferQueue 進行管理,而 bufferQueue 是在 SF 中創建的,應用端的 dequeue、queue Buffer 操作都是通過 binder 和 SF 進行交互。但是在 Android S 上,BufferQueue 的創建從 SF 中移出來,變成 BLASTBufferQueue 去完成 BufferQueue 的創建,dequeue、queue、acquire、release Buffer 的操作均由 App 進行。當 App 繪製完一幀後,會通過 BLASTBufferQueue 接口執行 onFrameAvailable 回調,並通過 transaction 事務將該 buffer 提交到 SF。

三、SF 到 HWC

當 SF 監聽到 Vsync-sf 信號就會調用 onMessageReceived 函數來處理 UI 數據。

圖片

onMessageReceived 方法主要是用來處理兩個 Message:

INVALIDATE 消息 -- onMessageInvalidate 首先會調用 handleMessageTransaction 方法檢查 Layer/Display 的屬性和數量是否有變化;然後調用 handleMessageInvalidate 方法檢查每個 Layer 是否有新提交的 buffer, 如果有則通過 handlePageFlip 調用 latchBuffer 方法將 BufferQueue 中的 buffer 通過 acquireBuffer 拿走;最後還會檢查 HWC 是否有 repaint 請求。上述三種情況只要有一種發生了,refreshNeeded 就會被置爲 true,onMessageRefresh 方法就會被調用做圖像合成。

圖片

REFRESH 消息—onMessageRefresh 方法則會對所有有 buffer 更新的 Layer 進行合成,大部分的合成工作是在 present 方法裏完成的,這裏 SurfaceFlinger 會和 HWC 進行交互,協同完成畫面的合成。基本流程如下:

  1. SF 爲 HWC 提供完整的 Layer 列表並通過 chooseCompositionStrategy 方法詢問合成策略。

  2. HWC 根據硬件性能決定是使用硬件圖層合成器還是 GPU 合成,並分別將每個 Layer 對應標記爲 overlay 或 GLES composition 來進行響應。

  3. 需要硬件圖層合成器合成的 Layer 直接由 HWC 處理,需要 GPU 合成的 Layer 則需要先由 SF 負責合成一個 Layer 後再和其他的 Layer 一起遞交給 HWC 完成剩餘的合成和顯示。

當畫面合成結束以後,SF 還會調用 postComposition 做一些合成後的處理,其中就包括通過 releaseBuffer 釋放 SF 在合成上一幀畫面時拿到的所有 buffer,以便應用在下次 dequeueBuffer 時能夠拿到可用的 buffer。

圖片

下圖的 systrace 展示了 UI 數據從 SF 到 HWC 的處理流程。當 SF 監聽到 Vsync-sf 信號後會順序執行 INVALIDATE 和 REFRESH 消息的回調函數,在執行 onMessageRefresh 方法時,SF 和 HWC 會協同選擇合成策略,並由 HWC 完成最終的畫面合成。畫面合成完成後,SF 會通過 transaction 事務通知應用釋放用於繪製上一幀畫面的 buffer。

圖片

四、HWC 到屏幕

systrace 上看到的都是軟件上的一些操作,無法看到硬件的處理流程。而 UI 數據從 HWC 到屏幕的這個階段只有一小段時間是在軟件上進行的,大部分時間都是硬件在處理數據。我們首先來看看該階段軟件上的主要工作,HWC 是通過 DRM 向屏幕輸出畫面的,DRM 是 linux 內核的一個子系統,用於負責與顯卡交互,因此 HWC 會先通過 drmModeAtomicCommit 接口向 kernel 提交 Layer 數據,drmModeAtomicCommit 通過 ioctl 調用到 kernel,然後通知 disp thread 也就是下圖中所示的 crtc_commit 線程調用 complete_commit 函數完成剩下的合成工作。

圖片

crtc_commit 線程執行 complete_commit 方法時幾乎一直處於 D 狀態,表明此時 DRM 內的硬件正在做合成工作。經過一系列合成處理後最終的畫面會通過 DSI 接口送到屏幕顯示。至此,UI 數據就完成了從應用端到硬件屏幕的旅程。

五、總結

一個 buffer 從被應用 dequeue 到最後 HWC 完成畫面合成後由 SF release 的整個生命週期經歷瞭如下流程:

  1. Vsync-app 信號到達後,APP 端由 Choreographer 順序執行 Input、Animation 和 Traversal 的回調,然後通過 dequeueBuffer 拿到一個可用的 buffer 開始繪圖,再通過 queueBuffer 將包含繪圖數據的 buffer 提交到 SF。

  2. Vsync-sf 信號到達後,SF 首先通過 aquireBuffer 獲取所有 Layer 更新的 buffer,然後根據 Z-Order、透明度、大小、位置等參數對這些 Layer 進行圖層處理,接着將所有的 Layer 送到 HWC 做合成。

  3. HWC 對多個 Layer 做合成,合成完成後通過 libdrm 提供的接口通知 DRM 模塊把圖像顯示到屏幕。

  4. 最終的畫面送到屏幕後,HWC 會將用完的 buffer 還給 SF,SF 會在下一個 Vsync-sf 信號到達後 release 被歸還的 buffer。

本文以 UI 數據在顯示系統中的數據流向爲指引,依次分析了 App、SF、HWC 和硬件屏幕處理 UI 數據的基本步驟。Android 顯示系統十分複雜,涉及模塊較多,並且隨着時間的推移也在不斷更新優化,但是其處理 UI 數據的基本流向不會變,本文旨在幫助讀者對顯示系統有一個宏觀的認識,其中的一些處理細節還需要讀者通過閱讀源碼慢慢領會。

參考資料:

1.Android 源碼:https://cs.android.com/android/platform/superproject

2.Android 圖形系統篇總結:https://www.jianshu.com/p/180e1b6d0dcd

3.Android Systrace 基礎知識:https://www.androidperformance.com/2019/05/28/Android-Systrace-About/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/LOa6u051OPPEPUASjvIbPg