同步處理架構

同步處理架構會明確說明 Android 圖形系統中不同非同步作業之間的依附元件。此架構提供的 API 可讓元件指出緩衝區何時釋出。此外,此架構還可讓同步處理原始碼在驅動程式之間 (從核心到使用者空間) 以及使用者空間程序本身之間傳遞。

舉例來說,應用程式可能會將要執行的作業排入 GPU 佇列。GPU 會開始繪製該圖片。雖然圖片尚未繪製到記憶體中,但緩衝區指標會連同表示 GPU 工作何時完成的柵欄傳遞至視窗合成器。視窗合成器會提前開始處理作業,並將工作傳遞至顯示控制器。同樣地,CPU 工作會提前完成。GPU 完成後,顯示控制器會立即顯示圖片。

同步處理架構也讓實作者能夠在自己的硬體元件中運用同步處理資源。最後,此架構會提供圖形處理管道的可視性,以利偵錯。

明確同步

明確的同步處理可讓圖形緩衝區的生產者和消費者在使用緩衝區時發出信號。明確的同步處理作業是在核心空間中實作。

明確同步的優點包括:

  • 裝置之間的行為差異較小
  • 提供更完善的偵錯支援
  • 改善測試指標

同步處理架構有三種物件類型:

  • sync_timeline
  • sync_pt
  • sync_fence

sync_timeline

sync_timeline 是單調遞增的時間軸,供應商應為每個驅動程式例項 (例如 GL 內容、顯示控制器或 2D blit 器) 實作此時間軸。sync_timeline 會為特定硬體設備計算提交至核心的工作。sync_timeline 可保證作業順序,並啟用硬體專屬的實作項目。

實作 sync_timeline 時,請遵循下列準則:

  • 為所有驅動程式、時間表和柵欄提供實用的名稱,以簡化偵錯作業。
  • 在時間軸中實作 timeline_value_strpt_value_str 運算子,讓偵錯輸出內容更易讀。
  • 實作填充 driver_data,讓使用者空間程式庫 (例如 GL 程式庫) 存取私人時間軸資料 (如有需要)。data_driver 可讓供應商傳遞有關不可變更的 sync_fencesync_pts 的資訊,以便根據這些資訊建立指令列。
  • 請勿允許使用者空間明確建立或發出邊界信號。明確建立信號/柵欄會導致阻斷服務攻擊,導致管道功能停止運作。
  • 請勿明確存取 sync_timelinesync_ptsync_fence 元素。API 提供所有必要的功能。

sync_pt

sync_ptsync_timeline 上的單一值或點。點有三種狀態:有效、已發出信號和錯誤。點數會從有效狀態開始,然後轉換為信號狀態或錯誤狀態。舉例來說,當圖片使用者不再需要緩衝區時,系統會發出 sync_pt 信號,讓圖片產生端知道可以再次寫入緩衝區。

sync_fence

sync_fencesync_pt 值的集合,通常具有不同的 sync_timeline 父項 (例如顯示控制器和 GPU)。sync_fencesync_ptsync_timeline 是驅動程式和使用者空間用來傳達其依附元件的必要元素。當邊界發出信號時,所有在邊界之前發出的指令都會保證完成,因為核心驅動程式或硬體區塊會依序執行指令。

同步處理架構可讓多個消費者或生產者在使用緩衝區後傳送信號,藉此透過一個函式參數傳達依附元件資訊。柵欄由檔案描述元備份,並從核心空間傳遞至使用者空間。舉例來說,柵欄可以包含兩個 sync_pt 值,用來表示兩個個別圖片消費者讀取緩衝區的時間。當系統發出柵欄信號時,圖片製作者就會知道兩個使用者都已完成取用。

柵欄與 sync_pt 值一樣,會在啟用時根據其點的狀態變更狀態。如果所有 sync_pt 值都收到信號,sync_fence 就會收到信號。如果一個 sync_pt 處於錯誤狀態,整個 sync_fence 就會處於錯誤狀態。

建立柵欄後,sync_fence 的成員資格即無法變更。如要在圍欄中取得多個點,系統會進行合併作業,將兩個不同圍欄中的點新增至第三個圍欄。如果其中一個點在原始邊界中發出訊號,而另一個點沒有發出訊號,第三個邊界也不會處於已發出訊號的狀態。

如要實作明確的同步處理,請提供下列資訊:

  • 核心空間子系統,可為特定硬體驅動程式實作同步處理架構。需要支援邊界功能的驅動程式,通常是指任何會存取或與硬體編譯器通訊的驅動程式。重要檔案包括:
    • 核心實作:
      • kernel/common/include/linux/sync.h
      • kernel/common/drivers/base/sync.c
    • kernel/common/Documentation/sync.txt 中的說明文件
    • 可與 platform/system/core/libsync 中的核心空間通訊的程式庫
  • 供應商必須提供適當的同步化圍欄,做為 HAL 中 validateDisplay()presentDisplay() 函式的參數。
  • 兩個圍欄相關的 GL 擴充功能 (EGL_ANDROID_native_fence_syncEGL_ANDROID_wait_sync),以及圖形驅動程式中的圍欄支援功能。

個案研究:實作顯示驅動程式

如要使用支援同步處理函式的 API,請開發具有顯示緩衝區函式的顯示驅動程式。在同步處理架構出現之前,這個函式會接收 dma-buf 物件,將這些緩衝區放在螢幕上,並在緩衝區可見時阻斷。例如:

/*
 * assumes buffer is ready to be displayed.  returns when buffer is no longer on
 * screen.
 */
void display_buffer(struct dma_buf *buffer);

使用同步化架構時,display_buffer 函式會變得更複雜。將緩衝區顯示在螢幕上時,緩衝區會與柵欄建立關聯,用於指出緩衝區何時就緒。您可以在柵欄清除後排入佇列並啟動工作。

在邊界清除後排入佇列並啟動工作不會阻斷任何內容。您會立即傳回自己的邊界,確保緩衝區何時會離開螢幕。當您將緩衝區排入佇列時,核心會列出同步處理架構的依附元件:

/*
 * displays buffer when fence is signaled.  returns immediately with a fence
 * that signals when buffer is no longer displayed.
 */
struct sync_fence* display_buffer(struct dma_buf *buffer, struct sync_fence
*fence);

同步處理整合

本節說明如何將核心空間同步架構與 Android 架構的使用者空間部分和必須相互通訊的驅動程式整合。在使用者空間中,核心空間物件會以檔案描述項表示。

整合慣例

遵循 Android HAL 介面慣例:

  • 如果 API 提供參照 sync_pt 的檔案描述符,使用 API 的供應商驅動程式或 HAL 就必須關閉檔案描述符。
  • 如果供應商驅動程式或 HAL 將包含 sync_pt 的檔案描述元傳遞至 API 函式,則供應商驅動程式或 HAL 不得關閉檔案描述元。
  • 如要繼續使用柵欄檔案描述元,供應商驅動程式或 HAL 必須複製描述元。

每當柵欄物件通過 BufferQueue 時,就會重新命名。核心柵欄支援可讓柵欄使用字串做為名稱,因此同步化架構會使用排入佇列的視窗名稱和緩衝區索引來命名柵欄,例如 SurfaceView:0。這有助於在偵錯時找出死結的來源,因為名稱會顯示在 /d/sync 和錯誤報告的輸出內容中。

ANativeWindow 整合

ANativeWindow 會感知邊界。dequeueBufferqueueBuffercancelBuffer 有圍欄參數。

OpenGL ES 整合

OpenGL ES 同步整合功能需要使用兩個 EGL 擴充功能:

  • EGL_ANDROID_native_fence_sync 提供一種方法,可在 EGLSyncKHR 物件中包裝或建立原生 Android 圍欄檔案描述項。
  • EGL_ANDROID_wait_sync 允許 GPU 端停滯,而非 CPU 端,讓 GPU 等待 EGLSyncKHREGL_ANDROID_wait_sync 副檔名與 EGL_KHR_wait_sync 副檔名相同。

如要獨立使用這些擴充功能,請實作 EGL_ANDROID_native_fence_sync 擴充功能,並提供相關的核心支援。接著,請在驅動程式中啟用 EGL_ANDROID_wait_sync 擴充功能。EGL_ANDROID_native_fence_sync 擴充功能包含不同的原生圍欄 EGLSyncKHR 物件類型。因此,適用於現有 EGLSyncKHR 物件類型的擴充功能不一定適用於 EGL_ANDROID_native_fence 物件,可避免不必要的互動。

EGL_ANDROID_native_fence_sync 擴充功能採用對應的原生圍欄檔案描述元屬性,只能在建立時設定,且無法直接從現有的同步物件查詢。這個屬性可設為下列兩種模式之一:

  • 有效的邊界檔案描述元會在 EGLSyncKHR 物件中包裝現有的原生 Android 邊界檔案描述元。
  • -1 會從 EGLSyncKHR 物件建立原生 Android 圍欄檔案描述元。

使用 DupNativeFenceFD() 函式呼叫,從原生 Android 圍欄檔案描述元中擷取 EGLSyncKHR 物件。這與查詢 set 屬性所產生的結果相同,但會遵循收件者關閉圍欄的慣例 (因此是重複作業)。最後,銷毀 EGLSyncKHR 物件會關閉內部柵欄屬性。

硬體 Composer 整合

硬體 Composer 會處理三種類型的同步化圍欄:

  • Acquire 柵欄會連同輸入緩衝區傳遞至 setLayerBuffersetClientTarget 呼叫。這些代表緩衝區的待寫入資料,必須在 SurfaceFlinger 或 HWC 嘗試從相關聯的緩衝區讀取資料以執行合成作業之前,先發出信號。
  • 使用 getReleaseFences 呼叫對 presentDisplay 的呼叫後,系統會擷取釋放柵欄。這些代表在同一層級上,從先前緩衝區讀取的待處理內容。當 HWC 不再使用先前的緩衝區,因為目前的緩衝區已取代顯示器上的先前緩衝區,此時會發出釋放柵欄信號。釋放柵欄會連同先前的緩衝區傳回至應用程式,這些緩衝區會在目前的組合期間替換。應用程式必須等到釋放柵欄信號,才能將新內容寫入已傳回的緩衝區。
  • 系統會在呼叫 presentDisplay 時傳回呈現柵欄,每個影格一個。當畫面邊界出現時,表示此影格組合已完成,或者,當不再需要先前影格的組合結果時。對於實體螢幕,presentDisplay 會在目前影格顯示在螢幕上時傳回現行柵欄。當現況柵欄傳回後,如果適用,您可以安全地再次寫入 SurfaceFlinger 目標緩衝區。對於虛擬螢幕,當從輸出緩衝區讀取資料時,系統會傳回現況柵欄。