[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 图形系统开篇