同步框架明確描述了Android圖形系統中不同非同步操作之間的依賴關係。該框架提供了一個 API,使元件能夠指示緩衝區何時被釋放。該框架還允許在驅動程式之間從核心到用戶空間以及用戶空間進程本身之間傳遞同步原語。
例如,應用程式可以對要在 GPU 中執行的工作進行排隊。 GPU 開始繪製該影像。儘管影像尚未繪製到記憶體中,但緩衝區指標會連同指示 GPU 工作何時完成的柵欄一起傳遞到視窗合成器。視窗合成器提前開始處理並將工作傳遞給顯示控制器。以類似的方式,CPU 工作提前完成。 GPU 完成後,顯示控制器立即顯示影像。
同步框架還允許實施者利用自己的硬體元件中的同步資源。最後,該框架提供了圖形管道的可見性以幫助調試。
明確同步
明確同步使圖形緩衝區的生產者和消費者能夠在使用完緩衝區時發出訊號。顯式同步是在核心空間中實現的。
顯式同步的好處包括:
- 設備之間的行為差異較小
- 更好的調試支持
- 改進的測試指標
同步框架有三種物件類型:
-
sync_timeline
-
sync_pt
-
sync_fence
同步時間軸
sync_timeline
是一個單調遞增的時間線,供應商應為每個驅動程式實例(例如 GL 上下文、顯示控制器或 2D 位元區塊傳輸)實現它。 sync_timeline
對特定硬體提交給核心的作業進行計數。 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_timeline
上的單一值或點。點有三種狀態:活動、已發出訊號和錯誤。點從活動狀態開始並轉換到有訊號或錯誤狀態。例如,當影像消費者不再需要緩衝區時,會發出sync_pt
訊號,以便影像生產者知道可以再次寫入緩衝區。
同步柵欄
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
中的會員資格是不可變的。為了在圍欄中獲得多個點,需要進行合併,將來自兩個不同圍欄的點添加到第三個圍欄中。如果其中一個點在原始柵欄中發出訊號,而另一個點沒有發出訊號,則第三個柵欄也不會處於有訊號狀態。
若要達到明確同步,請提供以下內容:
- 為特定硬體驅動程式實現同步框架的核心空間子系統。需要圍欄感知的驅動程式通常是任何存取 Hardware Composer 或與 Hardware Composer 通訊的驅動程式。關鍵文件包括:
- 核心實現:
-
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 fence 檔案描述符的方法。 -
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 fence 檔案描述符。
使用DupNativeFenceFD()
函數呼叫從本機 Android fence 檔案描述符中提取EGLSyncKHR
物件。這與查詢設定屬性具有相同的結果,但遵循接收者關閉柵欄的約定(因此是重複操作)。最後,銷毀EGLSyncKHR
物件會關閉內部柵欄屬性。
硬體作曲家集成
Hardware Composer 處理三種類型的同步柵欄:
- 取得柵欄與輸入緩衝區一起傳遞給
setLayerBuffer
和setClientTarget
呼叫。這些表示對緩衝區的待處理寫入,並且必須在 SurfaceFlinger 或 HWC 嘗試從關聯緩衝區讀取資料以執行組合之前發出訊號。 - 使用
getReleaseFences
呼叫呼叫presentDisplay
後會檢索釋放柵欄。這些表示來自同一層上的前一個緩衝區的待處理讀取。當 HWC 不再使用先前的緩衝區時,釋放柵欄會發出訊號,因為目前緩衝區已取代了顯示器上的先前緩衝區。釋放柵欄與將在目前合成期間替換的先前緩衝區一起傳遞回應用程式。應用程式必須等到釋放柵欄發出訊號,然後才能將新內容寫入返回給它們的緩衝區。 - 作為呼叫
presentDisplay
的一部分,每個畫面傳回一個目前柵欄。目前柵欄表示該幀的合成何時完成,或替代地,何時不再需要前一幀的合成結果。對於實體顯示器,噹噹前幀出現在螢幕上時,presentDisplay
會傳回目前柵欄。返回目前柵欄後,可以安全地再次寫入 SurfaceFlinger 目標緩衝區(如果適用)。對於虛擬顯示器,當可以安全地從輸出緩衝區讀取時,將返回目前的柵欄。