安卓渲染流程學習筆記

幀率

大家都知道安卓顯示的幀率為60fps但是為什么呢?
人類視覺系統每秒可處理10到12個圖像并單獨感知它們,而較高的速率則被視為運動。12fps 大概類似手動快速翻動書籍的幀率,這明顯是可以感知到不夠順滑的。24fps 使得人眼感知的是連續線性的運動,這其實是歸功于運動模糊的效果。24fps 是電影膠圈通常使用的幀率,因為這個幀率已經足夠支撐大部分電影畫面需要表達的內容。一般來說 30fps 就是可以接受的,低于 30fps 是無法順暢表現絢麗的畫面內容的,此時將幀率提升至 60fps 則可以明顯提升交互感和逼真感,但是一般來說超過 75fps 一般就不容易察覺到有明顯的流暢度提升了,所以,超過 60fps 是沒有必要的,并且如果幀率超過屏幕刷新率(手機屏幕通常都是 60fps 的刷新率)只會浪費圖形處理的能力,因為屏幕不能以這么快的速度更新,這樣超過刷新率的幀率就浪費掉了。
Android 在設計的時候,把幀頻限定在了每秒 60 幀,當我們的 APP 的幀頻 60fps 時,畫面就會非常的流暢。但是通常由于各種各樣的原因,幀頻很可能會小于 60fps,這樣就會出現丟幀現象,用戶端表現為可感知的卡頓等現象。那面我們的幀頻可以高于 60fps 嗎,答案是否定的,這是因為界面刷新渲染依賴底層的 VSYNC 信號,VSYNC 信號以每秒 60 次的頻率發送給上層,并且高于 60fps 的幀頻也是沒有必要的,因為人眼與大腦之間的協作無法感知超過 60fps 的畫面更新。
開發 app 的性能目標就是保持 60fps,這意味著每一幀你只有 1000/60=16.67ms 的時間來處理所有的任務。如果 16ms 內沒有辦法把這一幀的任務執行完畢,就會發生丟幀的現象。丟幀越多,用戶感受到的卡頓情況就越嚴重。

卡頓原因

大多數用戶感知到的卡頓等性能問題的最主要根源都是因為渲染性能,也就是 16ms 內沒有辦法把這一幀的任務執行完畢,發生了丟幀的現象。從設計師和產品的角度,他們希望 App 能夠有更多的動畫、圖片等時尚元素來實現流暢的用戶體驗。但是 Android 系統很有可能無法及時完成那些復雜的界面渲染操作。Android 系統每隔 16ms 發出 VSYNC 信號,觸發對 UI 進行渲染,如果每次渲染都成功,這樣就能夠達到流暢的畫面所需要的 60fps,為了能夠實現 60fps,這意味著程序的大多數操作都必須在 16ms 內完成。
如果 App 的某個操作花費時間是 24ms,系統在得到 VSYNC 信號的時候就無法進行正常渲染,這樣就發生了丟幀現象。那么用戶在 32ms 內看到的會是同一幀畫面。
通常,越復雜的 UI,越容易導致卡頓,例如,用戶容易在 UI 執行動畫或者滑動 ListView 的時候感知到卡頓不流暢,是因為這里的操作相對復雜,容易發生丟幀的現象,從而感覺卡頓。有很多原因可以導致丟幀,也許是因為你的 layout 太過復雜,無法在 16ms 內完成渲染,有可能是因為你的 UI 上有層疊太多的繪制單元,還有可能是因為動畫執行的次數過多。這些都會導致 CPU 或者 GPU 負載過重。
具體可見Android 黃油計劃和顯示刷新機制學習筆記

Android 渲染機制

Android 系統采用一種稱為 Surface 的 UI 架構為應用程序提供用戶界面。在 Android 應用程序中,每一個 Activity 組件都關聯有一個或者若干個窗口,每一個窗口都對應有一個 Surface。有了這個 Surface 之后,應用程序就可以在上面渲染窗口的 UI。最終這些已經繪制好了的 Surface 都會被統一提交給 Surface 管理服務 SurfaceFlinger 進行合成,最后顯示在屏幕上面。無論是應用程序,還是 SurfaceFlinger,都可以利用 GPU 等硬件來進行 UI 渲染,以便獲得更流暢的 UI。
一句話來概括一下 Android 應用程序顯示的過程:Android 應用程序調用 SurfaceFlinger 服務把經過測量、布局和繪制后的 Surface 渲染到顯示屏幕上。
成員包括:

  • ViewRootImpl:用來控制窗口的渲染,以及用來與 WindowManagerService、SurfaceFlinger 通信。

  • WindowManager:WindowManager 會控制窗口對象,它們是用于容納視圖對象的容器。窗口對象始終由 Surface 對象提供支持。WindowManager 會監督生命周期、輸入和聚焦事件、屏幕方向、轉換、動畫、位置、變形、Z 軸順序以及窗口的許多其他方面。WindowManager 會將所有窗口元數據發送到 SurfaceFlinger,以便 SurfaceFlinger 可以使用這些數據在屏幕上合成 Surface。

  • Surface:Android 應用的每個窗口對應一個畫布(Canvas),即 Surface,可以理解為 Android 應用程序的一個窗口。Surface 是一個接口,供生產方與使用方交換緩沖區。

  • SurfaceView:SurfaceView 是一個組件,可用于在 View 層次結構中嵌入其他合成層。SurfaceView 采用與其他 View 相同的布局參數,因此可以像對待其他任何 View 一樣對其進行操作,但 SurfaceView 的內容是透明的。當 SurfaceView 的 View 組件即將變得可見時,框架會要求 SurfaceControl 從 SurfaceFlinger 請求新的 Surface。

  • BufferQueue:BufferQueue 類將可生成圖形數據緩沖區的組件(生產方)連接到接受數據以便進行顯示或進一步處理的組件(使用方)。幾乎所有在系統中移動圖形數據緩沖區的內容都依賴于 BufferQueue。

  • SurfaceFlinger:Android 系統服務,負責管理 Android 系統的幀緩沖區,即顯示屏幕。

  • EGLSurface 和 OpenGL ES:OpenGL ES (GLES) 定義了用于與 EGL 結合使用的圖形渲染 API。EGI 是一個規定如何通過操作系統創建和訪問窗口的庫(要繪制紋理多邊形,請使用 GLES 調用;要將渲染放到屏幕上,請使用 EGL 調用)。

  • Vulkan:Vulkan 是一種用于高性能 3D 圖形的低開銷、跨平臺 API。與 OpenGL ES 一樣,Vulkan 提供用于在應用中創建高質量實時圖形的工具。

Android 圖形架構圖

圖片一

圖形系統使用生產者-消費者模式,根據框架圖,可以看出基本的操作流程:

圖形生產者創建一個 Surface,將圖形數據畫到 Surface 上。Surface 使用的圖形緩沖是通過 Gralloc 來分配的。 圖形生產者的窗口信息由 WindowManager 管理,WindowManager 將窗口元數據發送給 SurfaceFlinger 進行合成。 圖形消費者處理圖形生產者產生的數據。當圖形數據用于屏幕顯示時,SurfaceFlinger 使用窗口元數據將圖形緩沖合成到顯示上。圖形數據最終渲染到顯示設備上是通過 Hardware Composer 完成的。

圖像流生產方

圖像流生產方可以是生成圖形緩沖區以供消耗的任何內容。例如 OpenGL ES、Canvas 2D 和 mediaserver 視頻解碼器。

圖像流消耗方

圖像流的最常見消耗方是 SurfaceFlinger,該系統服務會消耗當前可見的 Surface,并使用窗口管理器中提供的信息將它們合成到顯示部分。
其他 OpenGL ES 應用也可以消耗圖像流,例如相機應用會消耗相機預覽圖像流。非 GL 應用也可以是使用方,例如 ImageReader 類

應用側繪制

一個 Android 應用程序窗口里面包含了很多 UI 元素,它們是以樹形結構來組織的,父視圖包含子視圖,一個窗口的根視圖是 DecorView 對象,ViewRootImpl 負責與系統進程進行通信。

在繪制一個 Android 應用程序窗口的 UI 之前,我們首先要確定它里面的各個子 UI 元素在父 UI 元素里面的大小以及位置。確定各個子 UI 元素在父 UI 元素里面的大小以及位置的過程又稱為測量過程和布局過程。因此,Android 應用程序窗口的 UI 渲染過程可以分為測量、布局和繪制三個階段,最后生成 Display List。

  1. 測量:遞歸(深度優先)確定所有視圖的大小(高、寬)
  2. 布局:遞歸(深度優先)確定所有視圖的位置(左上角坐標)
  3. 繪制:在畫布 canvas 上繪制應用程序窗口所有的視圖
  4. 生成 Display List 數據

測量、布局負責確定每個視圖組件的大小和位置信息,接下來就是繪制了。

Android 有兩種繪制模型:基于軟件的繪制模型和硬件加速的繪制模型?,F在默認都是使用硬件加速.

Android 需要把 XML 布局文件轉換成 GPU 能夠識別并繪制的對象。這個操作是在 DisplayList 的幫助下完成的。Display List 持有所有將要交給 GPU 繪制到屏幕上的數據信息。

關于 Display List 相關內容

  • Android 需要把 XML 布局文件轉換成 GPU 能夠識別并繪制的對象。這個操作是在 DisplayList 的幫助下完成的。DisplayList 持有所有將要交給 GPU 繪制到屏幕上的數據信息。

  • Display List 是一個緩存繪制命令的 Buffer,Display List 的本質是一個緩沖區,它里面記錄了即將要執行的繪制命令序列。

  • Display List 是視圖的基本繪制元素,包含元素原始屬性(位置、尺寸、角度、透明度等),對應 Canvas 的 drawXxx()方法。

  • 渲染 Display List,發生在應用程序進程的 Render Thread 中。增加 Render Thread 線程,也是為了避免 UI 線程任務過重,用于提高渲染性能。

  • Display List 是以視圖為單位進行構建的,因此每一個視圖都對應有一個 Display List。

  • 在某個 View 第一次需要被渲染時,Display List 會因此被創建,當這個 View 要顯示到屏幕上時,我們會執行 GPU 的繪制指令來進行渲染。

  • 在繪制窗口的下一幀時,若某一個視圖的 UI 沒有發生變化,那么就不必執行與它相關的 Canvas API,即不用執行它的成員函數 onDraw,而是直接復用上次構建的 Display List 即可。

  • 在繪制窗口的下一幀時,若某一個視圖的 UI 發生了變化,但是只是一些簡單屬性發生了變化,例如位置和透明度等簡單屬性,那么也不必重建它的 Display List,而是直接修改上次構建的 Display List 的相關屬性即可,這樣也可以省去執行它的成員函數 onDraw。

  • 硬件加速條件下,CPU 用于控制復雜繪制邏輯、構建或更新 DisplayList;GPU 用于完成圖形計算、渲染 DisplayList。

系統側渲染

1. Display List 數據交由 GPU 進行渲染處理

Android 需要把 XML 布局文件轉換成 GPU 能夠識別并繪制的對象。這個操作是在 DisplayList 的幫助下完成的。Display List 持有所有將要交給 GPU 繪制到屏幕上的數據信息。

Display List 是一個緩存繪制命令的 Buffer,Display List 的本質是一個緩沖區,它里面記錄了即將要執行的繪制命令序列。渲染 Display List,發生在應用程序進程的 Render Thread 中。增加 Render Thread 線程,也是為了避免 UI 線程任務過重,用于提高渲染性能。

這些繪制命令最終會轉化為 Open GL 命令由 GPU 執行。這意味著我們在調用 Canvas API 繪制 UI 時,實際上只是將 Canvas API 調用及其參數記錄在 Display List 中,然后等到下一個 VSYNC 信號到來時,記錄在 Display List 里面的繪制命令才會轉化為 Open GL 命令由 GPU 執行。

2. GPU 渲染處理

Android 使用 OpenGL ES (GLES) API 渲染圖形。GPU 將視圖柵格化后,生成 Surface。GPU 作為圖像流生產方將顯示內容,最終發送給圖像流的消耗方 SurfaceFlinger。

大多數客戶端使用 OpenGL ES 或 Vulkan 渲染到 Surface 上(硬件加速,使用了 GPU 渲染)。但是,有些客戶端使用畫布渲染到 Surface 上(未使用硬件加速)

3. 生成 Surface 并存儲到 BufferQueue

Surface 對象使應用能夠渲染要在屏幕上顯示的圖像。通過 SurfaceHolder 接口,應用可以編輯和控制 Surface。Surface 是一個接口,供生產方與使用方交換緩沖區。

BufferQueue 是 SurfaceFlinger 使用的緩沖區隊列,而 Surface 是 BufferQueue 的生產方。BufferQueue 類將可生成圖形數據緩沖區的組件(生產方)連接到接受數據以便進行顯示或進一步處理的組件(使用方,例如 SurfaceFlinger)。幾乎所有在系統中移動圖形數據緩沖區的內容都依賴于 BufferQueue。

用于顯示 Surface 的 BufferQueue 通常配置為三重緩沖。緩沖區是按需分配的,因此,如果生產方足夠緩慢地生成緩沖區(例如在 60 fps 的顯示屏上以 30 fps 的速度進行緩沖),隊列中可能只有兩個分配的緩沖區。按需分配緩沖區有助于最大限度地減少內存消耗。


圖片二

上圖描述了圖形管道的流程。左側為圖形生產者,右側為圖形消費者,中間通過 BufferQueues 連接。圖中,主屏幕、狀態欄和系統界面通過 GPU 渲染生成圖形緩沖區,做為生產者傳遞給 BufferQueues。SurfaceFlinger 做為消費者,接收到 BufferQueues 的通知后,取出可用的圖形緩沖區,送給顯示端。例圖中將狀態欄和系統界面的圖形緩沖送給 GPU 合成,生成新的圖形緩沖后再通過 BufferQueues 發送到硬件混合渲染器。

4. SurfaceFlinger 將顯示數據發送給顯示屏

SurfaceFlinger 接受來自多個源的數據緩沖區,然后將它們進行合成并發送到顯示屏。WindowManager 為 SurfaceFlinger 提供緩沖區和窗口元數據,而 SurfaceFlinger 可使用這些信息將 Surface 合成到屏幕。

SurfaceFlinger 可通過兩種方式接受緩沖區:通過 BufferQueue 和 SurfaceControl,或通過 ASurfaceControl。上文中,我們已經介紹了 BufferQueue。ASurfaceControl 是 Android 10 新增的,這是 SurfaceFlinger 接受緩沖區的另一種方式。ASurfaceControl 將 Surface 和 SurfaceControl 組合到一個事務包中,該包會被發送至 SurfaceFlinger。ASurfaceControl 與層相關聯,應用可通過 ASurfaceTransactions 更新該層。然后,應用可通過回調(用于傳遞包含鎖定時間、獲取時間等信息的 ASurfaceTransactionStats)獲取有關 ASurfaceTransactions 的信息。

總結

  1. 幀率是以幀為單位的位圖圖像每秒連續出現在顯示器上的次數(速率)。簡單來說就是一秒鐘,屏幕顯示多少張畫面。

  2. Android 在設計的時候,把幀頻限定在了每秒 60 幀,當我們的 APP 的幀頻 60fps 時,畫面就會非常的流暢。

  3. 界面刷新渲染依賴底層的 VSYNC 信號,VSYNC 信號以每秒 60 次的頻率發送給上層,并且高于 60fps 的幀頻也是沒有必要的,因為人眼與大腦之間的協作無法感知超過 60fps 的畫面更新。

  4. 大多數用戶感知到的卡頓等性能問題的最主要根源都是因為渲染性能,也就是 16ms 內沒有辦法把這一幀的任務執行完畢,發生了丟幀的現象。

  5. 由于 CPU 和 GPU 的設計不同,CPU 更擅長復雜邏輯控制,而 GPU 得益于大量 ALU 和并行結構設計,更擅長數學運算。在 Android 系統中,CPU 與 GPU 的分工不同,CPU 主要負責包括 Measure,Layout,Record,Execute 的計算操作,GPU 主要負責 Rasterization(柵格化)操作。

  6. Android 為了提高視圖渲染的性能,在 Android 3.0 中引入了硬件加速。

  7. 在 Android 應用程序中,每一個 Activity 組件都關聯有一個或者若干個窗口,每一個窗口都對應有一個 Surface。有了這個 Surface 之后,應用程序就可以在上面渲染窗口的 UI。最終這些已經繪制好了的 Surface 都會被統一提交給 Surface 管理服務 SurfaceFlinger 進行合成,最后顯示在屏幕上面。無論是應用程序,還是 SurfaceFlinger,都可以利用 GPU 等硬件來進行 UI 渲染,以便獲得更流暢的 UI。

  8. 一句話來概括一下 Android 應用程序顯示的過程:Android 應用程序調用 SurfaceFlinger 服務把經過測量、布局和繪制后的 Surface 渲染到顯示屏幕上。

  9. 圖形生產者創建一個 Surface,將圖形數據畫到 Surface 上。Surface 使用的圖形緩沖是通過 Gralloc 來分配的。 圖形生產者的窗口信息由 WindowManager 管理,WindowManager 將窗口元數據發送給 SurfaceFlinger 進行合成。 圖形消費者處理圖形生產者產生的數據。當圖形數據用于屏幕顯示時,SurfaceFlinger 使用窗口元數據將圖形緩沖合成到顯示上。圖形數據最終渲染到顯示設備上是通過 Hardware Composer 完成的。

  10. Android 需要把 XML 布局文件轉換成 GPU 能夠識別并繪制的對象。這個操作是在 DisplayList 的幫助下完成的。Display List 持有所有將要交給 GPU 繪制到屏幕上的數據信息。

  11. Display List 是一個緩存繪制命令的 Buffer,Display List 的本質是一個緩沖區,它里面記錄了即將要執行的繪制命令序列。渲染 Display List,發生在應用程序進程的 Render Thread 中。增加 Render Thread 線程,也是為了避免 UI 線程任務過重,用于提高渲染性能。

  12. Android 使用 OpenGL ES (GLES) API 渲染圖形。GPU 將視圖柵格化后,生成 Surface。GPU 作為圖像流生產方將顯示內容,最終發送給圖像流的消耗方 SurfaceFlinger。

  13. BufferQueue 是 SurfaceFlinger 使用的緩沖區隊列,而 Surface 是 BufferQueue 的生產方。BufferQueue 類將可生成圖形數據緩沖區的組件(生產方)連接到接受數據以便進行顯示或進一步處理的組件(使用方,例如 SurfaceFlinger)。

  14. SurfaceFlinger 接受來自多個源的數據緩沖區,然后將它們進行合成并發送到顯示屏。WindowManager 為 SurfaceFlinger 提供緩沖區和窗口元數據,而 SurfaceFlinger 可使用這些信息將 Surface 合成到屏幕。

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

推薦閱讀更多精彩內容