實作硬體 Composer HAL

SurfaceFlinger 提供的 Hardware Composer (HWC) HAL 複合層,可降低 OpenGL ES (GLES) 和 GPU 效能。

HWC 會將重疊和 2D blit 等物件抽象化,以便合成介面,並與專門的視窗合成硬體通訊,以便合成視窗。使用 HWC 建立複合視窗,而非讓 GPU 產生 SurfaceFlinger 複合視窗。大多數 GPU 並未針對合成進行最佳化,當 GPU 合成 SurfaceFlinger 的圖層時,應用程式就無法使用 GPU 進行轉譯。

HWC 實作項目應支援下列項目:

  • 至少四個疊加層:
    • 狀態列
    • 系統列
    • 應用程式
    • 桌布/背景
  • 圖層大於螢幕 (例如桌布)
  • 同時進行每個像素的預乘 Alpha 混合和每個平面的 Alpha 混合
  • 受保護影片播放的硬體路徑
  • RGBA 封裝順序、YUV 格式,以及傾斜、旋轉和步長屬性

如何實作 HWC:

  1. 實作非作業 HWC,並將所有合成工作傳送至 GLES。
  2. 實作演算法,以便逐步將合成作業委派給 HWC。例如,只將前三或四個途徑委派給 HWC 的重疊硬體。
  3. 最佳化 HWC。其中可能包括:
    • 選取可將 GPU 負載降到最低的途徑,並將其傳送至 HWC。
    • 正在偵測螢幕是否正在更新。如果不是,請將合成作業委派給 GLES 而非 HWC,以節省電力。當畫面再次更新時,請繼續將組合卸載至 HWC。
    • 準備常見用途,例如:
      • 主畫面,包括狀態列、系統列、應用程式視窗和動態桌布
      • 在直向和橫向模式下以全螢幕模式顯示遊戲
      • 以全螢幕播放影片,搭配隱藏式輔助字幕和播放控制項
      • 受保護的影片播放
      • 分割畫面多視窗

HWC 原始物件

HWC 提供兩種基本元素:圖層螢幕,用來代表合成作業及其與顯示硬體的互動情形。HWC 也提供 VSYNC 控制項和 SurfaceFlinger 回呼,以便在 VSYNC 事件發生時通知。

HIDL 介面

Android 8.0 以上版本會使用名為 Composer HAL 的 HIDL 介面,用於 HWC 和 SurfaceFlinger 之間的繫結 IPC。Composer HAL 會取代舊版 hwcomposer2.h 介面。如果廠商提供 HWC 的 Composer HAL 實作,Composer HAL 就能直接接受來自 SurfaceFlinger 的 HIDL 呼叫。如果供應商提供 HWC 的舊版實作項目,Composer HAL 會從 hwcomposer2.h 載入函式指標,並將 HIDL 呼叫轉送至函式指標呼叫。

HWC 提供函式,可用於判斷特定螢幕的屬性;切換不同的螢幕設定 (例如 4k 或 1080p 解析度) 和色彩模式 (例如原生色彩或真實 sRGB);以及開啟/關閉螢幕或進入省電模式 (如有支援)。

函式指標

如果廠商直接實作 Composer HAL,SurfaceFlinger 會透過 HIDL IPC 呼叫其函式。舉例來說,如要建立圖層,SurfaceFlinger 會在 Composer HAL 上呼叫 createLayer()

如果供應商實作 hwcomposer2.h 介面,Composer HAL 會呼叫 hwcomposer2.h 函式指標。在 hwcomposer2.h 註解中,HWC 介面函式會以 lowerCamelCase 名稱參照,這些名稱不會在介面中以已命名欄位的形式存在。使用 hwc2_device_t 提供的 getFunction 要求函式指標,即可載入幾乎每個函式。舉例來說,函式 createLayerHWC2_PFN_CREATE_LAYER 類型的函式指標,會在列舉值 HWC2_FUNCTION_CREATE_LAYER 傳遞至 getFunction 時傳回。

如需 Composer HAL 函式和 HWC 函式傳遞函式的詳細說明文件,請參閱 composer。如需 HWC 函式指標的詳細說明文件,請參閱 hwcomposer2.h

圖層和顯示處理程序

圖層和顯示方式是由 HWC 產生的控點操控。這些控制項對 SurfaceFlinger 來說是不可見的。

當 SurfaceFlinger 建立新圖層時,會呼叫 createLayer,並針對直接實作傳回 Layer 類型,或針對傳送方式實作傳回 hwc2_layer_t 類型。當 SurfaceFlinger 修改該層的屬性時,SurfaceFlinger 會將 hwc2_layer_t 值傳遞至適當的修改函式,並附上進行修改所需的任何其他資訊。hwc2_layer_t 類型足夠大,可容納指標或索引。

實體螢幕是透過熱插拔建立。當實體螢幕接上電源時,HWC 會建立控制代碼,並透過熱插電回呼將控點傳遞至 SurfaceFlinger。虛擬螢幕是由 SurfaceFlinger 呼叫 createVirtualDisplay() 來要求螢幕而建立。如果 HWC 支援虛擬顯示組合,則會傳回句柄。接著,SurfaceFlinger 會將螢幕的組合委派給 HWC。如果 HWC 不支援虛擬螢幕組合,SurfaceFlinger 會建立控制代碼並合成螢幕。

顯示組合作業

每個 VSYNC 一次,如果 SurfaceFlinger 有新內容要合成,就會喚醒。這類新內容可以是應用程式中的新圖片緩衝區,或是一或多個圖層屬性的變更。當 SurfaceFlinger 喚醒時:

  1. 處理交易 (如有)。
  2. 鎖定新的圖形緩衝區 (如果有的話)。
  3. 如果步驟 1 或 2 導致顯示內容變更,就會執行新的組合。

如要執行新的合成作業,SurfaceFlinger 會視情況建立及銷毀圖層或修改圖層狀態。它也會使用 setLayerBuffersetLayerColor 等呼叫,更新包含目前內容的圖層。所有圖層都更新後,SurfaceFlinger 會呼叫 validateDisplay,並指示 HWC 檢查圖層狀態,並決定合成作業的執行方式。根據預設,SurfaceFlinger 會嘗試設定每個圖層,以便由 HWC 合成圖層;但在某些情況下,SurfaceFlinger 會透過 GPU 備用方案合成圖層。

呼叫 validateDisplay 後,SurfaceFlinger 會呼叫 getChangedCompositionTypes,查看 HWC 是否希望在執行合成作業前變更任何圖層合成類型。如要接受變更,SurfaceFlinger 會呼叫 acceptDisplayChanges

如果任何圖層標示為 SurfaceFlinger 合成,SurfaceFlinger 就會將這些圖層合成至目標緩衝區。接著,SurfaceFlinger 會呼叫 setClientTarget 為螢幕提供緩衝區,以便讓緩衝區顯示在畫面上,或以尚未標記為 SurfaceFlinger 組合的圖層來合併。如果沒有標示要進行 SurfaceFlinger 合成的圖層,SurfaceFlinger 會略過合成步驟。

最後,SurfaceFlinger 會呼叫 presentDisplay,告知 HWC 完成組合程序並顯示最終結果。

多螢幕

Android 10 支援多個實體螢幕。設計用於 Android 7.0 以上版本的 HWC 實作時,HWC 定義中不會存在以下限制:

  • 系統會假設只有一個內部螢幕。內部顯示器是指在啟動期間,初始熱插拔裝置回報的顯示器。內部螢幕熱插後就無法中斷連線。
  • 除了內部螢幕之外,在裝置正常運作期間,任何數量的外部螢幕都可以熱插拔。架構會假設第一個內部螢幕之後的所有熱插拔都是外部螢幕,因此如果新增更多內部螢幕,這些螢幕會被錯誤分類為 Display.TYPE_HDMI,而非 Display.TYPE_BUILT_IN

雖然上述的 SurfaceFlinger 作業會針對每個螢幕執行,但即使只更新一個螢幕的內容,作業也會依序針對所有有效螢幕執行。

舉例來說,如果外部顯示器更新,序列就會是:

// In Android 9 and lower:

// Update state for internal display
// Update state for external display
validateDisplay(<internal display>)
validateDisplay(<external display>)
presentDisplay(<internal display>)
presentDisplay(<external display>)

// In Android 10 and higher:

// Update state for internal display
// Update state for external display
validateInternal(<internal display>)
presentInternal(<internal display>)
validateExternal(<external display>)
presentExternal(<external display>)

虛擬顯示器組合

虛擬螢幕組合與外部螢幕組合類似。虛擬顯示器組合和實體顯示器組合之間的差異在於,虛擬顯示器會將輸出內容傳送至 Gralloc 緩衝區,而非傳送至螢幕。硬體編譯器 (HWC) 會將輸出內容寫入緩衝區、提供完成柵欄,並將緩衝區傳送至消費者 (例如影片編碼器、GPU、CPU 等)。如果顯示管道寫入記憶體,虛擬螢幕可以使用 2D/blitter 或覆蓋。

模式

在 SurfaceFlinger 呼叫 validateDisplay() HWC 方法後,每個影格都會處於下列三種模式之一:

  • GLES:GPU 會將所有圖層合成,並直接寫入輸出緩衝區。HWC 不會參與組合。
  • MIXED:GPU 會將部分圖層合併至 framebuffer,HWC 則會合併 framebuffer 和其餘層,並直接寫入輸出緩衝區。
  • HWC:HWC 會合成所有圖層,並直接寫入輸出緩衝區。

輸出格式

虛擬顯示器緩衝區輸出格式取決於其模式:

  • GLES 模式:EGL 驅動程式會在 dequeueBuffer() 中設定輸出緩衝區格式,通常為 RGBA_8888。取用端必須能夠接受驅動程式集的輸出格式,否則無法讀取緩衝區。
  • 混合模式和 HWC 模式:如果消費者需要 CPU 存取權,則消費者會設定格式。否則格式為 IMPLEMENTATION_DEFINED,而 Gralloc 會根據用法標記設定最佳格式。舉例來說,如果使用者是影片編碼器,且 HWC 可有效地寫入格式,Gralloc 就會設定 YCbCr 格式。

同步處理圍欄

同步 (sync) 圍欄是 Android 圖形系統的重要部分。柵欄可讓 CPU 獨立於並行 GPU 工作進行,只有在存在真正的依附元件時才會阻斷。

舉例來說,當應用程式提交在 GPU 上產生的緩衝區時,也會提交同步化圍欄物件。當 GPU 完成寫入緩衝區時,這個圍欄就會發出信號。

HWC 需要 GPU 完成寫入緩衝區後才會顯示緩衝區。同步化圍籬會在寫入緩衝區時,透過繪圖管線傳遞緩衝區和信號。在顯示緩衝區之前,HWC 會檢查同步區是否已發出信號,如果已發出信號,就會顯示緩衝區。

如要進一步瞭解同步化圍欄,請參閱「硬體音樂製作人整合」。