[Android禪修之路] SurfaceFlinger 合成中的工作
theme: channing-cyan
SurfaceFlinger 合成中的工作
前言
在SurfaceFlinger 經歷完合成前的準備之後,接下來的就是具體的合成工作了,合成工作的入口程式碼就是 doComposition ,接下來看看 SurfaceFlinger 合成中又做了那些事情
一 doComposition
首先合成的方法都是在 doComposition 函式中呼叫的,這個函式中總共呼叫了
- doDisplayComposition
- display->getRenderSurface()->flip()
- postFramebuffer
```cpp
void SurfaceFlinger::doComposition(const sp
// 這幾個物件在合成前已經介紹過了,附錄1中也有詳細的介紹
auto display = displayDevice->getCompositionDisplay();
const auto& displayState = display->getState();
// 判斷 OutputCompositionState 是否為 true,如果 OutputCompositionState 的合成狀態不為 true ,
// 則不需要合成,這個變數中 DisplayDevice 建立的時候就設定為 true,只有電源關閉的時候才是 false
if (displayState.isEnabled) {
// 將髒區域轉換為螢幕的座標
const Region dirtyRegion = display->getDirtyRegion(repaintEverything);
// 如果需要則重新繪製幀緩衝區
doDisplayComposition(displayDevice, dirtyRegion);
// 清除髒區域
display->editState().dirtyRegion.clear();
// 通知 Surface
display->getRenderSurface()->flip();
}
// 發生資料到對應的裝置
postFramebuffer(displayDevice);
}
```
二 doDisplayComposition
doDisplayComposition 首先會判斷是否需要合成顯示,只有兩種情況才需要合成顯示,其他情況都可以直接跳過合成
- 髒區域不為空
- 由 hardware composer(後續簡稱hwc)處理
```cpp
void SurfaceFlinger::doDisplayComposition(const sp
// 定義一個準備合成的 Fence
base::unique_fd readyFence;
// 開始合成工作, 並把這個 Fence 傳遞進去
if (!doComposeSurfaces(displayDevice, Region::INVALID_REGION, &readyFence)) return;
// 交換緩衝區,這裡也攜帶了之前建立的 Fence 物件
display->getRenderSurface()->queueBuffer(std::move(readyFence));
} ``` doDisplayComposition 這個函式只做了兩件事 1. 呼叫 doComposeSurfaces 做合成工作。 2. 呼叫 queueBuffer 將處理完成的緩衝區入隊。 在這個過程中它還會傳遞一個 Fence,這個東西就是用來保證緩衝區同步機制的。
三 doComposeSurfaces
doComposeSurfaces - 合成Surface ,這個函式較長,主要分為以下幾個部分,我們還是分塊檢視
3.1 doComposeSurfaces 的第一部分
第一部分會獲得一些預先處理好的引數,這些都比較簡單,我們唯一需要注意的一點就是這裡拿到了一個 hasClientComposition 的變數,用來區分是否是客戶端處理
從這一步開始,SurfaceFlinger 就會根據圖層的不同處理型別,進行不同的合成邏輯,主要分為客戶端處理和硬體處理,其中客戶端處理也就是 OpenGl 處理(軟體處理),而硬體處理就是由 hwc 處理。
```cpp
bool SurfaceFlinger::doComposeSurfaces(const sp
auto display = displayDevice->getCompositionDisplay();
const auto& displayState = display->getState();
const auto displayId = display->getId();
// 這裡 getRenderEngine 獲得的是 SurfaceFlinger 在 init 函式中, setRenderEngine 建立的 GLESRenderEngine
// 它提供了一些 OpenGL 的方法
auto& renderEngine = getRenderEngine();
// 是否是受保護的內容,與版權保護相關,此處不關注
const bool supportProtectedContent = renderEngine.supportsProtectedContent();
const Region bounds(displayState.bounds);
const DisplayRenderArea renderArea(displayDevice);
// 是否包含由GLES處理的圖層, GLES的處理圖層就是客戶端處理, 是否就是系統硬體處理
const bool hasClientComposition = getHwComposer().hasClientComposition(displayId);
bool applyColorMatrix = false;
renderengine::DisplaySettings clientCompositionDisplay;
std::vector<renderengine::LayerSettings> clientCompositionLayers;
sp<GraphicBuffer> buf;
base::unique_fd fd;
if (hasClientComposition) {
// 如果需要客戶端處理,則執行下面這段邏輯
if (displayDevice->isPrimary() && supportProtectedContent) {
// 如果該圖層是受保護的圖層,則需要標記受保護的上下文
// 數字版權相關,此處無需關注,詳細的可以自行研究 Android DRM 技術
bool needsProtected = false;
for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
if (layer->isProtected()) {
needsProtected = true;
break;
}
}
if (needsProtected != renderEngine.isProtected()) {
renderEngine.useProtectedContext(needsProtected);
}
if (needsProtected != display->getRenderSurface()->isProtected() &&
needsProtected == renderEngine.isProtected()) {
display->getRenderSurface()->setProtected(needsProtected);
}
}
// 1. 呼叫 RenderSurface 的 dequeueBuffer
buf = display->getRenderSurface()->dequeueBuffer(&fd);
...
```
第一部分還是比較常規的引數處理,只是這裡值得注意的一點是,圖層的合成從這裡開始區分是由客戶端(軟體 OpenGl )處理,還是由硬體 hwc 處理,然後再呼叫 RenderSurface 的 dequeueBuffer 來獲取緩衝區,這裡會傳遞進去一個檔案描述符,它是 SurfaceFlinger 中用於同步的 Fence 機制,關於 Fence 機制檢視[同步機制 Fence ]
第一部分通過 displayDevice 開始獲取的一些物件就是封裝的顯示裝置的物件,具體的介紹可以檢視附錄1
最後就是從緩衝區佇列獲取一個緩衝區
3.2 doComposeSurfaces 的第二部分
這段程式碼的前半部分都是一些引數的設定,然後就是根據不同的合成方式,進行不同的處理
```cpp
bool SurfaceFlinger::doComposeSurfaces(const sp
clientCompositionDisplay.physicalDisplay = displayState.scissor;
clientCompositionDisplay.clip = displayState.scissor;
const ui::Transform& displayTransform = displayState.transform;
clientCompositionDisplay.globalTransform = displayTransform.asMatrix4();
clientCompositionDisplay.orientation = displayState.orientation;
// DisplayColorProfile 封裝了有關如何為顯示器轉換顏色的所有狀態和功能
const auto* profile = display->getDisplayColorProfile();
Dataspace outputDataspace = Dataspace::UNKNOWN;
if (profile->hasWideColorGamut()) {
outputDataspace = displayState.dataspace;
}
clientCompositionDisplay.outputDataspace = outputDataspace;
clientCompositionDisplay.maxLuminance =
profile->getHdrCapabilities().getDesiredMaxLuminance();
const bool hasDeviceComposition = getHwComposer().hasDeviceComposition(displayId);
const bool skipClientColorTransform = getHwComposer().hasDisplayCapability(displayId,
HWC2::DisplayCapability::SkipClientColorTransform);
// 計算顏色變化矩陣
applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform;
if (applyColorMatrix) {
clientCompositionDisplay.colorTransform = displayState.colorTransformMat;
}
}
/*
* 然後,渲染針對幀緩衝區的圖層
*/
bool firstLayer = true;
Region clearRegion = Region::INVALID_REGION;
for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
const Region viewportRegion(displayState.viewport);
const Region clip(viewportRegion.intersect(layer->visibleRegion));
if (!clip.isEmpty()) {
// 根據不同的合成型別,通過呼叫 prepareClientLayer 將相關的引數設定到圖層Layer中,
switch (layer->getCompositionType(displayDevice)) {
case Hwc2::IComposerClient::Composition::CURSOR:
case Hwc2::IComposerClient::Composition::DEVICE:
case Hwc2::IComposerClient::Composition::SIDEBAND:
case Hwc2::IComposerClient::Composition::SOLID_COLOR: {
const Layer::State& state(layer->getDrawingState());
if (layer->getClearClientTarget(displayDevice) && !firstLayer &&
layer->isOpaque(state) && (layer->getAlpha() == 1.0f) &&
layer->getRoundedCornerState().radius == 0.0f && hasClientComposition) {
//永遠不要清除第一個圖層,因為可以保證 FB 已經清除
renderengine::LayerSettings layerSettings;
Region dummyRegion;
// 2. 呼叫 Layer 的 prepareClientLayer
bool prepared =
layer->prepareClientLayer(renderArea, clip, dummyRegion,
supportProtectedContent, layerSettings);
if (prepared) {
layerSettings.source.buffer.buffer = nullptr;
layerSettings.source.solidColor = half3(0.0, 0.0, 0.0);
layerSettings.alpha = half(0.0);
layerSettings.disableBlending = true;
clientCompositionLayers.push_back(layerSettings);
}
}
break;
}
case Hwc2::IComposerClient::Composition::CLIENT: {
renderengine::LayerSettings layerSettings;
// 2. 呼叫 Layer 的 prepareClientLayer
bool prepared =
layer->prepareClientLayer(renderArea, clip, clearRegion,
supportProtectedContent, layerSettings);
if (prepared) {
clientCompositionLayers.push_back(layerSettings);
}
break;
}
default:
break;
}
} else {
ALOGV(" Skipping for empty clip");
}
firstLayer = false;
}
...
} ``` 第二部分主要呼叫的就是 Layer 的 prepareClientLayer,根據不同的型別,它會準備不同的引數傳遞到 Layer 中,這是為 Layer 設定一些合成前的引數。
3.3 doComposeSurfaces 的第三部分
第三部分的關鍵點是
```cpp
bool SurfaceFlinger::doComposeSurfaces(const sp
// 這裡先提高GPU的頻率用來處理色彩空間轉換,然後重置GPU的頻率來節省電量
const bool expensiveRenderingExpected =
clientCompositionDisplay.outputDataspace == Dataspace::DISPLAY_P3;
if (expensiveRenderingExpected && displayId) {
mPowerAdvisor.setExpensiveRenderingExpected(*displayId, true);
}
if (!debugRegion.isEmpty()) {
...
}
// 3. 如果是客戶端合成,則通過 GPU 合成為特定顯示器渲染圖層
// 這個 renderEngine 之前已經說明了,它是 GLESRenderEngine ,它封裝了一些 OpenGL 的方法
renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayers,
buf->getNativeBuffer(), /*useFramebufferCache=*/true, std::move(fd),
readyFence);
} else if (displayId) {
// 如果不是客戶端合成,則重置GPU的頻率,因為一直使用高頻GPU會耗電
mPowerAdvisor.setExpensiveRenderingExpected(*displayId, false);
}
return true;
} ```
3.4 doComposeSurfaces 的總結
現在看完了 doComposeSurfaces 的三個部分,這裡先做一下小小的總結
- 先是處理一些引數,然後呼叫 RenderSurface 的 dequeueBuffer ,分配緩衝區
- 處理 client composition 的相關引數,並計算顏色變換矩陣
- 渲染幀緩衝區的圖層,首先填充幀緩衝區的圖層 Layer prepareClientLayer, 設定一些引數
- 渲染圖層 Layer,如果是使用客戶端合成,就直接呼叫 GLESRenderEngine 的 drawLayers
補充說明:
緩衝區的分配,在 SurfaceFlinger 的合成過程中,涉及到一個生產者和消費者模型,並且這個模型貫穿了整個流程,詳情可見[解讀SurfaceFlinger中的生產者和消費者]
關於圖層的合成和一些處理操作,因為不同的圖層具體的操作也不相同,所以具體的操作參考[Layer詳解]
renderEngine 是 SurfaceFlinger 初始化時建立的一個 GLESRenderEngine 物件,所以最後它呼叫 GLESRenderEngine 的 drawLayers 函式,其實也是通過 OpenGL 完成的。
四 生產者和消費者簡介
之前已經說明了在合成中的緩衝區分配,有一個生產者和消費者,這裡我們可以簡單介紹一下這個模型執行的過程,如上圖所示,具體的流程如下
- 生產者從 BufferQueue 中申請緩衝區
- BufferQueue 從底層拿到一塊緩衝區,然後將緩衝區出隊,提供給生產者
- 生產者將資料寫入緩衝區後,將緩衝區入隊,然後緩衝區在 BufferQueue 中,等待消費者讀取
- 消費者讀取完緩衝區後,又回到步驟1
現在的 queueBuffer 其實就是步驟3,生產者將緩衝區入隊,然後緩衝區在 BufferQueue 中等待消費者讀取
緩衝區的入隊和緩衝區的出隊涉及到的邏輯一樣非常多,詳情可見解讀SurfaceFlinger中的生產者和消費者
五 postFramebuffer
在緩衝區入隊之後,還做了一個清除髒資料的操作和一個 flip 函式翻頁的操作,不過這個兩個函式的邏輯比較簡單,這裡就不展開了。接下來再來看看 postFramebuffer 函式
```cpp
void SurfaceFlinger::postFramebuffer(const sp
auto display = displayDevice->getCompositionDisplay();
const auto& displayState = display->getState();
const auto displayId = display->getId();
if (displayState.isEnabled) {
if (displayId) {
// Fence 同步
getHwComposer().presentAndGetReleaseFences(*displayId);
}
// DisplaySurface 的 onFrameCommitted
display->getRenderSurface()->onPresentDisplayCompleted();
for (auto& layer : display->getOutputLayersOrderedByZ()) {
// 按照 Z 軸遍歷圖層
sp<Fence> releaseFence = Fence::NO_FENCE;
bool usedClientComposition = true;
// 只有當來自該幀(如果有)的 release Fence 訊號發出時,HWC才會釋放來自前一幀(如果有)的層緩衝區。
// 始終首先從HWC處獲得釋放圍欄。
if (layer->getState().hwc) {
const auto& hwcState = *layer->getState().hwc;
releaseFence =
getHwComposer().getLayerReleaseFence(*displayId, hwcState.hwcLayer.get());
usedClientComposition =
hwcState.hwcCompositionType == Hwc2::IComposerClient::Composition::CLIENT;
}
// 如果前一幀使用的是軟體合成,則這一幀需要與前一幀合併
if (usedClientComposition) {
releaseFence =
Fence::merge("LayerRelease", releaseFence,
display->getRenderSurface()->getClientTargetAcquireFence());
}
// 拿到圖層,然後呼叫 onLayerDisplayed ,傳遞 Fence 訊號進行同步
layer->getLayerFE().onLayerDisplayed(releaseFence);
}
// 我們有一個圖層列表需要 fence , 這些圖層中 Z 軸數不相交的,
// 所以我們能做的最好方式就是為它們提供當前的 fence
if (!displayDevice->getLayersNeedingFences().isEmpty()) {
// 如果圖層中需要 Fence 訊號的佇列不為空,則在合成結束後,都傳遞一遍 Fence 訊號
sp<Fence> presentFence =
displayId ? getHwComposer().getPresentFence(*displayId) : Fence::NO_FENCE;
for (auto& layer : displayDevice->getLayersNeedingFences()) {
layer->getCompositionLayer()->getLayerFE()->onLayerDisplayed(presentFence);
}
}
if (displayId) {
// 然後清除 hwc 的 Fence 訊號
getHwComposer().clearReleaseFences(*displayId);
}
}
}
void RenderSurface::onPresentDisplayCompleted() { // 呼叫 DisplaySurface 的 onFrameCommitted mDisplaySurface->onFrameCommitted(); } ``` postFramebuffer 主要就是傳遞一些 Fence 訊號,然後提交資料。
需要注意的一點就是,在 SurfaceFlinger 的工作過程中,應用會將自己的畫面提供給 SurfaceFlinger,然後 SurfaceFlinger 再將這些資料所在的緩衝區提交到硬體。但是緩衝區提交給了硬體,並不代表這些資料就已經是準備好可以使用的,這時在提交資料的同時,還會附帶的提交一個 Fence 訊號標誌,具體什麼時候可以用,就需要等待這個訊號。所以這裡進行了一系列的訊號傳遞。
六 總結
關於合成中的操作,到這就簡單的介紹完了,還是進行一個簡單的總結
- 合成中的函式主要有兩個
- doDisplayComposition:重繪部分需要重繪的緩衝區
- 傳送緩衝區資料到顯示裝置
- doDisplayComposition 處理顯示的合成邏輯,包含兩步
- doComposeSurfaces:根據圖層的不同型別(Layer 和它的各種子類)還有合成方式(客戶端合成還是 hwc 合成)進行不同的處理
- queueBuffer:將處理完的緩衝區入隊
- postFramebuffer 發生緩衝區資料到顯示裝置,此處會有 Fence 同步
到此,SurfaceFlinger 合成中的工作流程,算是簡單的總結完了,但是對於其中 hwc 和 Layer 的細節,我們依舊還是不清楚,雖然瞭解了 SurfaceFlinger 合成的一些流程,但是又似乎並沒有完全弄懂,關於這些內容,後續還是需要單獨深入學習。
SurfaceFlinger 的合成工作比合成之前的準備工作要複雜許多,特別是裡面涉及到的一些其他知識點,如果不弄清這些知識點,又很難弄懂 SurfaceFlinger 合成時的具體邏輯,所以我將 SurfaceFlinger 合成時涉及到的其他知識點都單獨拿出來,然後和 SurfaceFlinger 合成的具體流程進行對照,通過這樣拆分和組合的方式,也更容易弄懂 SurfaceFlinger 的合成原理
- 首先是 display->getRenderSurface()->dequeueBuffer 和 display->getRenderSurface()->queueBuffer 這兩個函式,這裡面涉及到了 SurfaceFlinger 的生產者和消費者模型,以及 GraphicBuffer 的建立過程,以及 Android 系統中,圖形系統的 ION 機制,這三點的難度逐漸增加,需要在學習的時候舉一反三,不斷的回頭讀程式碼驗證
- [SurfaceFlinger 中的生產者和消費者]
- [GraphicBuffer 的建立過程]
-
[Android 圖形系統的 ION 機制]
-
然後是在不同圖層的處理,例如 prepareClientLayer 函式不同的 Layer 都做了些什麼,[Layer詳解]
- 軟體合成和硬體合成,HWC 的工作原理
- 接下來則是 GLESRenderEngine 的 drawLayers ,它涉及到 OpenGL 在 Android 圖形系統,不過對於 OpenGL 目前並沒有深入瞭解的打算,一是學這東西有門檻,另外則是對於應用工程師,學這個花費了精力,也不如專業弄這個的遊戲領域的人,所以 OpenGL 簡單瞭解即可。
- 最後則是 postFramebuffer 函式,它裡面又涉及到 Android 圖形系統中比較重要的一塊, [Fence 同步機制]
到此,合成中的邏輯已經整理清楚了,不過一下子出來這麼多東西,也不知道該慶幸還是不幸,不過 Android 圖形系統中,重要的內容這裡幾乎佔一半,接下來再看 SurfaceFlinger 合成後的處理
- Activity啟動原始碼解析(Android12)
- 從MediaServer看Binder的使用方式(一)
- 從MediaServer看Binder的使用方式(二)
- [Android禪修之路] 解讀Layer
- [Android禪修之路] Android圖形系統,從Activity到Surface
- [Android禪修之路] 解讀 GraphicBuffer 之 Framework 層
- [Android禪修之路] 解讀SurfaceFlinger中的BufferQueue
- [Android禪修之路] SurfaceFlinger 合成中的工作
- [Android禪修之路] SurfaceFlinger 中的一些物件
- [Android禪修之路] SurfaceFlinger 合成前的預處理
- [Android禪修之路] SurfaceFlinger合成總覽
- [Android禪修之路] SurfaceFlinger的啟動過程
- [Android禪修之路] Android 圖形系統開篇