同步處理架構

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

同步處理架構可讓多個消費者或生產者在使用緩衝區後傳送信號,藉此透過一個函式參數傳達依附元件資訊。Fences 受檔案描述元支援,且會從核心空間傳遞至使用者空間。舉例來說,柵欄可以包含兩個 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 時傳回Present 柵欄,每個影格一個。顯示圍欄代表這個影格的組合完成時間,或相反地,不再需要前一個影格的組合結果時。對於實體螢幕,presentDisplay 會在目前影格顯示在螢幕上時傳回現行柵欄。當現況柵欄傳回後,如果適用,您可以放心再次寫入 SurfaceFlinger 目標緩衝區。對於虛擬螢幕,當從輸出緩衝區讀取資料時,系統會傳回顯示邊界。