同步架構會明確說明 Android 圖像系統中不同非同步作業之間的依附元件。架構提供 API,讓元件指出緩衝區釋出時間。這個架構也允許在驅動程式之間,以及核心與使用者空間之間,傳遞同步基本類型。
舉例來說,應用程式可能會將要在 GPU 中執行的工作加入佇列。GPU 會開始繪製該圖片。雖然圖片尚未繪製到記憶體中,但緩衝區指標會連同柵欄傳遞至視窗合成器,指出 GPU 工作何時完成。視窗合成器會提前開始處理,並將工作傳遞給螢幕控制器。同樣地,CPU 工作會提前完成。GPU 完成後,顯示控制器會立即顯示圖片。
實作者也能透過同步架構,在自己的硬體元件中運用同步資源。最後,這個架構會提供圖形管道的能見度,協助進行偵錯。
明確同步
明確同步處理可讓圖像緩衝區的生產者和消費者,在完成緩衝區使用時發出信號。明確同步處理是在核心空間中實作。
明確同步處理的優點包括:
- 減少裝置間的行為差異
- 更完善的偵錯支援
- 改善測試指標
同步架構有三種物件類型:
sync_timeline
sync_pt
sync_fence
sync_timeline
sync_timeline
是單調遞增的時間軸,供應商應為每個驅動程式執行個體 (例如 GL 環境、螢幕控制器或 2D 繪圖器) 實作這個時間軸。sync_timeline
counts
提交至特定硬體核心的工作數量。
sync_timeline
可確保作業順序,並啟用硬體專屬的實作方式。
實作 sync_timeline
時,請遵循下列準則:
- 為所有驅動程式、時間軸和柵欄提供實用名稱,簡化偵錯程序。
- 在時間軸中實作
timeline_value_str
和pt_value_str
運算子,讓偵錯輸出內容更容易閱讀。 - 視需要實作填滿
driver_data
,讓使用者空間程式庫 (例如 GL 程式庫) 存取私人時間軸資料。data_driver
可讓供應商傳遞有關不可變動sync_fence
和sync_pts
的資訊,並根據這些資訊建構指令列。 - 不允許使用者空間明確建立或發出柵欄信號。明確建立信號/柵欄會導致阻斷服務攻擊,進而停止管道功能。
- 請勿明確存取
sync_timeline
、sync_pt
或sync_fence
元素。API 提供所有必要功能。
sync_pt
sync_pt
是 sync_timeline
上的單一值或點。點有三種狀態:有效、已發出信號和錯誤。點會先處於有效狀態,然後轉換為已發出信號或錯誤狀態。舉例來說,當圖片消費者不再需要緩衝區時,系統會發出 sync_pt
訊號,讓圖片生產者知道可以再次寫入緩衝區。
sync_fence
sync_fence
是 sync_pt
值的集合,通常具有不同的 sync_timeline
父項 (例如顯示控制器和 GPU)。sync_fence
、sync_pt
和 sync_timeline
是驅動程式和使用者空間用來傳達依附元件的主要基本類型。柵欄發出信號時,保證柵欄前發出的所有指令都會完成,因為核心驅動程式或硬體區塊會依序執行指令。
同步架構可讓多個消費者或生產者在完成緩衝區使用時發出信號,並透過一個函式參數傳達依附元件資訊。柵欄由檔案描述元支援,並從核心空間傳遞至使用者空間。舉例來說,柵欄可以包含兩個 sync_pt
值,表示兩個不同的圖片消費者何時完成讀取緩衝區。收到柵欄信號時,圖片產生器會知道兩個取用者都已完成取用。
圍欄與 sync_pt
值一樣,一開始會處於啟用狀態,並根據點的狀態變更狀態。如果所有 sync_pt
值都變成已發出信號,則 sync_fence
會變成已發出信號。如果其中一個 sync_pt
進入錯誤狀態,整個 sync_fence
就會處於錯誤狀態。
圍欄建立後,sync_fence
的成員資格就無法變更。如要在圍欄中取得多個點,請執行合併作業,將兩個不同圍欄中的點新增至第三個圍欄。如果其中一個點是在原始柵欄中發出信號,另一個點則否,第三個柵欄也不會處於發出信號的狀態。
如要實作明確同步處理,請提供下列項目:
- 實作特定硬體驅動程式同步架構的 Kernel 空間子系統。需要瞭解圍欄的驅動程式通常是存取或與硬體合成器通訊的任何項目。主要檔案包括:
- 核心導入:
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_sync
和EGL_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 可感知柵欄。dequeueBuffer
、queueBuffer
和 cancelBuffer
具有柵欄參數。
整合 OpenGL ES
OpenGL ES 同步整合功能依賴兩項 EGL 擴充功能:
EGL_ANDROID_native_fence_sync
提供在EGLSyncKHR
物件中包裝或建立原生 Android 柵欄檔案描述元的機制。EGL_ANDROID_wait_sync
允許 GPU 端停止,而非 CPU 端停止,讓 GPU 等待EGLSyncKHR
。EGL_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
擴充功能會採用對應的原生柵欄檔案描述元屬性,該屬性只能在建立時設定,且無法從現有同步物件直接查詢。這項屬性可設為下列兩種模式之一:
- 有效的柵欄檔案描述元會將現有的原生 Android 柵欄檔案描述元包裝在
EGLSyncKHR
物件中。 - -1 會從
EGLSyncKHR
物件建立原生 Android 柵欄檔案描述元。
使用 DupNativeFenceFD()
函式呼叫,從原生 Android 柵欄檔案描述元擷取 EGLSyncKHR
物件。這與查詢設定的屬性結果相同,但遵守接收者關閉柵欄的慣例 (因此作業重複)。最後,銷毀 EGLSyncKHR
物件會關閉內部柵欄屬性。
整合硬體 Composer
Hardware Composer 會處理三種同步柵欄:
- 取得柵欄會與輸入緩衝區一起傳遞至
setLayerBuffer
和setClientTarget
呼叫。這些代表緩衝區中待處理的寫入作業,且必須在 SurfaceFlinger 或 HWC 嘗試從相關聯的緩衝區讀取資料以執行組合作業前發出信號。 - 呼叫
presentDisplay
後,會使用getReleaseFences
呼叫擷取釋放柵欄。這代表同一層的先前緩衝區有待處理的讀取作業。當 HWC 不再使用先前的緩衝區,因為目前的緩衝區已取代顯示器上的先前緩衝區時,發布柵欄會發出信號。發布柵欄會連同先前緩衝區傳回應用程式,這些緩衝區會在目前的組合期間替換。應用程式必須等到發布柵欄信號,才能將新內容寫入傳回的緩衝區。 - 系統會傳回「顯示圍欄」,每個影格一個,做為
presentDisplay
呼叫的一部分。顯示圍欄代表這個影格的組合完成時間,或代表不再需要前一個影格的組合結果。如果是實體螢幕,當目前影格出現在畫面上時,presentDisplay
會傳回目前的柵欄。傳回目前的柵欄後,即可再次寫入 SurfaceFlinger 目標緩衝區 (如適用)。如果是虛擬螢幕,當可安全地從輸出緩衝區讀取資料時,系統會傳回呈現圍欄。