[Android禅修之路] SurfaceFlinger 合成中的工作

语言: CN / TW / HK

theme: channing-cyan

SurfaceFlinger 合成中的工作

Android禅修之路

前言

在SurfaceFlinger 经历完合成前的准备之后,接下来的就是具体的合成工作了,合成工作的入口代码就是 doComposition ,接下来看看 SurfaceFlinger 合成中又做了那些事情

一 doComposition

首先合成的方法都是在 doComposition 函数中调用的,这个函数中总共调用了

  1. doDisplayComposition
  2. display->getRenderSurface()->flip()
  3. postFramebuffer

```cpp void SurfaceFlinger::doComposition(const sp& displayDevice, bool repaintEverything) {

// 这几个对象在合成前已经介绍过了,附录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 首先会判断是否需要合成显示,只有两种情况才需要合成显示,其他情况都可以直接跳过合成

  1. 脏区域不为空
  2. 由 hardware composer(后续简称hwc)处理

```cpp void SurfaceFlinger::doDisplayComposition(const sp& displayDevice, const Region& inDirtyRegion) { auto display = displayDevice->getCompositionDisplay(); // 需要实际合成显示的2种情况 // 1) 由 hwc 处理,它可能需要合成来保持其虚拟显示的状态同步 // 2) 脏区域不为空 // displayDevice->getId() 拿到的 Id 就是 hwc 的Id,如果它不为空,就说明需要 hwc 处理 // 也就是说只有 hwc 的 Id 为空,并且脏区域也为空,才会从这里返回 if (!displayDevice->getId() && inDirtyRegion.isEmpty()) { return; }

// 定义一个准备合成的 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& displayDevice, const Region& debugRegion, base::unique_fd* readyFence) {

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& displayDevice, const Region& debugRegion, base::unique_fd* readyFence) { ...... // dequeueBuffer 是从缓冲区队列中获取一个缓冲区 buf = display->getRenderSurface()->dequeueBuffer(&fd); // 第二部分开始 // 首先就是判断拿到的缓冲区是否为空 if (buf == nullptr) { return false; }

    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& displayDevice, const Region& debugRegion, base::unique_fd* readyFence) { // 第三部分开始,如果使用客户端组合,则执行一些清理步骤 if (hasClientComposition) { clientCompositionDisplay.clearRegion = clearRegion;

    // 这里先提高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 的三个部分,这里先做一下小小的总结

  1. 先是处理一些参数,然后调用 RenderSurface 的 dequeueBuffer ,分配缓冲区
  2. 处理 client composition 的相关参数,并计算颜色变换矩阵
  3. 渲染帧缓冲区的图层,首先填充帧缓冲区的图层 Layer prepareClientLayer, 设置一些参数
  4. 渲染图层 Layer,如果是使用客户端合成,就直接调用 GLESRenderEngine 的 drawLayers

补充说明:

缓冲区的分配,在 SurfaceFlinger 的合成过程中,涉及到一个生产者和消费者模型,并且这个模型贯穿了整个流程,详情可见[解读SurfaceFlinger中的生产者和消费者]

关于图层的合成和一些处理操作,因为不同的图层具体的操作也不相同,所以具体的操作参考[Layer详解]

renderEngine 是 SurfaceFlinger 初始化时创建的一个 GLESRenderEngine 对象,所以最后它调用 GLESRenderEngine 的 drawLayers 函数,其实也是通过 OpenGL 完成的。

四 生产者和消费者简介

生产者消费者模型

之前已经说明了在合成中的缓冲区分配,有一个生产者和消费者,这里我们可以简单介绍一下这个模型运行的过程,如上图所示,具体的流程如下

  1. 生产者从 BufferQueue 中申请缓冲区
  2. BufferQueue 从底层拿到一块缓冲区,然后将缓冲区出队,提供给生产者
  3. 生产者将数据写入缓冲区后,将缓冲区入队,然后缓冲区在 BufferQueue 中,等待消费者读取
  4. 消费者读取完缓冲区后,又回到步骤1

现在的 queueBuffer 其实就是步骤3,生产者将缓冲区入队,然后缓冲区在 BufferQueue 中等待消费者读取

缓冲区的入队和缓冲区的出队涉及到的逻辑一样非常多,详情可见解读SurfaceFlinger中的生产者和消费者

五 postFramebuffer

在缓冲区入队之后,还做了一个清除脏数据的操作和一个 flip 函数翻页的操作,不过这个两个函数的逻辑比较简单,这里就不展开了。接下来再来看看 postFramebuffer 函数

```cpp

void SurfaceFlinger::postFramebuffer(const sp& displayDevice) {

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 信号标志,具体什么时候可以用,就需要等待这个信号。所以这里进行了一系列的信号传递。

六 总结

关于合成中的操作,到这就简单的介绍完了,还是进行一个简单的总结

  1. 合成中的函数主要有两个
  2. doDisplayComposition:重绘部分需要重绘的缓冲区
  3. 发送缓冲区数据到显示设备
  4. doDisplayComposition 处理显示的合成逻辑,包含两步
  5. doComposeSurfaces:根据图层的不同类型(Layer 和它的各种子类)还有合成方式(客户端合成还是 hwc 合成)进行不同的处理
  6. queueBuffer:将处理完的缓冲区入队
  7. 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 合成后的处理