Graphics in AOSP === ###### tags: `Android` `note` `AOSP` `graphics` # Overview Graphics in AOSP 將討論 HAL 以上,有關於圖像的內容。 Android 開發者可透過 Canvas, OpenGL ES, or Vulkan 將圖像畫在螢幕上。 ## Android graphcis components 無論使用什麼渲染 API,所有東西都會被渲染到 **surface** 上,**surface** 表示生產端的 buffer queue。通常被渲染過後的 surface 都會被 SurfaceFlinger 蒐集、編排後,顯示到畫面上。 下圖表示 Graphics 的關鍵元件之間如何運作: ![](https://source.android.com/devices/graphics/images/ape_fwk_graphics.png) ### Image Stream Producers 負責產出 graphic buffers 的人,像是 OpenGL ES, Canvas 2D, mediaserver video decoders,或者是 camera HAL、OpenGL ES games。 ### Image Stream Consumers 最常見的 cosumer 是 **SurfaceFlinger**,SurfaceFlinger 會把 Window Mangager 提供的資訊結合 surface 顯示在畫面上。SurfaceFlinger 是唯一個能夠修改顯示內容的服務。SurfaceFlinger 使用 OpenGL 和 Hardware Composer 來將一群 surface 組織起來。 另有其他消費者,像是 OpenGL ES app 的 camera app,或是 Non-GL 的 ImageReader class。 ### Hardware Composer 分擔來自 SurfaceFliner 的組成工作,間接降低 OpenGL 和 GPU 的負載。 Hardware Composer HAL 是 Android graphics rendering 的核心。Hardware Composer 必須實作事件觸發,像是 VSYNC, hotplug for plug-and-playHDMI。 ### Gralloc 當 image producer 有要求時,graphics memory allocator 就會分配一塊記憶體給要求者。 ## Data flow ![](https://source.android.com/devices/graphics/images/graphics_pipeline.png) 上圖左邊是產出 graphics buffers 的人,也就是 pruducer。SurfaceFlinger 負責一部分的排版,Hardware Composer 負責最後的統整。 ### BufferQueue BufferQueue 就像 Android graphics components 之間的接著劑,用來幫助傳遞 producer 和 consumer 之間的資料。 ![](https://source.android.com/devices/graphics/images/bufferqueue.png) BufferQueue 是結合了 buffer pool 和 queue 的資料結構,透過 Binder IPC 機制,可以將 buffers 在不同 processes 之間傳遞。Producer 必須實作 IGraphicBufferProducer介面(SurfaceTexture 的其中一部份),才能傳遞資料給 BufferQueue。BufferQueue 有以下三種運作模式: - *Synchronous-like mode*:預設模式。所有傳進來的 bufffer 都會傳給 consumer。如果 producer 產出 graphics buffers 的速度太快,則會等待 free buffer 出現時才會繼續寫入。 - *Non-blocking mode*:跟 Synchronous-like mode 一樣不會捨棄任何 buffer,但是當 graphics buffers 產出的速度太快時,則會產生 error 告知 buffer 不夠用了。 - *Discard mode*:當 buffer 不夠用時,會捨棄舊的資料,讓新的資料寫入。 # BufferQueue and Gralloc BufferQueue 扮演了 graphics buffers producer 和 consumer 之間接著劑的角色,幾乎所有透過系統搬動 graphical data 的 buffer 的工作都仰賴 BufferQueue。 可以在 [`hardware/interfaces/graphics/allocator/`](https://android.googlesource.com/platform/hardware/interfaces/+/refs/heads/master/graphics/allocator/) 和 [`hardware/interfaces/graphics/mapper/`](https://android.googlesource.com/platform/hardware/interfaces/+/refs/heads/master/graphics/mapper/) 找到與 Gralloc 相關的 HIDL 檔,由 HIDL 檔可知主要是藉由 **`allocate()`** 這個函式來取得 buffer。 ## BufferQueue producers and consumers > ?? Consumers create and own the BufferQueue data structure and can exist in different processes than their producers. ![](https://i.imgur.com/yL7GyUd.png) - producers & BufferQueue 1. 當 producer 要跟 BufferQueue 要 buffer 時,會呼叫 **`dequeueBuffer()`** 並指定 buffer 的長、寬、像素格式、以及旗標。 2. producer 產生 buffer 之後會透過呼叫 **`queueBuffer()`** 來回傳 buffer。 - consumers & BufferQueue 1. consumer 呼叫 **`acquireBuffer()`** 來取得要處理的 buffer。 2. 處理完後呼叫 **`releaseBuffer()`** 來將 buffer 放回佇列。 - 一部分的 BufferQueue 參數,是由 producer 和 consumer 共同決定的,像是最大 buffer 數。 - BufferQueue 只有在參數變動時才會分配新的 buffer。 ### Tracking BufferQueue with Systrace Please Check [**Here**](https://source.android.com/devices/graphics/arch-bq-gralloc#tracking) ## Gralloc 與 buffer 分配有關的旗標可以在 [`hardware/libhardware/include/hardware/gralloc.h`](https://android.googlesource.com/platform/hardware/libhardware/+/refs/heads/master/include/hardware/gralloc.h) 找到,這些旗標包括像是: - 記憶體多久會被軟體(CPU)存取一次 - 記憶體多久會被硬體(GPU)存取一次 - 記憶體是否會被作為 OpenGL ES (GLES) 材質使用 - 記憶體是否會被影片編碼器使用 例如 producer 要求格式為 RGBA_8888 的 buffer 且會被從軟體存取,Gralloc 就會建立每個像素為 4 bytes 且以 R-G-B-A 順序排列的 buffer。 Gralloc 回傳的 handle 可透過 Binder 在不同程序之間傳遞。 ### Protected buffers Gralloc 透過 GRALLOC_USAGE_PROTECTED 旗標來使得 graphics buffer 僅能透過 hardware-protected path 來顯示,這是顯示 DRM 內容的唯一方法。(DRM-protected buffers 不能被 SurfaceFlinger 或 OpenGL ES driver 存取) - 要支援受保護內容的播放軟體必須實作 SurfaceView - 運行在非受保護硬體上的軟體無法存取 buffer # Surface and SurfaceHolder ## Overview ### Surface Surface 是讓 producer 能夠與 consumer 交換 buffer 的一個介面,是 buffer 的進一步封裝。 一個 surface 通常在 BufferQueue 內配置為 tripple-buffering(三倍緩衝區,多一個給 GPU 寫入資料用,作為額外的緩衝區),但當畫面幀率較慢時(30fps on 60fps screen),可能只會在佇列中分配兩個 buffers。可以透過 **`SurfaceFlinger`** 來查看整體的 buffers。 大部分的 surface 是透過 OpenGL ES 或 Vulkan 渲染,然而,有些是透過 Canvas。 > Surface 應該就像是將抽象的 graphic buffers 具體化後的樣子 > graphic buffer 則專注在像素與記憶體空間的對映 #### Canvas rendering Android 的 canvas rendering 是由 [Skia Graphics Library](https://skia.org/) 提供,就像其他渲染引擎,能夠透過 API 畫出各種形狀。畫圖案的這個動作在底層其實就是對指定的記憶體位置寫入資料,但該記憶體位置不能被多人共用,因此也有實作 lock 機制: - lockCanvas():鎖住一塊 buffer 給 CPU 渲染使用,並回傳一個可供繪圖的 Canvas - unlockCanvasAndPost():解鎖 buffer 並送給 compositor (SurfaceFlinger等) - lockHardwareCanvas():鎖住一塊 buffer 給 GPU 渲染使用,並回傳一個可供繪圖的 Canvas producer 第一次向 BufferQueue 要 buffer 時,buffer 會被清空為 0,這是避免分配到的區塊曾被寫入其他資料,而導致顯示錯誤。之後幾次呼叫 lock/unlock 則不會有上述動作,因此拿到的 buffer 為上一個畫面使用過的 buffer。 由於 surface 會保留上一個 buffer 的位址,因此當你 lock surface 時取的一塊不乾淨的區域,系統會幫你把上一個 buffer 的資料複製到剛取得的區塊。SurfaceFlinger 或 HWC 大多負責處理 buffer,但因為我們只需要從 buffer 讀取資料,因此不需要額外的等待時間。 ### SurfaceHolder SurfaceHolder 是系統與應用程式共享 surface 擁有權的一個介面,應用程式可透過SurfaceHolder 對 surface 設定參數。 大部分與 view 互動的元件都有 SurfaceHolder 參與其中,然而有些其他的 API 像是 MediaCodec 卻能獨自與 surface 互動。 ## SurfaceView and GLSurfaceView ### SurfaceView - SurfaceView 可以幫你將渲染的 surface 與其他 surface 合成在一起,例如 Camera API 或 OpenGL ES 內容 - 可透過覆寫預設的 z-ordering 來把surface 移到最上層 ### SurfaceView and the activity lifecycle App 啟動時收到的 callback 順序如下 1. onCreate() 2. onResume() 3. surfaceCreated() 4. surfaceChanged() 按下退後鍵時的 callback 順序如下 1. onPause() 2. surfaceDestroyed() 有關 SurfaceView 和 activity lifecyle 的注意事項: - 按下電源鍵會收到 onPause() 但 surface 會保持運作,因此不會收到 surfaceDestroyed() - 快速重啟 activity 可能會使得 surfaceCreated() 發生在 onPuase() 之前 - 在 onResume()/onStop() 中 start/stop renderer thread - 必須等到 surface 建立後,才能在執行緒中進行初始化,也不要在 surfaceCreate() 中進行初始化,取而代之,應紀錄 surface 狀態,必要時才傳給 rederer thread - 使用 Handler Message 來傳遞 surface 或 SurfaceHolder ### GLSurfaceView - GLSurfaceView 提供了許多 helper classes 來管理 EGL 內容 - 不需要特別為了使用 GLES 來引用 GLSurfaceView - GLSurface 會幫你建立執行緒並管理 EGL 內容,activity pause 時會自動清空狀態 - 大多時候使用 GLSurfaceView 可以讓操作 GLES 的工作更簡單,有時反之 ## SurfaceTexture - SurfaceTexture 是 surface 和 OpenGL ES (GLES) texture 的合體,用來提供 surface 給 GLES texture # SurfaceFlinger and WindowManager ## Overview SurfaceFlinger 接受、編寫並將 buffer 發送到顯示器。 WindowManager 為SurfaceFlinger 提供 buffer 和 window metadata,SurfaceFlinger 用來將 surface 合成到顯示器上。 [WindowManager] -> buffer/metadata -> [SurfaceFlinger] -> [HardwareComposer HAL] > SurfaceFlinger 相關的原始碼位置 frameworks/native/services/surfaceflinger/ - main_surfaceflinger.cpp - SurfaceFlinger.cpp - DispSync.cpp - MessageQueue.cpp - DisplayHardware/HWComposer.cpp frameworks/native/libs/gui/ - DisplayEventReceiver.cpp - BitTube.cpp ### SurfaceFlinger SurfaceFlinger 可以透過兩種方式接收 buffer:1. BufferQueue 和 SurfaceControl 2. ASurfaceControl(*Android 10 以後加入*)。 1. BufferQueue 和 SurfaceControl app 到前端上顯示時,會向 WindowManager 要 buffer,WindowManager 再和 SurfaceFlinger 要 layer。Layer 包含了 BufferQueue 和 SurfaceContorl,而SurfaceControl 包含了 layer 的 metadata。 2. ASurfaceControl(*Android 10 以後加入*)。 - ASurfaceControl 結合了一個 surface 和 SurfaceControl - ASurfaceControl 會與 layer 產生連結 - Apps 透過 callback 取得 ASurfaceTrasactions 的資訊(ASurfaceTransactionStats),像是 latch time, acquire time | Component | Description | | -------- | -------- | | ASurfaceControl | 包裹 SurfaceControl,並使得 app 可以創建與螢幕上的 layer 相關聯的 SurfaceControls | | ASurfaceTransaction | 包裝 Transaction 以使客戶端能夠編輯圖層的描述屬性(例如幾何圖形),並將更新的 buffer 發送到SurfaceFlinger | | ASurfaceTransactionStats | 透過預先註冊的 callback 回傳有關 transactions 的資訊(像是 latch time, acuire times, previous release fence) | 儘管應用程式可以在任何時間提交 buffer,但 SurfaceFlinger 只有在螢幕更新時才會醒來,這是為了最小化記憶體的使用、以及避免畫面撕裂。 當螢幕畫面要切換時,螢幕會送出 VSYNC 訊號給 SurfaceFlinger,SurfaceFlinger 收到後會去輪循每一個 layer 來檢查是否有新的 buffer,如果有則去取得該 buffer,若沒有則採用上次所取得的 buffer,若是該 layer 完全沒有 buffer,則忽略該 layer。 ![](https://i.imgur.com/xeCgF0c.png) 蒐集完所有 buffer 後,SurfaceFlinger 會詢問 Hardware Composer(HWC) 如何組織,如果 HWC 指示 client composition 則 SurfaceFlinger 合成完所有 layer 之後,將 output buffer 丟給 HWC。 ``` adb shell dumpsys SurfaceFlinger ``` ### WindowManager window 物件是 view 物件的容器,WindowManager 負責控制 / 監督 window 物件(lifecycle、input / focus 事件、畫面旋轉、過度、動畫、位置、轉換、z-order),並將所有 metadata 送給 SurfaceFlinger,如此一來 SurfacFlinger 便可透過這些資料來組合 surfaces。 #### Pre-orientation > Many hardware overlays don't support rotation (and even if they do, it costs processing power); the solution is to transform the buffer before it reaches SurfaceFlinger. Android supports a query hint (NATIVE_WINDOW_TRANSFORM_HINT) in ANativeWindow to represent the most likely transform to be applied to the buffer by SurfaceFlinger. GL drivers can use this hint to pre-transform the buffer before it reaches SurfaceFlinger so that when the buffer arrives, it's correctly transformed. > > For example, when receiving a hint to rotate 90 degrees, generate and apply a matrix to the buffer to prevent it from running off the end of the page. To save power, do this pre-rotation. For details, see the ANativeWindow interface defined in system/core/include/system/window.h. ## Tracing Window Transitions // 介紹 WinScope 工具 # Hardware Composer HAL ## Overview HWC 協助 SurfaceFlinger 進行不同 layer 間的合成,Hardware Composer(HWC) HAL 決定了最有效的組合 buffer 的方式,HWC 通常由顯示器OEM 所開發。 試想有一支手機,狀態欄在上、導航欄在下、其餘部分為應用程式,有兩種方式可以組合這些 buffer: 1. 先把應用程式畫面渲染到 buffer 上,再將狀態列蓋上去,接著把導航欄也蓋上去,最後將這個 buffer 傳遞給顯示器。 2. 把這三個 buffer 丟給顯示器,並指示顯示器在顯示不同部分的時候讀取不同的 buffer。 後者的效率遠高於前者。 SurfaceFlinger 和 HWC 的互動: 1. SurfaceFlinger 提供一個列表的 layer 給 HWC 並詢問:你要怎麼處理這些 layer? 2. HWC 對每一個 layer 標記 client / device composition。 3. SurfaceFlinger 會將標註為 client 的 layer 處理完之後傳給 HWC,HWC 則處理其他部分。 當畫面沒有變化時,重疊平面的合成比 GL 合成更沒效率,在上方的圖曾為透明時更是如此,此時 HWC 會要求 GLES 幫忙合成部分圖層。 Android 裝置通常支援到4個重疊的平面,如果試圖合成更多重疊平面的話,系統會試圖使用 GLES 來合成部分的圖層,藉此來節省功耗及提升效能。 ## Implmenting the HWC // 介紹螢幕OEM如何實作HWC # Layers and Displays ## Layers Layer 是圖層組合中最重要的原件,由 surface 和 SurfaceControl 的組合,每一個 layer 會有以下參數來表示如何與其他人互動: | Property | Description | | -------- | -------- | | Position | 定義 layer 在顯示器上的位置,包括 layer 邊緣及 Z-order | | Content | 定義 content 如何在 position 規定的邊界內顯示,像是裁剪(如何在邊界內展開,等比例 or 填滿)和轉換(旋轉、平移) | | Composition | 定義如何與其他 layers 合成,像是 blending-mode 和 layer-wide alpha([alpha composition](https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending)) | | Optimization | 提供非必要但是能供 HWC 合成時最佳化的資訊,像是 layer 的可視區域或是與上一幀有差異的地方 | ## Displays ### Virtual displays ## Case study:screenrecord ## Case study:simulate scondary displays # VSYNC 每幀開始掃描前送出的訊號,會觸發 front buffer 和 back buffer 互換。 # OpenGL ES # // Vulkan # // Synchronization Framework # // Testing # Q 1. 解釋 surface, window, view 的差異 2. 說明 BufferQueue 3. 說明 Gralloc 4. 說明 SurfaceFlinger 5. 說明 WindowManager 6. 說明 Hardware Composer, Hardware Composer HAL 7. 說明 OpenGL, OpenGL ES 8. VSYNC 的動作流程 9. app 到 frontend 的動作流程 10. 還沒進到作業系統前的畫面如何顯示 # Reference - [AOSP Graphic Official Document](https://source.android.com/devices/graphics) - [(簡)Android圖形系統概述](https://juejin.im/post/5d9090af518825323a37713d) - [(簡)Android圖形顯示系統(一)](https://www.jianshu.com/p/424918260fa9) - [(簡)Android圖形系統概述 Posted by Gityuan](http://gityuan.com/2017/02/05/graphic_arch/) - [(簡)SurfaceFlinger啟動篇](http://gityuan.com/2017/02/11/surface_flinger/) - [(簡)SurfaceFlinger繪圖篇](http://gityuan.com/2017/02/18/surface_flinger_2/)