[Android禪修之路] SurfaceFlinger 合成前的預處理
theme: channing-cyan
SurfaceFlinger 合成前的預處理
前言
之前的SurfaceFlinger 合成總覽已經把合成的整體流程羅列出來了, 從這篇開始, 我們會分別就合成的不同階段, 進行詳細的解讀。
首先是合成前的準備工作.
一 合成前
SurfaceFlinger 合成流程的全部程式碼之前已經列出來了,這裡只關注合成前的部分。
1.1 handleMessageRefresh
```cpp void SurfaceFlinger::handleMessageRefresh() { mRefreshPending = false;
const bool repaintEverything = mRepaintEverything.exchange(false);
// 合成前預處理
preComposition();
// 重建Layer集合,並計算每個Layer的可見區域的髒資料
rebuildLayerStacks();
// 計算工作區間
calculateWorkingSet();
...
} ```
可以看出, SurfaceFlinger 整個合成前的準備的過程, 還是比較明朗的, 接下來我們就來詳細解讀這三部分
- preComposition:合成前的預處理
- rebuildLayerStacks:重建圖層髒區域的集合
- calculateWorkingSet:計算工作區間
對於這些函式的具體內容,我們先不做猜測,通過看具體的原始碼,再來總結
二 preComposition
preComposition 就是合成前的預處理工作,我們之間看原始碼
cpp
void SurfaceFlinger::preComposition()
{
mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
bool needExtraInvalidate = false;
// 遍歷圖層呼叫對應 Layer 的 onPreComposition
mDrawingState.traverseInZOrder([&](Layer* layer) {
if (layer->onPreComposition(mRefreshStartTime)) {
needExtraInvalidate = true;
}
});
// 如果有 Layer 還有未處理的 Frame (或者說發生了改變),
// 則傳送一個 Invalidate 訊息, 再進行一次合成和渲染操作
if (needExtraInvalidate) {
signalLayerUpdate();
}
}
首先就出現了一個 mDrawingState 物件,這個物件定義在 SurfaceFlinger.h 的標頭檔案中,它是一個 State 內部類,在 Layer.h 標頭檔案中也定義了 State,不過它是一個結構體。它們是兩個不同的 State,所以在閱讀程式碼的時候不能搞混。
c
State mDrawingState{LayerVector::StateSet::Drawing};
2.1 State
```c class State { public: // LayerVector::StateSet 是 LayerVector.h 標頭檔案中定義的一個列舉類,它有三個列舉物件 // Invalid:已失效的 // Current:當前的 // Drawing:之前繪製的 // 現在我們再來看看 SurfaceFlinger 中定義的 State,它使用的列舉物件是 Drawing,也就是之前繪製的 explicit State(LayerVector::StateSet set) : stateSet(set), layersSortedByZ(set) {} State& operator=(const State& other) { // 顯式地拷貝 State layersSortedByZ = other.layersSortedByZ; displays = other.displays; colorMatrixChanged = other.colorMatrixChanged; if (colorMatrixChanged) { colorMatrix = other.colorMatrix; } globalShadowSettings = other.globalShadowSettings; return *this; }
const LayerVector::StateSet stateSet = LayerVector::StateSet::Invalid;
// 所有參與繪製的 Layer
LayerVector layersSortedByZ;
// 所有輸出裝置的物件,這個一個 map 物件,key 是一個 IBinder,值是 DisplayDeviceState
DefaultKeyedVector< wp<IBinder>, DisplayDeviceState> displays;
bool colorMatrixChanged = true;
mat4 colorMatrix;
renderengine::ShadowSettings globalShadowSettings;
void traverse(const LayerVector::Visitor& visitor) const;
void traverseInZOrder(const LayerVector::Visitor& visitor) const;
void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const;
};
```
通過這個 mDrawingState , 我們簡單的瞭解了一些 State 這個類。
這個 mDrawingState 儲存了所有需要參與繪製的 Layer , 此處會按照 Z 軸的排序取到當前需要繪製的 Layer ,然後對每個 Layer 呼叫其 onPreComposition 判斷其是否還有未處理的的 Frame ,如果有就將 needExtraInvalidate 置為 true ,表示需要進行額外的合成和渲染操作。
2.2 LayerVector
我們再來簡單看一下定義列舉的 LayerVector
```c
class LayerVector : public SortedVector
LayerVector& operator=(const LayerVector& rhs);
// 對 Layer 進行排序
int do_compare(const void lhs, const void rhs) const override;
// 注意, 這裡用 Visitor 命名了一個引數說 Layer 指標, 返回值是 void 的函式
// 這個函式後續會用到
using Visitor = std::function<void(Layer*)>;
void traverseInReverseZOrder(StateSet stateSet, const Visitor& visitor) const;
void traverseInZOrder(StateSet stateSet, const Visitor& visitor) const;
private: const StateSet mStateSet; }; } ```
LayerVector 這個類也比較簡單, 不過讓人注意的一點是, 裡面有一個 StateSet 的列舉變數, 它有三個引數, 而這三個引數基本對應著一個 Layer 的三種狀態
最後再來看一下它的父類 SortedVector
2.3 SortedVector
c
template <class TYPE>
class SortedVector : private SortedVectorImpl
{
friend class Vector<TYPE>;
public:
typedef TYPE value_type;
}
這是一個模板類, 而裡面有一個 Vector 容器, 它裝的就是需要顯示的 Layer。
2.4 signalLayerUpdate
最後再來看一下如果 preComposition 做完遍歷後的另一個操作,也就是如果 Layer 發生了改變的情況, 這裡呼叫了 EventQueue 的 invalidate 傳送訊息, 也就是將操作傳遞到了 SurfaceFlinger 中的 Handle 的訊息機制。
cpp
void SurfaceFlinger::signalLayerUpdate() {
mScheduler->resetIdleTimer();
//又呼叫了一次 invalidate , INVALIDATE 事件之前已經分析過了
mEventQueue->invalidate();
}
到此,SurfaceFlinger 的合成前的預處理 preComposition,我們算是大概看完了,不過看完之後不僅沒有解答之前的疑惑,還出現了更多的疑惑。
首先,我們用一句話概括預處理 preComposition 所做的事情,那就是:
遍歷 mDrawingState 物件中的 Layer,呼叫每個 Layer 的 preComposition,這個函式會返回這個 Layer 是否發生了改變,如果發生了改變,就需要重繪,並且通過 SurfaceFlinger 的訊息機制,將重繪訊息傳入 EventQueue。
當然,在這個過程中,我們遇到了一些疑問:
- Layer 的 preComposition 做了哪些事:[解讀Layer]
- EventQueue 的訊息佇列接收到重繪訊息後又發生了什麼:[解讀SurfaceFlinger中的訊息佇列]
- 這個 mDrawingState 中的 Layer 圖層,是怎樣新增進來的 這些疑問我們現在還無法解答,只能在針對這些機制的篇幅中單獨研究,等到我們看完整個 SurfaceFlinger 的工作原理後,這些我們也就知道了。
三 rebuildLayerStacks
rebuildLayerStacks 中的工作較多, 不過一句話概括就是: 重新計算所有需要繪製的 Layer 的髒區域
當然,rebuildLayerStacks 函式的邏輯較長, 這裡分為幾個部分解析
3.1 rebuildLayerStacks 的第一部分
cpp
// 重建裝置的可見Layer集合,並計算每個Layer的可見區域的髒區域
void SurfaceFlinger::rebuildLayerStacks() {
// 只重建可見的髒區域
if (CC_UNLIKELY(mVisibleRegionsDirty)) {
mVisibleRegionsDirty = false;
//將 mGeometryInvalid 置為 true ,這個值影響後續是否需要 hwc 合成
invalidateHwcGeometry();
// 針對每一個顯示裝置重建可見Layer
for (const auto& pair : mDisplays) {
// mDisplays 是一個 map,它是 value 就是 DisplayDevice
const auto& displayDevice = pair.second;
//DisplayDevice 的 getCompositionDisplay ,返回一個 std::shared_ptr<compositionengine::Display>
auto display = displayDevice->getCompositionDisplay();
//Display 繼承自 Output , Output 的 getState ,返回一個 OutputCompositionState
//OutputCompositionState 是輸出的原始合成狀態資料
const auto& displayState = display->getState();
//兩個區域, opaqueRegion 是不透明區域, dirtyRegion 是髒區域
Region opaqueRegion;
Region dirtyRegion;
compositionengine::Output::OutputLayers layersSortedByZ;
Vector<sp<Layer>> deprecated_layersSortedByZ;
Vector<sp<Layer>> layersNeedingFences;
//transform 是邏輯到物理的轉換
const ui::Transform& tr = displayState.transform;
//bounds 是物理顯示屏的區域
const Rect bounds = displayState.bounds;
...
}
}
}
第一部分的程式碼出現了幾個物件,關於這幾個物件這裡簡單介紹一下,具體詳細的可以看附錄1
- mDisplays: 這是 SurfaceFlinger 中儲存 DisplayDevice 物件的 map 集合, 它的 key 是一個 IBinder 的弱指標, value 則是一個 DisplayDevice 指標
cpp
std::map<wp<IBinder>, sp<DisplayDevice>> mDisplays;
- DisplayDevice: 顯示裝置, Android 系統是支援多顯示器的, 而這個 DisplayDevice 則封裝了具體顯示器的顯示引數。
- compositionengine::Display:一個合成目標物件,它繼承自 Output,封裝了硬體合成器 HWC 的一些引數
我們再看一下 DisplayDevice 的建立方式, 在之前介紹 SurfaceFlinger 的啟動流程中的 init 函式中, 有這樣一行程式碼。SurfaceFlinger的啟動流程
cpp
const auto display = getDefaultDisplayDeviceLocked();
顯然, 它是獲取預設的 DisplayDevice , 在一系列的呼叫之後, 會通過一個 token 來拿到一個 DisplayDevice
```cpp
[frameworks/native/services/surfaceflinger/SurfaceFlinger.h]
sp
// 拿到 displayId,這個 displayId 也就是它的 IBinder 的值
sp
std::optional
這裡拿到的 it 就是 map , 而 it->second 則是我們需要的 DisplayDevice. 關於 mDisplays 集合的資料來源, 這裡暫不深究, 後續單獨探究
第一部分, 主要還是定義了一些後續需要用到的物件. 接下來, 我們就看看這些物件的處理邏輯
3.2 rebuildLayerStacks 第二部分
接下來就是第二部分了,之前的第一部分主要做的就是準備一些現在需要用到的物件,而第二部分就是具體的執行
- OutputCompositionState 是輸出合成的原始資料, 如果它的 isEnabled 為 false ,則表示這個 DisplayDevice 不需要此次合成, 所以這裡就不需要執行具體的邏輯
- 然後是計算可見區域, 這也是我們常說的計算髒區域
- 接下來就是遍歷需要繪製的 Layer , 按照我們傳入的 Visitor 函式處理,關於 Visitor 的定義檢視[2.2]
關於這裡出現的 DisplayDevice 和 OutputCompositionState 的詳細說明, 可以跳轉附錄1檢視
```cpp //只有 OutputCompositionState 的 isEnabled 為 true 時,才會執行合成 if (displayState.isEnabled) { //計算可見區域 computeVisibleRegions(displayDevice, dirtyRegion, opaqueRegion);
// 遍歷所有需要繪製的 Layer
// 遍歷到的 Layer 會經過函式 Visitor 的處理
// 這個 Visitor 函式在之前的 LayerVector 中
mDrawingState.traverseInZOrder([&](Layer* layer) {
...
});
} ```
3.2.1 computeVisibleRegions
計算可見區域, 傳入的兩個引數
- dirtyRegion: 髒區域
- opaqueRegion: 不透明區域
在傳入的 Visitor 函式中, 處理了 Layer 物件, 關於 Layer 的詳細解讀, 跳轉Layer 解讀
關於 SurfaceFlinger 中可見區域的計算, 有以下規則
- 在 Android 中, 螢幕上繪製出來的影象上多個應用相加而來的
- 上層的應用的不透明區域會遮擋住下層的應用,這部分不需要繪製
- 上層應用的透明或半透明區域遮擋住的部分, 依舊是可見的, 需要繪製
其次,再來說說 Android 中的圖層 Layer,使用用過 Photoshop 的使用者應該知道,圖層的概念。軟體中的一張圖是由多個圖層疊加而來的,如果上面有一個不透明的圖層覆蓋,那麼下面的圖層將會被遮擋,也就是不可見。 在 Android 中也是一樣的,所以關於圖層區域的分類,有如下幾類
- aboveOpaqueLayers:當前 Layer 的上層 Layer 所有的不透明區域。也就是說這部分的區域是都不需要繪製的,需要從髒區域中減去
- aboveCoveredLayers:當前 Layer 的上層 Layer 所有的可見區域。
- opaqueRegion:當前 Layer 的不透明區域。(透明和半透明都不屬於不透明區域)
- visibleRegion:當前 Layer 的可見區域。這個區域是需要計算的,被上層 Layer 覆蓋的,完全透明的都需要減去。
- coveredRegion:當前 Layer 被上層 Layer 覆蓋的區域。(被透明區域覆蓋的區域也算被覆蓋區域)
- transparentRegion:完全透明的區域。(這部分割槽域沒有必要合成)
基於這些理解,我們再來看以下程式碼
```cpp
void SurfaceFlinger::computeVisibleRegions(const sp
//std::shared_ptr<compositionengine::Display>
// 由 hwc 裝置支援的顯示器合成目標,封裝了一些 hwc 相關的引數
auto display = displayDevice->getCompositionDisplay();
// 上層應用全部 Layer 的不透明區域
Region aboveOpaqueLayers;
// 上層應用全部 Layer 的被覆蓋的(可見)區域,
Region aboveCoveredLayers;
// 髒區域: 需要繪製的地方
Region dirty;
// outDirtyRegion 是要返回的繪製區域,所以這裡先清除之前遺留的資料
outDirtyRegion.clear();
// 從這裡開始遍歷,也就是說之前定義的一些變數是會隨著遍歷一直存在的,在按照 Z 軸遍歷圖層的時候
// 每遍歷到的一層都需要新增到之前定義的區域中,因為對於它下面的圖層來說,它就是上層的圖層
// 遍歷 mDrawingState 中需要繪製的 Layer,這裡傳入的是一個函式 Visitor
mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
// Visitor 的入引數是 Layer 指標,這個函式就是將 Layer 按照 Z 軸的座標逆序排列
// 獲取 Layer 的 mDrawingState
const Layer::State& s(layer->getDrawingState());
// Android支援多個螢幕,layer可以定製化的只顯示到某個顯示螢幕上。其中就是靠layerStack(Layer棧)來實現的
// Layer的stack值如果和DisplayDevice的stack值一樣,說明這個layer是屬於這個顯示螢幕的
// 所以如果 Layer 不屬於此 DisplayDevice,則不顯示
if (!display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
return;
}
//不透明區域
Region opaqueRegion;
//可見區域,螢幕上的不完全透明的區域,被半透明覆蓋的區域,部分被不透明覆蓋的區域
Region visibleRegion;
//覆蓋區域,上方被可見區域(包括半透明區域)覆蓋的表面區域
Region coveredRegion;
//完全透明的區域
Region transparentRegion;
// 通過將可見區域設定為空來處理隱藏表面
if (CC_LIKELY(layer->isVisible())) {
//isOpaque 返回 true 表示 layer 不透明,所以返回 false 時表示這個 layer 是透明的
const bool translucent = !layer->isOpaque(s);
// 這個 Layer 的需要繪製的區域,getScreenBounds 見[3.2.1.1]
Rect bounds(layer->getScreenBounds());
// 先將這個區域設定為可見區域
visibleRegion.set(bounds);
ui::Transform tr = layer->getTransform();
// 如果可見區域不為空
if (!visibleRegion.isEmpty()) {
//先判斷 layer 是否透明
if (translucent) {
//如果layer是透明的,就判斷能否做透明區域的優化
if (tr.preserveRects()) {
// 如果可以進行透明區域優化, 則執行透明區域優化
transparentRegion = tr.transform(layer->getActiveTransparentRegion(s));
} else {
// 如果 Transform 太複雜, 就不執行透明區域優化
transparentRegion.clear();
}
}
// 計算完全不透明的區域,完全不透明區域居然要系統的圓角 radius 為0
const int32_t layerOrientation = tr.getOrientation();
if (layer->getAlpha() == 1.0f && !translucent &&
layer->getRoundedCornerState().radius == 0.0f &&
((layerOrientation & ui::Transform::ROT_INVALID) == false)) {
// 如果這個區域是完全不透明的區域,就把可見區域放到完全不透明的區域中
opaqueRegion = visibleRegion;
}
}
}
// 如果可見區域是空, 就清除掉圖層上的可見區域範圍
if (visibleRegion.isEmpty()) {
layer->clearVisibilityRegions();
return;
}
// 將被覆蓋區域 = 處於上層區域中的可見區域
coveredRegion = aboveCoveredLayers.intersect(visibleRegion);
// 將本層的可見區域新增到上層圖層的區域中, 用來處理下一個圖層
aboveCoveredLayers.orSelf(visibleRegion);
// 本層的可見區域需要減去上層應用的不透明區域
visibleRegion.subtractSelf(aboveOpaqueLayers);
// 計算圖層的髒區域
if (layer->contentDirty) {
// 如果內容需要重繪,那麼可見區域就是需要重繪的髒區域
dirty = visibleRegion;
// 將本層的髒區域新增到之前圖層的髒區域中, 也就是合成所有圖層的髒區域
dirty.orSelf(layer->visibleRegion);
// 圖層的可見區域處理了之後, 圖層就沒有髒區域了
layer->contentDirty = false;
} else {
/* 如果圖層沒有髒區域, 則計算暴露區域
* 暴露區域由兩部分組成
* 1) 現在可見並且以前被覆蓋的區域
* 2) 現在新增的暴露區域(以前這個區域區域被遮擋了)
*/
// 新暴露的區域: 可見區域間去被覆蓋的區域
const Region newExposed = visibleRegion - coveredRegion;
// 舊的可見區域即圖層的可見區域
const Region oldVisibleRegion = layer->visibleRegion;
// 舊的被覆蓋區域即圖層的被覆蓋區域
const Region oldCoveredRegion = layer->coveredRegion;
// 舊的暴露區域: 即舊的可見區域減去舊的被覆蓋的區域
const Region oldExposed = oldVisibleRegion - oldCoveredRegion;
// (newExposed-oldExposed) 是新增的暴露區域
// (visibleRegion&oldCoveredRegion) 是新增的沒有被覆蓋的區域
// 髒區域 = 新增的沒有被覆蓋的區域 + 新增的暴露區域
dirty = (visibleRegion&oldCoveredRegion) | (newExposed-oldExposed);
}
// 髒區域需要裁剪掉上層圖層的不透明區域
dirty.subtractSelf(aboveOpaqueLayers);
// 將新增的髒區域新增到返回引數中
outDirtyRegion.orSelf(dirty);
// 將本圖層不透明區域新增到上層圖層區域, 處理下一個圖層
aboveOpaqueLayers.orSelf(opaqueRegion);
//儲存圖層計算區域
layer->setVisibleRegion(visibleRegion);
layer->setCoveredRegion(coveredRegion);
layer->setVisibleNonTransparentRegion(
visibleRegion.subtract(transparentRegion));
});
outOpaqueRegion = aboveOpaqueLayers;
} ```
3.2.1.1 Layer::getScreenBounds
getScreenBounds 就是獲取當前 Layer 圖層的邊界
```c [frameworks/native/services/surfaceflinger/Layer.cpp] Rect Layer::getScreenBounds(bool reduceTransparentRegion) const { if (!reduceTransparentRegion) { return Rect{mScreenBounds}; }
FloatRect bounds = getBounds();
ui::Transform t = getTransform();
// Transform to screen space.
bounds = t.transform(bounds);
return Rect{bounds};
} ```
我們看到的 getScreenBounds 沒有傳遞引數,而這裡確有一個引數,也就是說它有一個預設引數,從 Layer.h 這個標頭檔案中科院得知,這個預設引數是 true,也就是說 Layer 會先通過 getBounds 拿到一個 bounds,然後再對這個 bounds 進行處理,最後得到一個 Rect
c
[frameworks/native/services/surfaceflinger/Layer.h]
Rect getScreenBounds(bool reduceTransparentRegion = true) const;
3.2.1.2 Layer::getBounds
getBounds 函式做了如下操作
- 拿到 mDrawingState
- 呼叫 getActiveTransparentRegion拿到一個 Rect
- 呼叫有引數的 getBounds,引數就是 getActiveTransparentRegion 的返回值
c
[frameworks/native/services/surfaceflinger/Layer.cpp]
FloatRect Layer::getBounds() const {
const State& s(getDrawingState());
return getBounds(getActiveTransparentRegion(s));
}
3.2.1.3 Layer::getDrawingState
getDrawingState 是一個行內函數,定義在 Layer.h 標頭檔案中,它返回了 Layer 物件中的 mDrawingState,mDrawingState 它是一個 State,這個 State 定義在 Layer.h 這個標頭檔案中,在 Layer 物件中有兩個 State
c
State mCurrentState; // 當前正在使用的 State
State mDrawingState; // 正在繪製的 State
inline const State& getDrawingState() const { return mDrawingState; }
3.2.1.4 Layer::getActiveTransparentRegion
getActiveTransparentRegion 定義在 Layer.h 標頭檔案中,它直接返回了 State 中的 activeTransparentRegion_legacy,activeTransparentRegion_legacy 的字面意思就是遺留的活動透明區域,按照我們之前的理解,透明的區域應該是不需要合成的,所以當前 Layer 的合成區域中,應該會減去這部分割槽域。
c
[frameworks/native/services/surfaceflinger/Layer.h]
virtual Region getActiveTransparentRegion(const Layer::State& s) const {
return s.activeTransparentRegion_legacy;
}
在拿到這個 Region 之後,又呼叫了一個 getBounds 函式,這次傳入的引數就是這個 Region
3.2.1.5 Layer::getBounds(const Region& activeTransparentRegion)
在這次的 getBounds 函式中,執行了我們上述猜測的邏輯,減去了活動透明區域。最後拿到的就是我們要合成的區域。
```c FloatRect Layer::getBounds(const Region& activeTransparentRegion) const { // 減去邊界的透明區域,獲取最後的邊界 return reduce(mBounds, activeTransparentRegion); }
```
3.2.1.6 reduce
reduce 這個函式,就是直接減去傳入引數 win 中的 exclude 區域
c
static FloatRect reduce(const FloatRect& win, const Region& exclude) {
if (CC_LIKELY(exclude.isEmpty())) {
return win;
}
return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect();
}
3.2.1.7 getScreenBounds 總結
getScreenBounds 這個函式就是獲取這個 Layer 的需要合成的區域,它首先會拿到當前繪製的 mDrawingState,然後拿到這個 State 的透明區域,最後再用自己的區域減去這部分透明區域,這個最終區域就是 Layer 要繪製的區域。
瞭解 getScreenBounds 的原理,再回過頭來看[3.2.1]中計算髒區域的邏輯,就很好理解了。
3.2.2 traverseInZOrder
完成了髒區域的計算之後, 還需要做一件事情, 那就是將每個圖層的當前的顯示裝置進行比較, 看看當前的圖層的髒區域, 是否在顯示區域內, 如果中顯示區域內, 那麼才會繪製當前的圖層
因為 Android 是支援多顯示器多, 不同的圖層, 可能在不同的螢幕中, 而每個螢幕的顯示資料並不相同,所以每個 Display 只需要新增自己需要顯示的圖層
```cpp mDrawingState.traverseInZOrder(& { auto compositionLayer = layer->getCompositionLayer(); if (compositionLayer == nullptr) { return; }
// 先拿到顯示裝置的 displayId
const auto displayId = displayDevice->getId();
sp<compositionengine::LayerFE> layerFE = compositionLayer->getLayerFE();
bool needsOutputLayer = false;
// 只有顯示裝置與layerStackId匹配時才需要輸出到顯示裝置中
if (display->belongsInOutput(layer->getLayerStack(),
layer->getPrimaryDisplayOnly())) {
Region drawRegion(tr.transform(
layer->visibleNonTransparentRegion));
// 新增需要繪製的區域
drawRegion.andSelf(bounds);
if (!drawRegion.isEmpty()) {
// 是否需要輸出顯示
needsOutputLayer = true;
}
}
if (needsOutputLayer) {
layersSortedByZ.emplace_back(
display->getOrCreateOutputLayer(displayId, compositionLayer,
layerFE));
// 如果需要輸出顯示, 就新增到 deprecated_layersSortedByZ 中, 後面新增到顯示裝置中
deprecated_layersSortedByZ.add(layer);
auto& outputLayerState = layersSortedByZ.back()->editState();
outputLayerState.visibleRegion =
tr.transform(layer->visibleRegion.intersect(displayState.viewport));
} else if (displayId) {
// 對於從 HWC 顯示中刪除的,並且有入隊幀的 Layer
bool hasExistingOutputLayer =
display->getOutputLayerForLayer(compositionLayer.get()) != nullptr;
bool hasQueuedFrames = std::find(mLayersWithQueuedFrames.cbegin(),
mLayersWithQueuedFrames.cend(),
layer) != mLayersWithQueuedFrames.cend();
if (hasExistingOutputLayer && hasQueuedFrames) {
// 如果滿足以上兩種情況,就將它們新增到 layersNeedingFences 列表中,這個列表就是用來設定 Fence 同步機制的
layersNeedingFences.add(layer);
}
}
}); ```
對於這部分邏輯,首先是判斷髒區域要顯示到那個顯示裝置中,如果髒區域屬於該顯示裝置,就將它新增進去。
對於另外一部分邏輯就不容易讓人理解,它涉及到 hwc 和 Fence 機制,這部分只能後面再看了。
3.3 rebuildLayerStacks 的第三部分
```cpp display->setOutputLayersOrderedByZ(std::move(layersSortedByZ)); // 往顯示裝置中新增需要顯示的圖層 displayDevice->setVisibleLayersSortedByZ(deprecated_layersSortedByZ); // 設定圖層的 Fence 訊號 displayDevice->setLayersNeedingFences(layersNeedingFences);
Region undefinedRegion{bounds}; undefinedRegion.subtractSelf(tr.transform(opaqueRegion));
display->editState().undefinedRegion = undefinedRegion; display->editState().dirtyRegion.orSelf(dirtyRegion); ```
這部分程式碼就是將之前圖層計算的一些結果儲存到顯示裝置中,到這裡,重建圖層髒區域的集合就已經完成了,最後再簡單總結一下
- 圖層髒區域集合的計算首先會按 Z 軸的排序遍歷圖層 Layer
- 對於遍歷中的 Layer 會分別計算它的可見區域和之前所有圖層的不透明區域和被覆蓋的區域
- 當前圖層的 Layer 需要減去之前所有圖層覆蓋的區域和活動中的透明區域等等,最後拿到的區域才是可見區域
- 拿到可見區域後會計算這個可見區域的髒區域,並將它新增到遍歷圖層的總髒區域的集合中,當前的圖層也會新增到所有圖層的覆蓋區域和不透明的區域中,用於計算後面的圖層
- 計算完的需要需要判斷它屬於那個顯示裝置,新增到對應的顯示裝置中
- hwc 處理的需要新增 Fence 同步機制
- 遍歷完成後的結果需要儲存到對應的顯示裝置中
四 calculateWorkingSet
- 這裡首先會遍歷所有的顯示裝置, 然後根據不同的顯示裝置處理對應的圖層
- 對於圖層的處理主要是更新了圖層的合成狀態
- 最後將圖層的資料寫入, setPerFrameData 這個方法不同的圖層有不同的實現, 詳情檢視[Layer 解讀]
```cpp //hwcomposer的設定,將Layer資料更新給HWC void SurfaceFlinger::calculateWorkingSet() {
// mGeometryInvalid 已經在 rebuildLayerStacks 中修改為了 true,見[3.1]
if (CC_UNLIKELY(mGeometryInvalid)) {
mGeometryInvalid = false;
//mDisplays 是一個 map ,定義如下
//std::map<wp<IBinder>, sp<DisplayDevice>> mDisplays;
// 根據不同的顯示裝置做處理
for (const auto& [token, displayDevice] : mDisplays) {
//返回一個 std::shared_ptr<compositionengine::Display>
//compositionengine::Display 是由hardware的顯示裝置組合來的,封裝了 hwc 相關的一些引數
auto display = displayDevice->getCompositionDisplay();
uint32_t zOrder = 0;
// 拿到了不同的顯示裝置, 然後處理每個顯示裝置中的圖層
//將這些 display 中的 layer 按照 Z 軸的順序進行處理
for (auto& layer : display->getOutputLayersOrderedByZ()) {
//這個 layer 是 OutputLayer, 獲取的是 OutputCompositionState
// 首先是拿到圖層的合成狀態
auto& compositionState = layer->editState();
//forceClientComposition 指強制 GPU 合成(Client)
//如果為 true ,將在此輸出上使用客戶端組合,即 OpenGL ES 合成,如果為 false 則為 hwc 合成
compositionState.forceClientComposition = false;
if (!compositionState.hwc || mDebugDisableHWC || mDebugRegion) {
compositionState.forceClientComposition = true;
}
// 設定 Z 軸順序
compositionState.z = zOrder++;
//更新圖層的合成狀態
layer->getLayerFE().latchCompositionState(layer->getLayer().editState().frontEnd,
true);
// 重新計算OutputLayer的幾何狀態
// 比如根據顯示屏全域性矩陣調整該Layer的DisplayFrame、
// 變換視窗裁剪以匹配緩衝區座標系等等。
layer->updateCompositionState(true);
// 將Layer更新完畢的幾何狀態寫入HWC
layer->writeStateToHWC(true);
}
}
}
// 設定每幀資料
for (const auto& [token, displayDevice] : mDisplays) {
auto display = displayDevice->getCompositionDisplay();
const auto displayId = display->getId();
if (!displayId) {
continue;
}
auto* profile = display->getDisplayColorProfile();
//顏色矩陣
if (mDrawingState.colorMatrixChanged) {
display->setColorTransform(mDrawingState.colorMatrix);
}
//色彩模式
Dataspace targetDataspace = Dataspace::UNKNOWN;
if (useColorManagement) {
ColorMode colorMode;
RenderIntent renderIntent;
pickColorMode(displayDevice, &colorMode, &targetDataspace, &renderIntent);
display->setColorMode(colorMode, targetDataspace, renderIntent);
}
//遍歷可見的Layer
for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
//根據layer的資料空間 dataSpace 來設定layer的合成方式
if (layer->isHdrY410()) {
layer->forceClientComposition(displayDevice);
} else if ((layer->getDataSpace() == Dataspace::BT2020_PQ ||
layer->getDataSpace() == Dataspace::BT2020_ITU_PQ) &&
!profile->hasHDR10Support()) {
layer->forceClientComposition(displayDevice);
} else if ((layer->getDataSpace() == Dataspace::BT2020_HLG ||
layer->getDataSpace() == Dataspace::BT2020_ITU_HLG) &&
!profile->hasHLGSupport()) {
layer->forceClientComposition(displayDevice);
}
if (layer->getRoundedCornerState().radius > 0.0f) {
layer->forceClientComposition(displayDevice);
}
if (layer->getForceClientComposition(displayDevice)) {
layer->setCompositionType(displayDevice,
Hwc2::IComposerClient::Composition::CLIENT);
continue;
}
// 設定每幀的資料
const auto& displayState = display->getState();
// 這裡的 setPerFrameData ,對於不同的 Layer 有不同的實現方式,
// 比如 ColorLayer 和 BufferLayer 的實現方式就不同
// 具體的可以檢視 Layer 詳解
layer->setPerFrameData(displayDevice, displayState.transform, displayState.viewport,
displayDevice->getSupportedPerFrameMetadata(),
isHdrColorMode(displayState.colorMode) ? Dataspace::UNKNOWN
: targetDataspace);
}
}
mDrawingState.colorMatrixChanged = false;
for (const auto& [token, displayDevice] : mDisplays) {
auto display = displayDevice->getCompositionDisplay();
for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
auto& layerState = layer->getCompositionLayer()->editState().frontEnd;
// 設定 layerState 的合成方式
layerState.compositionType = static_cast<Hwc2::IComposerClient::Composition>(
layer->getCompositionType(displayDevice));
}
}
} ```
calculateWorkingSet 的函式比較長,這裡就不進行拆分了,還是簡單總結一些
- calculateWorkingSet 中有三個迴圈,他們都是對 mDisplays 這個 map 進行遍歷
- 第一次迴圈是拿到每個 DisplayDevice 的 compositionengine::Display,這裡面封裝了 hwc 的一些引數
- 按照 Z 軸的排序遍歷 compositionengine::Display 裡面的圖層,並且判斷它們是否需要使用客戶端合成,並儲存對應的欄位到 compositionState.forceClientComposition。client 是相對 hwc 而言的,指的是使用 CPU 進行合成,即 OpenGL ES 合成。與之對應的是 hwc 合成,即使用 GPU 進行合成。
- 第二次迴圈是設定顯示裝置的引數和 Layer 的合成引數,並呼叫 setPerFrameData 將對應的資料儲存到 Layer 中
- 第三個迴圈就是遍歷 DisplayDevice 中的 layer,並且將它的合成方式儲存到 layerState.compositionType 欄位
總的來說,calculateWorkingSet 就是判斷 Layer 的合成方式,並設定資料。當然具體到 Layer 的是如何操作,還是需要看 Layer,這部分查詢[解讀Layer]
五 beginFrame 和 prepareFrame
5.1 beginFrame
beginFrame 做了合成前的一些判斷, 判斷是否本次需要重新合成, 那麼什麼情況下本次才需要重新合成呢?
- 首先本次合成要有髒區域, 如果本次連髒區域都沒有了, 那麼自然不需要合成
- 如果有髒區域, 那麼只存在一種情況不需要合成, 其他情況都需要合成
- 不需要合成的情況就是: 本次的可見圖層為空, 且上次合成的時候, 可見圖層也是空
```cpp
void SurfaceFlinger::beginFrame(const sp
// 是否有髒區域
bool dirty = !display->getDirtyRegion(false).isEmpty();
// Z 軸的可見圖層是否為空
bool empty = displayDevice->getVisibleLayersSortedByZ().size() == 0;
//上次合成是否有 Z 軸的可見圖層
bool wasEmpty = !displayState.lastCompositionHadVisibleLayers;
// 如果有髒區域,那麼除非本次可見圖層為空,且上次可見圖層也為空才不會合成
// 其他情況下, 都需要進行合成操作
bool mustRecompose = dirty && !(empty && wasEmpty);
const char flagPrefix[] = {'-', '+'};
static_cast<void>(flagPrefix);
// 傳遞是否需要合成
display->getRenderSurface()->beginFrame(mustRecompose);
if (mustRecompose) {
// 將本次合成是否有可見圖層儲存到 lastCompositionHadVisibleLayers 中
display->editState().lastCompositionHadVisibleLayers = !empty;
}
} ```
這裡呼叫了 display->getRenderSurface()->beginFrame(mustRecompose)
, 這裡 getRenderSurface 拿到的其實是它對應的 Surface
5.2 prepareFrame
```cpp
void SurfaceFlinger::prepareFrame(const sp
if (!displayState.isEnabled) {
return;
}
// 呼叫 FramebufferSurface 的 prepareFrame
status_t result = display->getRenderSurface()->prepareFrame();
}
status_t FramebufferSurface::prepareFrame(CompositionType /compositionType/) { return NO_ERROR; } ```
prepareFrame 函式更加簡單,只是呼叫了 FramebufferSurface 的 prepareFrame 。
最後我們看一下這個 FramebufferSurface 在準備階段做了怎樣的處理
六 FramebufferSurface
出乎意料的是,它居然什麼都沒有做
```cpp status_t FramebufferSurface::beginFrame(bool /mustRecompose/) { return NO_ERROR; }
status_t FramebufferSurface::prepareFrame(CompositionType /compositionType/) { return NO_ERROR; } ```
到此,合成之前的工作基本就已經分析完了,最後再做一個總結。
總結
SurfaceFlinger 合成前做了以下工作
- preComposition 合成前預處理,它其實就是呼叫圖層的 onPreComposition ,不同圖層的具體實現不同,詳情見[Layer 解讀]。
- rebuildLayerStacks 重新計算髒區域,因為 Android 是支援多個顯示裝置的,所以它會根據不同的顯示裝置進行分別處理。其中最主要的就是計算可見區域 computeVisibleRegions 。
- calculateWorkingSet 計算工作區間,根據髒區域來處理圖層,然後 setPerFrameData 設定幀資料,根據不同的圖層它有不同的實現,詳情見[Layer 解讀]。
- 最後的掃尾工作 beginFrame 和 prepareFrame
- 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 圖形系統開篇