同步框架明確描述了Android圖形系統中不同異步操作之間的依賴關係。該框架提供了一個API,使組件可以指示何時釋放緩衝區。該框架還允許將同步原語在驅動程序之間從內核傳遞到用戶空間,以及在用戶空間進程本身之間傳遞。
例如,應用程序可以將要在GPU中執行的工作排隊。 GPU開始繪製該圖像。儘管尚未將圖像繪製到內存中,但緩衝區指針與圍欄一起傳遞到窗口合成器,該圍欄指示GPU工作何時完成。窗口合成器會提前開始處理,並將工作傳遞給顯示控制器。以類似的方式,CPU工作提前完成。 GPU完成後,顯示控制器將立即顯示圖像。
同步框架還允許實現者利用其自己的硬件組件中的同步資源。最後,該框架提供了對圖形管道的可見性,以幫助調試。
顯式同步
顯式同步使圖形緩衝區的生產者和使用者可以在使用完緩衝區後發出信號。顯式同步是在內核空間中實現的。
顯式同步的好處包括:
- 設備之間的行為差異較小
- 更好的調試支持
- 改進的測試指標
同步框架具有三種對像類型:
-
sync_timeline
-
sync_pt
-
sync_fence
sync_timeline
sync_timeline
是供應商應為每個驅動程序實例(例如GL上下文,顯示控制器或2D阻擊器)實施的單調遞增的時間線。 sync_timeline
對提交給特定硬件的內核的作業進行計數。 sync_timeline
提供有關操作順序的保證,並啟用特定於硬件的實現。
實施sync_timeline
時,請遵循以下準則:
- 為所有驅動程序,時間線和圍欄提供有用的名稱,以簡化調試。
- 在
timeline_value_str
實現timeline_value_str
和pt_value_str
運算符,以使調試輸出更具可讀性。 - 如果需要,實現fill
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_pt
值通常具有不同的sync_timeline
父級(例如用於顯示控制器和GPU的父級)。 sync_fence
, sync_pt
和sync_timeline
是驅動程序和用戶空間用來傳達其依賴性的主要原語。當發出隔離信號時,由於內核驅動程序或硬件模塊按順序執行命令,因此可以保證在隔離之前發出的所有命令都是完整的。
同步框架允許多個使用者或生產者使用緩衝區發信號通知何時完成,並通過一個函數參數傳達相關性信息。防護由文件描述符支持,並從內核空間傳遞到用戶空間。例如,圍柵可以包含兩個sync_pt
值, sync_pt
值表示兩個單獨的圖像使用者完成讀取緩衝區的時間。當柵欄發出信號時,圖像製作者就會知道兩個消費者都已完成消費。
柵欄(如sync_pt
值)開始活動並根據其點的狀態更改狀態。如果所有sync_pt
值sync_pt
發出信號,則sync_fence
會發出信號。如果一個sync_pt
進入錯誤狀態,則整個sync_fence
都處於錯誤狀態。
創建籬笆後, sync_fence
成員身份不變。為了在圍欄中獲得多個點,需要進行合併,其中將來自兩個不同圍欄的點添加到第三個圍欄中。如果這些點中的一個在發源柵欄中發出信號,而另一點沒有發信號,則第三個柵欄也不會處於發信號狀態。
要實現顯式同步,請提供以下內容:
- 一個內核空間子系統,為特定的硬件驅動程序實現同步框架。通常,需要防護的驅動程序是任何可與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
具有fence參數。
OpenGL ES集成
OpenGL ES同步集成依賴於兩個EGL擴展:
-
EGL_ANDROID_native_fence_sync
提供了一種在EGL_ANDROID_native_fence_sync
對像中包裝或創建本機Android fence文件描述符的EGLSyncKHR
。 -
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
對象。其結果與查詢set屬性的結果相同,但遵循以下約定:接收者關閉籬笆(因此重複操作)。最後,銷毀EGLSyncKHR
對象將關閉內部圍欄屬性。
硬件組合器集成
硬件編輯器處理三種類型的同步防護:
- 獲取圍欄與輸入緩衝區一起傳遞給
setLayerBuffer
和setClientTarget
調用。這些表示對緩衝區的未決寫操作,並且必須在SurfaceFlinger或HWC嘗試從關聯的緩衝區讀取以執行合成之前發出信號。 - 在使用
getReleaseFences
調用presentDisplay
之後,可以獲取發布圍欄。這些代表從同一層上的先前緩衝區的未決讀取。當HWC不再使用先前的緩衝區時,釋放柵欄會發出信號,因為當前緩衝區已替換了顯示屏上的先前緩衝區。釋放防護和先前的緩衝區一起傳遞回應用程序,這些緩衝區將在當前合成期間被替換。該應用程序必須等到釋放屏障發出信號後,才能將新內容寫入返回給它們的緩衝區。 - 作為對
presentDisplay
的調用的一部分,返回當前的籬笆,每幀一個。當前的圍欄表示何時完成此框架的合成,或者替代地,何時不再需要前一幀的合成結果。對於物理顯示,噹噹前幀出現在屏幕上時,presentDisplay
返回當前的圍欄。返回當前的籬笆後,可以安全地再次寫入SurfaceFlinger目標緩衝區。對於虛擬顯示,當可以安全地從輸出緩衝區讀取時,將返回當前的籬笆。