SurfaceFlinger 和 Hardware Composer
擁有圖形數據緩沖區是很精彩的,但是當你在你的設備屏幕上看到它們時生活甚至更美好。那就是 SurfaceFlinger 和 Hardware Composer HAL 做的事情。
SurfaceFlinger
SurfaceFlinger 的角色是接收來自于多個源的數據緩沖區,組合它們,并將它們發送給顯示設備。曾經一段時期這是通過軟將數據塊傳送到硬件 framebuffer (比如 /dev/graphics/fb0) 完成的,但那些日子已經過去許久了。
當一個應用來到前臺,WindowManager 服務向 SurfaceFlinger 請求一塊繪制 surface。SurfaceFlinger 創建一個 layer(其主要的組件是一個 BufferQueue),而
SurfaceFlinger 將作為其消費者。生產者端的 Binder 對象通過 WindowManager 被傳遞給應用,然后它可以開始直接向 SurfaceFlinger 發送幀。
注意:這一節使用 SurfaceFlinger 術語,WindowManager 使用術語 window 而不是 layer. . . 并使用 layer 表示其它一些東西。( 可以認為 SurfaceFlinger 應該被稱為LayerFlinger。)
大多數應用于任何時間在屏幕上具有三個 layers:屏幕頂部的狀態欄,底部或側面的導航欄,以及應用程序 UI。一些應用具有更多,一些更少(比如默認的 home 應用有一個單獨的壁紙 layer,而全屏游戲可能會隱藏狀態欄)。每個 layer 可以被獨立地更新。狀態欄和導航欄由一個系統進程渲染,而應用 layers 有應用渲染,兩者之間沒有協調。
設備顯示器以一定頻率刷新,在手機和平板上典型的是每秒鐘 60 幀。如果顯示的內容在刷新中間更新,則將看到花屏;因此只在周期中間更新內容很重要。當可以安全更新內容時,系統收到來自于顯示器的信號。出于歷史原因我們稱它為 VSYNC 信號。
刷新頻率可能會隨著時間而改變,比如依賴于當前的條件,一些設備的范圍在 58 到 62fps。對于 HDMI 連接的電視機,理論上可以下降到 24 或 48Hz 來匹配視頻。由于每個刷新周期我們只能更新屏幕一次,以 200 fps 提交緩沖區來顯示將是巨大的浪費,因為大多數幀將從不會被看到。
不是在應用提交緩沖區時采取行動,SurfaceFlinger 而是在顯示器為顯示一些新東西做好準備時才喚醒。
當 VSYNC 信號到達時,SurfaceFlinger 遍歷它的 layers 列表尋找新的緩沖區。如果它找到了一個新的,它獲取它;如果沒有,它繼續使用之前獲取的緩沖區。SurfaceFlinger 總是想要一些東西來顯示,因此它會掛在一個緩沖區上。如果沒有緩沖區已經提交給一個 layer,則該 layer 被忽略。
在 SurfaceFlinger 收集了所有可見的 layers 的緩沖區之后,它詢問 Hardware Composer 應該如何執行組合。
Hardware Composer
Hardware Composer HAL (HWC) 在 Android 3.0 中被引入,并已經穩定發展多年。它的主要目標是通過可用硬件確定組合緩沖區的最有效方式。作為 HAL,其實現是特定于設備的,且通常由顯示設備硬件 OEM 完成。
當考慮 覆蓋平面(overlay planes) 時,這種方法的價值很容易識別,其目的是在顯示硬件而不是 GPU 中將多個緩沖區組合在一起。比如,考慮一個典型的豎直方向的 Android 手機,其狀態欄在頂部,導航欄在底部,應用內容在其余的地方。每個 layers 的內容在單獨的緩沖區中。你可以使用下列方法中的一種處理組合:
-
將應用內容渲染到暫存緩沖區中,然后將狀態欄渲染在它的上面,導航欄位于其上,最后將暫存緩沖區傳遞給顯示硬件。
-
將所有三個緩沖區傳遞給顯示硬件,并告訴它從不同的緩沖區為不同的屏幕部分讀取數據。
后一種方法可以顯著提高效率。
顯示處理器功能差異很大。overlays 的數量,layers 是否可以旋轉或混合,以及位置和重疊上的限制,可能非常難以通過一個 API 來描述。HWC 試圖通過一系列的決定容納這些多樣性:
SurfaceFlinger 為 HWC 提供完整的 layers 的列表并詢問,“你想要如何處理它?”。
HWC 通過將每個 layer 標記為 overlay 或 GLES composition 來進行響應。
SurfaceFlinger 關心任何 GLES composition,并把輸出緩沖區傳給 HWC,讓 HWC 處理其余的事情。
由于硬件供應商可以定制或裁剪決定作出的代碼,因此可以從每個設備中獲得最佳性能。
當屏幕上的東西沒有改變時,overlay 平面可能比 GL composition 更低效。當 overlay 內容具有透明像素且覆蓋的 layers 被混合在一起時尤其如此。在這種情況下,HWC 可以選擇為一些或所有 layers 請求 GLES composition 并保留組合的緩沖區。如果 SurfaceFlinger 回來請求組合相同的緩沖區集合,HWC 可以繼續展示之前組合好的臨時緩沖區。這可以提升空閑的設備的電池續航能力。
運行 Android 4.4 及更新版本的設備典型地支持四個 overlay 平面。試圖組合比 overlays 更多的 layers 會導致系統為它們中的一些使用 GLES composition,這意味著一個應用使用的 layers 的數量可能對電源消耗和性能有著重大的影響。
虛擬顯示器
SurfaceFlinger 支持一個主顯示器(比如手機或平板內置的東西),一個外部顯示器(比如通過 HDMI 連接的電視機),以及一個或多個使組合后的輸出在系統中可用的虛擬顯示器。虛擬顯示器可用于記錄屏幕或通過網絡發送。
虛擬顯示器可以共享主顯示器相同的 layers 集合(layer 棧)或擁有它們自己的集合。虛擬顯示器沒有 VSYNC,因此主顯示器的 VSYNC 用于觸發所有顯示器的組合
(composition) 。
在老版本的 Android 中,虛擬顯示器總是通過 GLES 組合,而 Hardware Composer 只管理主顯示器的組合。在 Android 4.4 中,Hardware Composer 獲得了參與虛擬顯示器組合的能力。
如你期待的那樣,為一個虛擬顯示器產生的幀被寫入 BufferQueue。
案例研究:screenrecord
screenrecord 命令 允許你將屏幕上出現的所有東西記錄為磁盤上的 .mp4 文件。為了實現它,我們不得不從 SurfaceFlinger 接收組合之后的幀,將它們寫入視頻編碼器,然后將編碼的視頻數據寫入一個文件。視頻編解碼由一個單獨的進程 (mediaserver) 管理,因此我們不得不在系統中移動巨大的圖形緩沖區。使這件事情更具挑戰性的是,我們還要試圖以全解析度記錄 60fps 的視頻。使這件事情高效工作的關鍵是 BufferQueue。
MediaCodec 類允許一個應用以緩沖區中的原始字節提供數據,或通過一個 Surface。當 screenrecord 請求訪問一個視頻編碼器時,mediaserver 創建一個 BufferQueue,將它自己與消費者一端相連,然后將生產者端作為一個 Surface 傳回給 screenrecord。
screenrecord 命令然后請求 SurfaceFlinger 創建一個鏡像主顯示器的虛擬顯示器 (比如它具有所有相同的 layers),然后指示它將輸出發送給來自于 mediaserver 的 Surface。在這種情況下,SurfaceFlinger 是緩沖區的生產者而不是消費者。
配置完成之后,screenrecord 等待編碼的數據出現。隨著應用的繪制,它們的緩沖區進入 SurfaceFlinger,SurfaceFlinger 將它們組合為一個單獨的緩沖區并直接發送給 mediaserver 中的視頻編碼器。screenrecord 進程甚至從來不會看到完整的幀。在內部,mediaserver 有著它自己的移動緩沖區的方式,即通過句柄傳遞數據,以最小化開銷。
案例研究:模擬二次顯示
WindowManager 可以請求 SurfaceFlinger 創建一個可見的 layer,其中 SurfaceFlinger 作為 BufferQueue 的消費者。它還可以請求 SurfaceFlinger 創建一個虛擬顯示器,其中 SurfaceFlinger 作為 BufferQueue 的生產者。如果你將它們連接到一起會如何呢,配置一個虛擬顯示器顯示渲染到一個可見 layer 的東西?
你創建了一個閉環,其中組合后的屏幕出現在一個窗口中。然后窗口現在是組合后的輸出的一部分了,因此在下一次刷新窗口內組合后的圖像將也顯示窗口的內容(然后 海龜下面還是海龜一路下來)。為了看到這種行為,啟用設置中的 Developer options,選擇 Simulate secondary displays,并啟用一個窗口。為了加分,請使用 screenrecord 捕獲啟用顯示的行為,然后逐幀播放。
原文
總結
以上是生活随笔為你收集整理的SurfaceFlinger 和 Hardware Composer的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HiKey960 开发板 android
- 下一篇: EGLSurfaces 和 OpenGL