Android P 图形显示系统(一)硬件合成HWC2

发布时间 2023-08-17 14:50:38作者: Hello-World3

一、简介

1. 我们来看看 SurfaceFlinger 的类定义:

//frameworks/native/services/surfaceflinger/SurfaceFlinger.h
class SurfaceFlinger : public BnSurfaceComposer,
                       public PriorityDumper,
                       private IBinder::DeathRecipient,
                       private HWC2::ComposerCallback
{

SurfaceFlinger 继承 BnSurfaceComposer,实现 ISurfaceComposer 接口。实现 ComposerCallback。PriorityDumper 是一个辅助类,主要提供 SurfaceFlinger 的信息dump,并提供信息的分离和格式设置。


2. ISurfaceComposer 接口实现

ISurfaceComposer 是提供给上层Client端的接口,ISurfaceComposer 接口包括:

//frameworks/native/libs/gui/include/gui/ISurfaceComposer.h
class BnSurfaceComposer: public BnInterface<ISurfaceComposer> {
    public:
    enum {
        // Note: BOOT_FINISHED must remain this value, it is called from Java by ActivityManagerService.
        BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
        CREATE_CONNECTION,
        UNUSED, // formerly CREATE_GRAPHIC_BUFFER_ALLOC
        CREATE_DISPLAY_EVENT_CONNECTION,
        CREATE_DISPLAY,
        DESTROY_DISPLAY,
        GET_BUILT_IN_DISPLAY,
        SET_ACTIVE_COLOR_MODE,
        ...
        ENABLE_VSYNC_INJECTIONS,
        INJECT_VSYNC,
        GET_LAYER_DEBUG_INFO,
        CREATE_SCOPED_CONNECTION
    };
    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
};

ISurfaceComposer 的接口在 SurfaceFlinger 中都有对应的方法实现。Client端通过Binder调到 SurfaceFlinger 中。前面我们已经说过上层怎么获取Display的信息,其实现就是 SurfaceFlinger 中的 getDisplayConfigs 函数。其他的类似,根据上面Binder的command去找对应的实现。


3. ComposerCallback 接口实现

ComposerCallback 是 HWC2 的callback接口,包括以下接口:

//frameworks/native/services/surfaceflinger/DisplayHardware/HWC2.h
class ComposerCallback {
 public:
    virtual void onHotplugReceived(int32_t sequenceId, hwc2_display_t display, Connection connection, bool primaryDisplay) = 0;
    virtual void onRefreshReceived(int32_t sequenceId, hwc2_display_t display) = 0;
    virtual void onVsyncReceived(int32_t sequenceId, hwc2_display_t display,int64_t timestamp) = 0;
    virtual ~ComposerCallback() = default;
};

callback 提供了注册接口,registerCallback,在 SurfaceFlinger 初始化时,注册:

void SurfaceFlinger::init() {
    ...
    LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay, "Starting with vr flinger active is not currently supported.");
    getBE().mHwc.reset(new HWComposer(getBE().mHwcServiceName));
    getBE().mHwc->registerCallback(this, getBE().mComposerSequenceId);

注:Android12没有这个callback.

registerCallback 时的 this 就是 SurfaceFlinger 对 ComposerCallback 接口的实现。

(1) onHotplugReceived

热插拔事件的回调,显示屏幕连接或断开时回调。Surfaceflinger 中实现的方法为 SurfaceFlinger::onHotplugReceived。这块逻辑,我们稍后介绍。

(2) onRefreshReceived

接收底层 HWComposer 的刷新请求,实现方法如下:

void SurfaceFlinger::onRefreshReceived(int sequenceId, hwc2_display_t /*display*/) {
    Mutex::Autolock lock(mStateLock);
    if (sequenceId != getBE().mComposerSequenceId) {
        return;
    }
    repaintEverythingLocked();
}

void SurfaceFlinger::repaintEverythingLocked() {
    android_atomic_or(1, &mRepaintEverything);
    signalTransaction();
}

注:Android12没有 onRefreshReceived 函数名变了。

在 repaintEverythingLocked 中,注意这里的 mRepaintEverything,repaintEverythingLocked 的值为1。signalTransaction 将触发一次刷新,重新进行合成显示。
重新绘制,说明底层配置,参数等有变动,SurfaceFlinger 前面给的数据不能用,得重新根据变动后的配置进行进行合成,给适合当前配置的显示数据。

onVsyncReceived: Vsync事件上报,接收底层硬件上报的垂直同步信号。

需要注意的是,这里收到的热插拔和Vsync回调,又将被封装为事件的形式再通知出去,这是回话,后续介绍。


4. 显示周期Vsync

设备显示会按一定速率刷新,在手机和平板电脑上通常为每秒 60 帧。如果显示内容在刷新期间更新,则会出现撕裂现象;因此,务必只在周期之间更新内容。在可以安全更新内容时,系统便会收到来自显示设备的信号。由于历史原因,我们将该信号称为 VSYNC 信号。

刷新率可能会随时间而变化,例如,一些移动设备的刷新率范围在 58fps 到 62fps 之间,具体要视当前条件而定。对于连接了 HDMI 的电视,刷新率在理论上可以下降到 24Hz 或 48Hz,以便与视频相匹配。由于每个刷新周期只能更新屏幕一次,因此以 200fps 的刷新率为显示设备提交缓冲区只是在做无用功,####### 因为大多数帧永远不会被看到。SurfaceFlinger 不会在应用提交缓冲区时执行操作,而是在显示设备准备好接收新的缓冲区时才会唤醒。

当 VSYNC 信号到达时,SurfaceFlinger 会遍历它的层列表,以寻找新的缓冲区。如果找到新的缓冲区,它会获取该缓冲区;否则,它会继续使用以前获取的缓冲区。##### SurfaceFlinger 总是需要可显示的内容,因此它会保留一个缓冲区。如果在某个层上没有提交缓冲区,则该层会被忽略。


5. 合成方式

目前 SurfaceFlinger 中支持两种合成方式,一种是 Device 合成,一种是 Client 合成。SurfaceFlinger 在收集可见层的所有缓冲区之后,便会询问 Hardware Composer 应如何进行合成。

(1) Client合成

Client合成方式是相对与硬件合成来说的,其合成方式是,将各个 Layer 的内容用GPU渲染到暂存缓冲区中,最后将暂存缓冲区传送到显示硬件。这个暂存缓冲区,我们称为 FBTarget,每个 Display 设备有各自的 FBTarget。Client合成之前称为GLES合成,我们也可以称之为GPU合成。Client合成采用 RenderEngine 进行合成。#########

(2) Device合成

就是用专门的硬件合成器进行合成 HWComposer,所以硬件合成的能力就取决于硬件的实现。其合成方式是将各个 Layer 的数据全部传给显示硬件,并告知它从不同的缓冲区读取屏幕不同部分的数据。HWComposer 是 Devicehec 的抽象。

所以,整个显示系统的数据流如下图所示,此图来源于Androd官网:

GPU合成后数据,作为一个特殊的 Layer,传给显示硬件。


6. dump信息

dump信息是很好的一个调试手段,dump命令:adb shell dumpsys SurfaceFlinger

通过dump,就可以知道当前有那些 Layer,都用什么合成方式

Display 0 HWC layers:
-------------------------------------------------------------------------------
 Layer name
           Z |  Comp Type |   Disp Frame (LTRB) |          Source Crop (LTRB)
-------------------------------------------------------------------------------
 com.android.settings/com.android.settings.Settings#0
       21005 |     Client |    0    0  480  800 |    0.0    0.0  480.0  800.0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 StatusBar#0
      181000 |     Client |    0    0  480   36 |    0.0    0.0  480.0   36.0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

此时,有两个 Layer,采用 Client 合成方式。

(1) 后端 SurfaceFlingerBE

SurfaceFlingerBE 是 Android P 上新分离出来的,没有太多信息,从目前的定义来看是将 SurfaceFlinger 分离为前端和后端,这里的 SurfaceFlingerBE 就是后端,现在的 SurfaceFlinger 充当前端的角色。后端 SurfaceFlingerBE 主要就是和底层合成打交道,前端和上层进行交互。######### 在后续的版本中,更多的逻辑会被移到后端中。

(2) 消息队列和主线程

和应用进程类似,SurfaceFlinger 也有一个主线程,SurfaceFlinger 的主线程主要进行显示数据的处理,也就是合成。SurfaceFlinger 是一个服务,将会响应上层的很多请求,各个进程的请求都在 SurfaceFlinger 的各个Binder线程中处理,####### 如果线程很耗时,那么应用端就会被block,显示也会被block。主线程就是将他们分离开来,各干各的事。

SurfaceFlinger 还有很多逻辑,我们先来看看 SurfaceFlinger 相关的类,接下再来我们一下进行介绍。

其他的都好理解,有两个地方需要注意:

(1) SurfaceFlinger 有两个状态,Layer 也有两个状态,一个 mCurrentState,一个 mDrawingState。注意这里 State 类定义是不一样的。
(2) 两个 EventThread(ps看是composer@2.4-se的辅线程),一个是给 SurfaceFlinger 本身用,一个是为了给应用分发事件的。

void SurfaceFlinger::init() {
    ... ...
    sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync, SurfaceFlinger::vsyncPhaseOffsetNs, true, "app");
    mEventThread = new EventThread(vsyncSrc, *this, false);
    sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync, SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf");
    mSFEventThread = new EventThread(sfVsyncSrc, *this, true);
    mEventQueue.setEventThread(mSFEventThread);

VSyncSources 是 Vsync 的源,并不是每一个 Vsync 信号都是从底层硬件上报的,###### 平时的 Vsync 都是 VSyncSources 分发出来的,VSyncSources 定期会和底层的 Vsync 进行同步,确保和底层屏幕新的步调一致。


二、HWC2 概述

Android 7.0 包含新版本的 HWC (HWC2),Android需要自行配置,到Android 8.0,HWC2正式开启,且版本升级为2.1版本。HWC2是 SurfaceFlinger 用来与专门的窗口合成硬件进行通信。SurfaceFlinger 包含使用 3D 图形处理器 (GPU) 执行窗口合成任务的备用路径,但由于以下几个原因,此路径并不理想:

(1) 通常,GPU 未针对此用例进行过优化,因此能耗可能要大于执行合成所需的能耗。
(2) 每次 SurfaceFlinger 使用 GPU 进行合成时,应用都无法使用处理器进行自我渲染,(?)######### 因此应尽可能使用专门的硬件而不是 GPU 进行合成。

下面是 GPU 和 HWC 两种方式的优劣对比:

所以,HWC的设计最好遵循一些基本的规则~


三、HWC 常规准则

由于 Hardware Composer 抽象层后的物理显示设备硬件可因设备而异,因此很难就具体功能提供建议。一般来说,请遵循以下准则:

(1) HWC 应至少支持 4 个叠加层(状态栏、系统栏、应用和壁纸/背景)。
(2) 层可以大于屏幕,因此 HWC 应能处理大于显示屏的层(例如壁纸)。
(3) 应同时支持预乘每像素 Alpha 混合和每平面 Alpha 混合。
(4) HWC 应能处理 GPU、相机和视频解码器生成的相同缓冲区,因此支持以下某些属性很有帮助:RGBA打包顺序、YUV格式、Tiling, swizzling和步幅属性。
(5) 为了支持受保护的内容,必须提供受保护视频播放的硬件路径。

Tiling,翻译过来就没有原文的意味了,说白了,就是将image进行切割,切成 M x N 的小块,最后用的时候,再将这些小块拼接起来,就像铺瓷砖一样。

swizzling,比Tiling难理解点,它是一种拌和技术,这是向量的单元可以被任意地重排或重复,见过的hwc代码写的都比较隐蔽,没有见多处理的地方。

HWC专注于优化,智能地选择要发送到叠加硬件的 Surface,以最大限度减轻 GPU 的负载。另一种优化是检测屏幕是否正在更新;如果不是,则将合成委托给 OpenGL 而不是 HWC,以节省电量。当屏幕再次更新时,继续将合成分载到 HWC。

为常见用例做准备,如:

(1) 纵向和横向模式下的全屏游戏。
(2) 带有字幕和播放控件的全屏视频。
(3) 主屏幕(合成状态栏、系统栏、应用窗口和动态壁纸)。
(4) 受保护的视频播放。
(5) 多显示设备支持。


四、HWC2 框架

从Android 8.0开始的Treble项目,对Android的架构做了重大的调整,让制造商以更低的成本更轻松、更快速地将设备更新到新版 Android 系统。这就对 HAL 层有了很大的调整,利用提供给Vendor的接口,将Vendor的实现和Android上层分离开来。

这样的架构让HWC的架构也变的复杂,HWC属于 Binderized 的HAL类型。######### Binderized 类型的HAL,将上层Androd和底层HAL分别采用两个不用的进程实现,中间采用Binder进行通信,为了和前面的Binder进行区别,这里采用HwBinder。
因此,我们可以将HWC再进行划分,可以分为下面这几个部分(空间Space),如下图:

(1) Client端

Client也就是 SurfaceFlinger,不过 SurfaceFlinger 采用前后端的设计,以后和HWC相关的逻辑应该都会放到 SurfaceFlinger 后端也就是 SurfaceFlingerBE 中。代码位置:frameworks/native/services/surfaceflinger

(2) HWC2 Client端

这一部分属于 SurfaceFlinger 进程,其直接通过Binder通信,和HWC2的HAL Server交互。这部分的代码也在 SurfaceFlinger 进程中,但是采用Hwc2的命名空间。

(3) HWC Server端

这一部分还是属于Android的系统,这里将建立一个进程,实现HWC的服务端,Server端再调底层Vendor的具体实现。并且,对于底层合成的实现不同,这里会做一些适配,适配HWC1.x,和 FrameBuffer 的实现。这部分包含三部分:接口、实现、服务,###### 以动态库的形式存在:

android.hardware.graphics.composer@2.1.so
android.hardware.graphics.composer@2.1-impl.so //TODO: 为啥叫-impl?
android.hardware.graphics.composer@2.1-service.so

代码位置:hardware/interfaces/graphics/composer/2.1/default //注:目前已经变成2.4了,在2.4目录下

(3) HWC Vendor的实现

这部分是HWC的具体实现,这部分的实现由硬件厂商完成。比如高通平台,代码位置一般为:hardware/qcom/display

需要注意的是,HWC必须采用 Binderized HAL模式,######## 但是,并没有要求一定要实现HWC2的HAL版本。HWC2的实现需要配置,以Android 8.0为例:

a. 添加宏定义 TARGET_USES_HWC2
b. 编译打包HWC2相关的so库
c. SeLinux相关的权限添加
d. 配置manifest.xml

<hal format="hidl">
    <name>android.hardware.graphics.composer</name>
    <transport>hwbinder</transport>
    <version>2.1</version>
    <interface>
        <name>IComposer</name>
        <instance>default</instance>
    </interface>
</hal>

基于上面的划分,我们来看看HWC2相关的类图:

 

五、HWC2 数据结构

HWC2 中提供了几个数据结构来描述合成以及可以显示设备的交互,比如图层(Layer),显示屏(Display)######## 。HWC2的一些常用接口定义在头文件 hwcomposer2.h 中:

hardware/libhardware/include/hardware
    ├── hwcomposer2.h
    ├── hwcomposer_defs.h
    └── hwcomposer.h

hardware/interfaces/graphics/composer/2.1/types.hal

另外一些共用的数据定义是HAL的接口中:

hardware/interfaces/graphics/common/1.0
    ├── Android.bp
    └── types.hal

当然,SurfaceFlinger 中有很多相关的数据结构:frameworks/native/services/surfaceflinger


六、图层Layer

图层(Layer)是合成的最重要单元;每个图层都有一组属性,用于定义它与其他层的交互方式。Layer 在每一层中的代码的实现不一样,基本上 Layer 的理念都是一样的。

SurfaceFlinger 中定义了 Layer,相关的类如下

frameworks/native/services/surfaceflinger
├── Layer.h
├── Layer.cpp
├── ColorLayer.h
├── ColorLayer.cpp
├── BufferLayer.h
└── BufferLayer.cpp

HWC2中定义了HWC2::Layer:

frameworks/native/services/surfaceflinger/DisplayHardware
├── HWC2.h
└── HWC2.cpp

在HAL中实现时,定义为 hwc2_layer_t,这是在头文件 hwcomposer2.h 中定义的

typedef uint64_t hwc2_layer_t;

HIDL中定义为 Layer,这个 Layer 其实和 hwc2_layer_t 是一样的:

typedef uint64_t Layer;

都是 Layer,但是在具体的代码中要具体区分。

1. 类型

Android中的图层按照有没有Buffer分,有两种类型的 Layer:BufferLayer 和 ColorLayer。###### 这也是Android实现中时,采用的方式。

(1) BufferLayer 顾名思义,就是有 Buffer 的 Layer,需要上层应用 Producer 去生产。

(2) ColorLayer 可以绘制一种制定的颜色,和制定的透明度 Alpha。它是取代之前的 Dim Layer 的,可以设置任何的颜色,而不只是黑色。

按照数据格式分,可以分为 RGB Layer,YUV Layer。

(1) RGB Layer

Buffer 是 RGB 格式,比较常见的就是UI界面的数据。

(2) YUV Layer

Buffer 是 YUV 类型的,平常播放Video,Camera预览等,都是YUV类型的。


2. 属性

Layer 的属性定义它与其他层的关系,和显示屏的关系等。###### Layer包括的属性类别如下:

(1) 位置属性

定义层在其显示设备上的显示位置。包括层边缘的位置及其相对于其他层的 Z-Order(指示该层在其他层之前还是之后)等信息。Layer 中为实现这一点,定义了除了 z-order 外,定义了很多个区域 Region:

//frameworks/native/services/surfaceflinger/Layer.h
class Layer : public virtual RefBase {
    ... ...
public:
    ... ...
    // regions below are in window-manager space
    Region visibleRegion;
    Region coveredRegion;
    Region visibleNonTransparentRegion;
    Region surfaceDamageRegion;

Region 中,是很多 Rect 的集合。简言之,一个 Layer 的 visibleRegion可 能是几个 Rect 的集合,其间的关系如下图:

SurfaceFlinger 中定义的 Region 都是上层传下来的,在 WindowManager 空间。####### 而在 HWC 中,用下面的结构描述:

typedef struct hwc_frect {
    float left;
    float top;
    float right;
    float bottom;
} hwc_frect_t;

typedef struct hwc_rect {
    int left;
    int top;
    int right;
    int bottom;
} hwc_rect_t;

typedef struct hwc_region {
    size_t numRects;
    hwc_rect_t const* rects;
} hwc_region_t;

另外,在 SurfaceFlinger 中,还定义了一个重要的结构 Transform。Transform 就是变换矩阵,它是一个3x3的矩阵。相关类的关系如下:

每一个 Layer 的都有两个状态: mCurrentState 和 mDrawingState,mCurrentState 是给 SurfaceFlinger 的前端准备数据,mDrawingState 是给将数据给到合成;每个状态有两个 Geometry 的描述 requested 和 active,requested 是上层请求的,active 是当前正在用的;每个 Geometry 中,有一个 Transform 矩阵,一个 Transform 包含一个 mat33 的整列。

Transform 中,其实包含两部分,一部分是位置 Postion,另外一部分才是真正的2D的变换矩阵。通过下面两个函数设置。

//frameworks/native/services/surfaceflinger/Transform.cpp

void Transform::set(float tx, float ty)
{
    mMatrix[2][0] = tx;
    mMatrix[2][1] = ty;
    mMatrix[2][2] = 1.0f;

    if (isZero(tx) && isZero(ty)) {
        mType &= ~TRANSLATE;
    } else {
        mType |= TRANSLATE;
    }
}

void Transform::set(float a, float b, float c, float d)
{
    mat33& M(mMatrix);
    M[0][0] = a;    M[1][0] = b;
    M[0][1] = c;    M[1][1] = d;
    M[0][2] = 0;    M[1][2] = 0;
    mType = UNKNOWN_TYPE;
}

这两个函数对应 Layer 中的 setPosition 和 setMatrix 函数;这是上层 WindowManager 设置下来的。


(2) 内容属性

定义显示内容如何呈现,显示的内容也就是Buffer。Layer的显示,除了前面说道的几个区域描述,很有很多结构进一步的描述才能最终显示出来。包括诸如剪裁(用来扩展内容的一部分以填充层的边界)和转换(用来显示旋转或翻转的内容)等信息。HWCInfo 结构体中包括了一些这些信息:

//frameworks/native/services/surfaceflinger/Layer.h
struct HWCInfo {
    HWCInfo()
          : hwc(nullptr),
            layer(nullptr),
            forceClientComposition(false),
            compositionType(HWC2::Composition::Invalid),
            clearClientTarget(false) {}

    HWComposer* hwc;
    HWC2::Layer* layer;
    bool forceClientComposition;
    HWC2::Composition compositionType;
    bool clearClientTarget;
    Rect displayFrame;
    FloatRect sourceCrop;
    HWComposerBufferCache bufferCache;
};

怎么理解这里的 sourceCrop 和 displayFrame ? 如果再加上可见区域 visibleRegion 呢? 再加上 damageRegion 呢? 还有 Transform 呢?

我们来个神图,让你一下子就能明白:

看图说话:

1. Layer区域和屏幕区域,就是 Layer 和屏幕本身的大小区域。

2. sourceCrop 剪切区域。
sourceCrop 是对 Layer 进行剪切的,只截取部分 Layer 的内容进行显示;sourceCrop 不超过 Layer 的大小,超过没有意义。

3. displayFrame 显示区域。
displayFrame 表示 Layer 在屏幕上的显示区域,具体说来,是 sourceCrop 区域在显示屏上的显示区域。displayFrame 一般来说,小于屏幕的区域。而 displayFrame 可能比 sourceCrop 大,可能小,这都是正常的,只是需要做缩放,这就是合成时需要处理的。

4. visibleRegion 可见区域。
displayFrame 区域不一定都能看到的,如果存在上层 Layer,那么 displayFrame 区域可能部分或全部被盖住,displayFrame 没有被盖住的部分就是可见区域 visibleRegion。

5. damageRegion 受损区域,或者称之为更新区域。
damageRegion 表示 Layer 内容被破坏的区域,也就是说这部分区域的内容变了,所以这个属性一般是和上一帧相比时才有意义。这算是对合成的一种优化,重新合成时,我们只去合成 damageRegion 区域,其他的可见区域还是用的上一帧的数据。#######

6. visibleNonTransparentRegion 可见非透明区域。
透明区域 transparentRegion 是可见区域 visibleRegion 的一部分,只是这一部分透明的看到的是底层 Layer 的内容。在 SurfaceFlinger 的 Layer 中定义 visibleNonTransparentRegion,表示可见而又不透明的部分。

7. coveredRegion 被覆盖的区域。
表示 Layer 被 TopLayer 覆盖的区域,一看图就很好理解。从图中,你可以简单的认为是 displayFrame 和 TopLayer 区域重合的部分。

注意, 这里之所以说简单的认为,这是因为 HWC 空间的区域大小是 SurfaceFlinger 空间的区域经过缩放,经过 Transform 旋转,移动等后才得出的,要是混淆了就理解不对了。


(3) 合成属性

定义层应如何与其他层合成。包括混合模式和用于 Alpha 合成的全层 Alpha 值等信息。总的说来,合成分为两个大类:GPU合成,HWC合成。根据具体的情况,分为下列几类:

//hardware/libhardware/include/hardware/hwcomposer2.h
enum class Composition : int32_t {
    Invalid = HWC2_COMPOSITION_INVALID,
    Client = HWC2_COMPOSITION_CLIENT,
    Device = HWC2_COMPOSITION_DEVICE,
    SolidColor = HWC2_COMPOSITION_SOLID_COLOR,
    Cursor = HWC2_COMPOSITION_CURSOR,
    Sideband = HWC2_COMPOSITION_SIDEBAND,
};

Client 相对HWC2硬件合成的概念,主要是处理 BufferLayer 数据,用GPU处理。
Device HWC2硬件设备,主要处理 BufferLayer 数据,用HWC处理
SolidColor 固定颜色合成,主要处理 ColorLayer 数据,用HWC处理或GPU处理。
Cursor 鼠标标识合成,主要处理鼠标等图标,用HWC处理或GPU处理
Sideband Sideband为视频的边频带,一般需要需要硬件合成器作特殊处理,但是也可以用GPU处理。

在合成信息 HWCInfo 中,包含成的类型。通过 Layer 的 setCompositionType 方法进行指定:

void Layer::setCompositionType(int32_t hwcId, HWC2::Composition type, bool callIntoHwc) {
    ... ...
    auto& hwcInfo = getBE().mHwcLayers[hwcId];
    auto& hwcLayer = hwcInfo.layer;
    ALOGV("setCompositionType(%" PRIx64 ", %s, %d)", hwcLayer->getId(), to_string(type).c_str(), static_cast<int>(callIntoHwc));
    if (hwcInfo.compositionType != type) {
        ALOGV("    actually setting");
        hwcInfo.compositionType = type;
        if (callIntoHwc) {
            auto error = hwcLayer->setCompositionType(type);
            ... ...
        }
    }
}

callIntoHwc 是否设置到HWC中,默认参数为true。其实确定合成类型分3步,第一步,SurfaceFlinger 制定合成类型,callIntoHwc 这个时候 callIntoHwc 为true,将类型制定给HWC。第二步,HWC根据实际情况,看看 SurfaceFlinger 制定的合成类型能不能执行,如果条件不满足,做出修改;第三步,SurfaceFlinger 根据HWC的修改情况,再做调整,最终确认合成类型,这个时候 callIntoHwc 参数设置为false。#########


(4) 优化属性

提供一些非必须的参数,以供HWC进行合成的优化。包括层的可见区域以及层的哪个部分自上一帧以来已经更新等信息。也就是前面说到的 visibleRegion,damageRegion 等。


七、显示屏Display

显示屏 Display 是合成的另一个重要单元。系统可以具有多个显示设备,并且在正常系统操作期间可以添加或删除显示设备。该添加/删除可以应 HWC 设备的热插拔请求,或者应客户端的请求进行,这允许创建虚拟显示设备,其内容会渲染到离屏缓冲区(而不是物理显示设备)######。
HWC中,SurfaceFlinger 中创建的 Layer,在合成开始时,将被指定到每个 Display 上,此后合成过程中,每个 Display 合成指定给自己的 Layer。

参考前面我们大的类图:
SurfaceFlinger 前端,每个显示屏,用 DisplayDevice 类描述,####### 在后端显示数据用 DisplayData 描述。###### 而在 HWC2 的Client 端,定义了 Display 类进行描述。对于 HWC2 服务端则用 hwc2_display_t 描述,hwc2_display_t 只是一个序号,Vendor具体实现时,才具体的去管理Display的信息。#######
HWC2 提供相应函数来确定给定显示屏的属性,在不同配置(例如 4k 或 1080p 分辨率)和颜色模式(例如native颜色或真彩 sRGB)之间切换,以及打开、关闭显示设备或将其切换到低功率模式(如果支持)。


八、HWC设备 composerDevice

一定要注意显示屏,和合成设备的差别。HWC设备就一个,在头文件中定义如下:

typedef struct hwc2_device {
    struct hw_device_t common;
    void (*getCapabilities)(struct hwc2_device* device, uint32_t* outCount, int32_t* /*hwc2_capability_t*/ outCapabilities);
    hwc2_function_pointer_t (*getFunction)(struct hwc2_device* device, int32_t /*hwc2_function_descriptor_t*/ descriptor);
} hwc2_device_t;

在HWC 的 Client 端,采用 Device 描述,底层采用 hwc2_device_t 描述。整个合成服务都是围绕 hwc2_device_t 展开的。
除了层和显示设备之外,HWC2 还提供对硬件垂直同步 (VSYNC) 信号的控制,以及对于客户端的回调,用于通知它何时发生 vsync 事件。


九、接口指针

HWC2 头文件中,HWC 接口函数由 lowerCamelCase 命名惯例 定义,但是这些函数名称的字段并不实际存在于接口中。相反,几乎每个函数都是通过使用 hwc2_device_t 提供的 getFunction 请求函数指针来进行加载。例如,函数 createLayer 是一个 HWC2_PFN_CREATE_LAYER 类型的函数指针,当枚举值 HWC2_FUNCTION_CREATE_LAYER 传递到 getFunction 中时便会返回该指针。

接口指针定义在 hwcomposer2.h 中,基本上都是以 HWC2_PFN* 开始命名,接口比较多,这里就不贴代码了。而每个接口,都对应一个接口描述 hwc2_function_descriptor_t。hwc2_function_descriptor_t 定义如下,就是一个枚举列表。

//hardware/libhardware/include/hardware/hwcomposer2.h
/* Function descriptors for use with getFunction */
typedef enum {
    HWC2_FUNCTION_INVALID = 0,
    HWC2_FUNCTION_ACCEPT_DISPLAY_CHANGES,
    HWC2_FUNCTION_CREATE_LAYER,
    ...
    HWC2_FUNCTION_SET_POWER_MODE,
    HWC2_FUNCTION_SET_VSYNC_ENABLED,
    HWC2_FUNCTION_VALIDATE_DISPLAY,
} hwc2_function_descriptor_t;

所以,Vendor的HWC2实现,基本都是这样,伪代码:

class VendorComposer2 : public hwc2_device_t {
    ... ...
    static void GetCapabilities(struct hwc2_device *device, uint32_t *outCount, int32_t *outCapabilities);
    static hwc2_function_pointer_t GetFunction(struct hwc2_device *device, int32_t descriptor);
    
    //具体的接口实现
    static int32_t VendorAcceptDisplayChanges(hwc2_device_t *device, hwc2_display_t display);
    static int32_t VendorCreateLayer(hwc2_device_t *device, hwc2_display_t display, hwc2_layer_t *out_layer_id);
    static int32_t VendorCreateVirtualDisplay(hwc2_device_t *device, uint32_t width, uint32_t height, int32_t *format, hwc2_display_t *out_display_id);
        ... ...
}

//GetFunction中
hwc2_function_pointer_t HWCSession::GetFunction(struct hwc2_device *device, int32_t int_descriptor) {
  auto descriptor = static_cast<HWC2::FunctionDescriptor>(int_descriptor);

  switch (descriptor) {
    case HWC2::FunctionDescriptor::AcceptDisplayChanges:
      return AsFP<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>(VendorAcceptDisplayChanges);
    case HWC2::FunctionDescriptor::CreateLayer:
      return AsFP<HWC2_PFN_CREATE_LAYER>(VendorCreateLayer);
    case HWC2::FunctionDescriptor::CreateVirtualDisplay:
      return AsFP<HWC2_PFN_CREATE_VIRTUAL_DISPLAY>(Vendor:CreateVirtualDisplay);
    ... ...

 

十、句柄Handle

这个前面穿插讲过了。Layer,Display 和配置信息 Config 在HWC都是用各自的 Handle 表示的。

typedef uint32_t hwc2_config_t;
typedef uint64_t hwc2_display_t;
typedef uint64_t hwc2_layer_t;

Buffer 也是用 handle 来描述 native_handle_t。

当 SurfaceFlinger 想要创建新层时,它会调用 createLayer 函数,然后返回一个 hwc2_layer_t 类型的句柄,。在此之后,SurfaceFlinger 每次想要修改该层的属性时,都会将该 hwc2_layer_t 值以及进行修改所需的任何其他信息传递给相应的修改函数。hwc2_layer_t 类型句柄的大小足以容纳一个指针或一个索引,并且 SurfaceFlinger 会将其视为不透明,从而为 HWC 实现人员提供最大的灵活性。


十一、HWC合成服务

代码位置:hardware/interfaces/graphics/composer/2.1/default

这个HWC的的默认服务。SurfaceFlinger 初始化时,可以通过属性 debug.sf.hwc_service_name 来制定,默认为default:

//frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
std::string getHwcServiceName() {
    char value[PROPERTY_VALUE_MAX] = {};
    property_get("debug.sf.hwc_service_name", value, "default");
    ALOGI("Using HWComposer service: '%s'", value);
    return std::string(value);
}

在编译时,manifest.xml 中配置的也是default。

1. HWC服务分两部分:

(1) 可以执行程序 android.hardware.graphics.composer@2.1-service

其 main 函数如下,通过 defaultPassthroughServiceImplementation 函数注册 IComposer 服务。

//hardware/interfaces/graphics/composer/2.1/default/service.cpp
int main() {
    // the conventional HAL might start binder services
    android::ProcessState::initWithDriver("/dev/vndbinder");
    android::ProcessState::self()->setThreadPoolMaxThreadCount(4);
    android::ProcessState::self()->startThreadPool();

    // same as SF main thread
    struct sched_param param = {0};
    param.sched_priority = 2; //97
    if (sched_setscheduler(0, SCHED_FIFO | SCHED_RESET_ON_FORK, &param) != 0) {
        ALOGE("Couldn't set SCHED_FIFO: %d", errno);
    }

    return defaultPassthroughServiceImplementation<IComposer>(4);
}

对应的rc文件如下:

service vendor.hwcomposer-2-1 /vendor/bin/hw/android.hardware.graphics.composer@2.1-service
    class hal animation
    user system
    group graphics drmrpc
    capabilities SYS_NICE
    onrestart restart surfaceflinge

(2) 实现库

android.hardware.graphics.composer@2.1-impl.so

hwc 的执行程序中,注册的 IComposer,将调到对应的 FETCH 函数,FETCH 函数实现及是so库中。FETCH如下:

IComposer* HIDL_FETCH_IComposer(const char*)
{
    const hw_module_t* module = nullptr;
    int err = hw_get_module(HWC_HARDWARE_MODULE_ID, &module);
    if (err) {
        ALOGI("falling back to FB HAL");
        err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
    }
    if (err) {
        ALOGE("failed to get hwcomposer or fb module");
        return nullptr;
    }

    return new HwcHal(module);
}

FETCH函数中,才正在去加载Vendor的实现,通过统一的接口 hw_get_module 根据 IDHWC_HARDWARE_MODULE_ID 去加载。加载完成后,创建HAL描述类似 HwcHal。

HwcHal::HwcHal(const hw_module_t* module) : mDevice(nullptr), mDispatch(), mMustValidateDisplay(true), mAdapter() {
    uint32_t majorVersion;
    if (module->id && strcmp(module->id, GRALLOC_HARDWARE_MODULE_ID) == 0) {
        majorVersion = initWithFb(module);
    } else {
        majorVersion = initWithHwc(module);
    }

    initCapabilities();
    if (majorVersion >= 2 && hasCapability(HWC2_CAPABILITY_PRESENT_FENCE_IS_NOT_RELIABLE)) {
        ALOGE("Present fence must be reliable from HWC2 on.");
        abort();
    }

    initDispatch();
}

如果是 FrameBuffer 驱动,通过 initWithFb 初始化。如果是HWC驱动,通过 initWithHwc 初始化。我们需要的是HWC2的接口,如果不是HWC2的HAl实现,那么需要做适配。

FrameBuffer 驱动,采用 HWC2OnFbAdapter 进行适配:

uint32_t HwcHal::initWithFb(const hw_module_t* module)
{
    framebuffer_device_t* fb_device;
    int error = framebuffer_open(module, &fb_device);
    if (error != 0) {
        ALOGE("Failed to open FB device (%s), aborting", strerror(-error));
        abort();
    }

    mFbAdapter = std::make_unique<HWC2OnFbAdapter>(fb_device);
    mDevice = mFbAdapter.get();

    return 0;
}

HWC1.x 通过 HWC2On1Adapter 进行适配:

uint32_t HwcHal::initWithHwc(const hw_module_t* module)
{
    // Determine what kind of module is available (HWC2 vs HWC1.X).
    hw_device_t* device = nullptr;
    int error = module->methods->open(module, HWC_HARDWARE_COMPOSER, &device);
    ... ...
    uint32_t majorVersion = (device->version >> 24) & 0xF;

    // If we don't have a HWC2, we need to wrap whatever we have in an adapter.
    if (majorVersion != 2) {
        uint32_t minorVersion = device->version & HARDWARE_API_VERSION_2_MAJ_MIN_MASK;
        minorVersion = (minorVersion >> 16) & 0xF;
        ALOGI("Found HWC implementation v%d.%d", majorVersion, minorVersion);
        if (minorVersion < 1) {
            ALOGE("Cannot adapt to HWC version %d.%d. Minimum supported is 1.1", majorVersion, minorVersion);
            abort();
        }
        mAdapter = std::make_unique<HWC2On1Adapter>(reinterpret_cast<hwc_composer_device_1*>(device));

        // Place the adapter in front of the device module.
        mDevice = mAdapter.get();
    } else {
        mDevice = reinterpret_cast<hwc2_device_t*>(device);
    }

    return majorVersion;
}

 这两种适配的实现代码如下:

frameworks/native/libs
├── hwc2onfbadapter
└── hwc2on1adapter

分别打包成两个动态so库,Adapter 中再调具体的 Vendor 实现。

初始化时,第一步先获取 Vendor 实现的处理能力:

void HwcHal::initCapabilities()
{
    uint32_t count = 0;
    mDevice->getCapabilities(mDevice, &count, nullptr);

    std::vector<int32_t> caps(count);
    mDevice->getCapabilities(mDevice, &count, caps.data());
    caps.resize(count);

    mCapabilities.reserve(count);
    for (auto cap : caps) {
        mCapabilities.insert(static_cast<hwc2_capability_t>(cap));
    }
}

第二步,初始化,HWC的接口函数:

void HwcHal::initDispatch()
{
    initDispatch(HWC2_FUNCTION_ACCEPT_DISPLAY_CHANGES, &mDispatch.acceptDisplayChanges);
    initDispatch(HWC2_FUNCTION_CREATE_LAYER, &mDispatch.createLayer);
    initDispatch(HWC2_FUNCTION_CREATE_VIRTUAL_DISPLAY, &mDispatch.createVirtualDisplay);
    ... ...

根据ID,同过Vendor实现的 getFunction 函数,去获取 Vendor 对应的函数地址。

template<typename T>
void HwcHal::initDispatch(hwc2_function_descriptor_t desc, T* outPfn)
{
    auto pfn = mDevice->getFunction(mDevice, desc);
    if (!pfn) {
        LOG_ALWAYS_FATAL("failed to get hwcomposer2 function %d", desc);
    }
    *outPfn = reinterpret_cast<T>(pfn);
}

 

十二、Client和Server的通信

SurfaceFlinger 和HWC服务之间,很多函数,并没有直接的调用,而是通过 Buffer 的读写来实现调用和参数的传递的。所以,Client 端和 Server 端通信,基本通过以下相关的途径:

(1) 通过 IComposerClient.hal 接口
(2) 通过 IComposer.hal 接口
(3) 通过 command Buffer

Server 端回调 Client 端,通过 IComposerCallback.hal 接口。

hal接口的方式,比较好理解,其本质就是Binder。那么问题来了,既然有hal的接口,为什么又加了一个 command Buffer 的方式呢?其实这是为了解决Binder通信慢的问题。

我们先来看看 IComposerClient.hal 接口

 

1. IComposerClient.hal 接口

IComposerClient.hal的接口如下:

//hardware/interfaces/graphics/composer/2.1/IComposerClient.hal

    registerCallback(IComposerCallback callback);

    getMaxVirtualDisplayCount() generates (uint32_t count);

    createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat formatHint, uint32_t outputBufferSlotCount)
              generates (Error error, Display display, PixelFormat format);

    destroyVirtualDisplay(Display display) generates (Error error);

    createLayer(Display display, uint32_t bufferSlotCount)
     generates (Error error, Layer layer);

    destroyLayer(Display display, Layer layer) generates (Error error);

    getActiveConfig(Display display) generates (Error error, Config config);

    getClientTargetSupport(Display display, uint32_t width, uint32_t height, ixelFormat format, Dataspace dataspace)
                generates (Error error);

    getColorModes(Display display)
       generates (Error error, ec<ColorMode> modes);

    getDisplayAttribute(Display display, Config config, Attribute attribute)
             generates (Error error, int32_t value);

    getDisplayConfigs(Display display)
           generates (Error error, vec<Config> configs);

    getDisplayName(Display display) generates (Error error, string name);

    getDisplayType(Display display) generates (Error error, DisplayType type);

    getDozeSupport(Display display) generates (Error error, bool support);

    getHdrCapabilities(Display display)
            generates (Error error, vec<Hdr> types, float maxLuminance, float maxAverageLuminance, float minLuminance);

    setClientTargetSlotCount(Display display, uint32_t clientTargetSlotCount)
                  generates (Error error);

    setActiveConfig(Display display, Config config) generates (Error error);

    setColorMode(Display display, ColorMode mode) generates (Error error);

    setPowerMode(Display display, PowerMode mode) generates (Error error);

    setVsyncEnabled(Display display, Vsync enabled) generates (Error error);

    setInputCommandQueue(fmq_sync<uint32_t> descriptor)
              generates (Error error);

    getOutputCommandQueue()
              generates (Error error, fmq_sync<uint32_t> descriptor);

    executeCommands(uint32_t inLength, vec<handle> inHandles)
         generates (Error error, bool outQueueChanged, uint32_t outLength, vec<handle> outHandles);

IComposerClient.hal 的接口函数比较多,这里提供的接口,都是一些实时的接口,也就是Client需要Server立即响应的接口。


2. IComposer.hal接口

IComposer.hal 就3个接口函数,createClient 主要的是这里的 createClient 函数,创建一个 ComposerClient,通过 ComposerClient,就可以用上面的 IComposerClient.hal 接口。

//hardware/interfaces/graphics/composer/2.1/IComposer.hal
    getCapabilities() generates (vec<Capability> capabilities);
    dumpDebugInfo() generates (string debugInfo);
    createClient() generates (Error error, IComposerClient client);

IComposer.hal 接口也是实时接口。


3. IComposerCallback.hal接口

IComposerCallback.hal 接口是Server回调给Client端的,主要是下面3个接口:

    onHotplug(Display display, Connection connected);
    oneway onRefresh(Display display);
    oneway onVsync(Display display, int64_t timestamp);

三个接口分别上报热插拔,刷新请求,和Vsync事件。另外,IComposerCallback 接口需要通过 IComposerClient.hal 的 registerCallback 函数进行注册。


4. command Buffer

相比前面的hal接口,这里的Buffer方式的调用都不是实时的,先将命令写到Buffer中,最后再一次性的调用到Server。
相关的定义在头文件 IComposerCommandBuffer.h 中,定义了一个 CommandWriterBase 和一个 CommandReaderBase

//hardware/interfaces/graphics/composer/2.1/default/IComposerCommandBuffer.h

class CommandWriterBase {
    ... ...
    uint32_t mDataMaxSize;
    std::unique_ptr<uint32_t[]> mData;

    uint32_t mDataWritten;
    // end offset of the current command
    uint32_t mCommandEnd;

    std::vector<hidl_handle> mDataHandles;
    std::vector<native_handle_t *> mTemporaryHandles;

    std::unique_ptr<CommandQueueType> mQueue;
};

class CommandReaderBase {
    ... ...
    std::unique_ptr<CommandQueueType> mQueue;
    uint32_t mDataMaxSize;
    std::unique_ptr<uint32_t[]> mData;

    uint32_t mDataSize;
    uint32_t mDataRead;

    // begin/end offsets of the current command
    uint32_t mCommandBegin;
    uint32_t mCommandEnd;

    hidl_vec<hidl_handle> mDataHandles;
};

都是用一个uint32_t的数组来保存数据 mData。Client端和Server端基于两个Base类,又继承Base,重写各自的Reader和Writer。

相关的类图如下:

相关的command命令,定义在 IComposerClient.hal 中

//hardware/interfaces/graphics/composer/2.1/IComposerClient.hal
enum Command : int32_t {
        LENGTH_MASK                        = 0xffff,
        OPCODE_SHIFT                       = 16,
        OPCODE_MASK                        = 0xffff << OPCODE_SHIFT,

        /** special commands */
        SELECT_DISPLAY                     = 0x000 << OPCODE_SHIFT,
        SELECT_LAYER                       = 0x001 << OPCODE_SHIFT,
        ... ...

我们以 setZOrder 函数为例,对应的command为 SET_LAYER_Z_ORDER。

SET_LAYER_Z_ORDER                  = 0x40a << OPCODE_SHIFT,

对应的函数为:

Error Composer::setLayerZOrder(Display display, Layer layer, uint32_t z)
{
    mWriter.selectDisplay(display);
    mWriter.selectLayer(layer);
    mWriter.setLayerZOrder(z);
    return Error::NONE;
}

setZOrder时:

1.首先指定Display

//hardware/interfaces/graphics/composer/2.1/default/IComposerCommandBuffer.h

    static constexpr uint16_t kSelectDisplayLength = 2;
    void selectDisplay(Display display)
    {
        beginCommand(IComposerClient::Command::SELECT_DISPLAY, kSelectDisplayLength);
        write64(display);
        endCommand();
    }

指定Display也是通过一个命令来完成,传递一个命令具体分为3步:

(1) beginCommand

beginCommand 先写命令值,SELECT_DISPLAY

void beginCommand(IComposerClient::Command command, uint16_t length)
{
    if (mCommandEnd) {
        LOG_FATAL("endCommand was not called before command 0x%x", command);
    }

    growData(1 + length);
    write(static_cast<uint32_t>(command) | length);

    mCommandEnd = mDataWritten + length;
}

beginCommand 时,先增加Buffer的大小,Buffer采用一个uint32_t类型的数组。

std::unique_ptr<uint32_t[]> mData;

再将command和长度或后,写入Buffer。最后记录,Buffer应该结束的位置 mCommandEnd。这里的+1是为了写command命令,是command命令的长度。

(2) 写具体的值

display是64bit的,所以直接用write64的接口;将被拆分为两个32位,用32位的接口write写入Buffer。写入后,mDataWritten相应的增加。

void write(uint32_t val)
{
    mData[mDataWritten++] = val;
}

(3) endCommand

函数如下:

void endCommand()
{
    if (!mCommandEnd) {
        LOG_FATAL("beginCommand was not called");
    } else if (mDataWritten > mCommandEnd) {
        LOG_FATAL("too much data written");
        mDataWritten = mCommandEnd;
    } else if (mDataWritten < mCommandEnd) {
        LOG_FATAL("too little data written");
        while (mDataWritten < mCommandEnd) {
            write(0);
        }
    }
    mCommandEnd = 0;
}

endCommand 中主要是看我们写的数据对不对,如果写的太多,超过 mCommandEnd 的截掉。如果写的太少,补0。


2. 指定Layer

通过 selectLayer 函数指定layer;selectLayer 函数和前面的 selectDisplay 类似:

static constexpr uint16_t kSelectLayerLength = 2;
void selectLayer(Layer layer)
{
    beginCommand(IComposerClient::Command::SELECT_LAYER, kSelectLayerLength);
    write64(layer);
    endCommand();
}

 

3. 将要设置的数据写入Buffer

static constexpr uint16_t kSetLayerZOrderLength = 1;
void setLayerZOrder(uint32_t z)
{
    beginCommand(IComposerClient::Command::SET_LAYER_Z_ORDER, kSetLayerZOrderLength);
    write(z);
    endCommand();
}

设置 z-order 时,用的 SET_LAYER_Z_ORDER 命令。z-order是32bit的类型,这里直接用的write函数。

到此,z-order写到Buffer mData中。注意,只是写到了mData中,还没有传到HWC的服务端呢。


4. 执行命令

Buffer中的数据什么时候才传到HWC的服务端呢? 答案是 executeCommands 的时候。execute 基本就是 validate 和 present 处理的时候会先调,execute 将Buffer命令真正是传到 Server 进行解析,调相应的接口。execute 主要是通过 IComposerClient.hal 的实时接口来完成。
execute 函数如下:

Error Composer::execute()
{
    // 准备command队列
    bool queueChanged = false;
    uint32_t commandLength = 0;
    hidl_vec<hidl_handle> commandHandles;
    if (!mWriter.writeQueue(&queueChanged, &commandLength, &commandHandles)) {
        mWriter.reset();
        return Error::NO_RESOURCES;
    }

    // set up new input command queue if necessary
    if (queueChanged) {
        auto ret = mClient->setInputCommandQueue(*mWriter.getMQDescriptor());
        auto error = unwrapRet(ret);
        if (error != Error::NONE) {
            mWriter.reset();
            return error;
        }
    }

    if (commandLength == 0) {
        mWriter.reset();
        return Error::NONE;
    }

    Error error = kDefaultError;
    auto ret = mClient->executeCommands(commandLength, commandHandles,
            [&](const auto& tmpError, const auto& tmpOutChanged, const auto& tmpOutLength, const auto& tmpOutHandles)
            {
                ... ...
                if (error == Error::NONE && tmpOutChanged) {
                    error = kDefaultError;
                    mClient->getOutputCommandQueue([&](const auto& tmpError, const auto& tmpDescriptor)
                            {
                                ... ...
                                mReader.setMQDescriptor(tmpDescriptor);
                            });
                }
                ... ...
                if (mReader.readQueue(tmpOutLength, tmpOutHandles)) {
                   ... ...
            });
    ... ...
    mWriter.reset();
    return error;
}

execute主要做了下面几件事:

(1) 准备命令队列 commandQueue

通过Writer的writeQueue来实现,其作用就是将前面我们已经写到Buffer中的command及数据,写到命令队列mQueue中。代码如下:

bool writeQueue(bool* outQueueChanged, uint32_t* outCommandLength, hidl_vec<hidl_handle>* outCommandHandles)
{
    ... ...
    } else {
        auto newQueue = std::make_unique<CommandQueueType>(mDataMaxSize);
        if (!newQueue->isValid() || !newQueue->write(mData.get(), mDataWritten)) {
            ALOGE("failed to prepare a new message queue ");
            return false;
        }

        mQueue = std::move(newQueue);
        *outQueueChanged = true;
    }

    *outCommandLength = mDataWritten;
    outCommandHandles->setToExternal(const_cast<hidl_handle*>(mDataHandles.data()), mDataHandles.size());

    return true;
}


(2) 设置新的命令队列

setInputCommandQueue传递的是命令队列的文件描述符,Server端Reader根据队列的文件描述符去找对应的队列。

Return<Error> ComposerClient::setInputCommandQueue(const MQDescriptorSync<uint32_t>& descriptor)
{
    std::lock_guard<std::mutex> lock(mCommandMutex);
    return mReader->setMQDescriptor(descriptor) ? Error::NONE : Error::NO_RESOURCES;
}

setMQDescriptor 时Reader会根据描述符创建对应的命令队形。

(3) 执行命令队列

executeCommands 在server端的实现为:

Return<void> ComposerClient::executeCommands(uint32_t inLength, const hidl_vec<hidl_handle>& inHandles,
        executeCommands_cb hidl_cb)
{
    std::lock_guard<std::mutex> lock(mCommandMutex);

    bool outChanged = false;
    uint32_t outLength = 0;
    hidl_vec<hidl_handle> outHandles;

    if (!mReader->readQueue(inLength, inHandles)) {
        hidl_cb(Error::BAD_PARAMETER, outChanged, outLength, outHandles);
        return Void();
    }

    Error err = mReader->parse();
    if (err == Error::NONE && !mWriter.writeQueue(&outChanged, &outLength, &outHandles)) {
        err = Error::NO_RESOURCES;
    }

    hidl_cb(err, outChanged, outLength, outHandles);

    mReader->reset();
    mWriter.reset();

    return Void();
}

server 端的 Reader 读取命令队列,将命令,数据等从 mQueue 中又读到 Reader 的 Buffer mData 中。读到 Mdata 中后,再解析 parse。

Error ComposerClient::CommandReader::parse()
{
    IComposerClient::Command command;
    uint16_t length = 0;

    while (!isEmpty()) {
        if (!beginCommand(&command, &length)) {
            break;
        }

        bool parsed = parseCommand(command, length);
        endCommand();

        if (!parsed) {
            ALOGE("failed to parse command 0x%x, length %" PRIu16, command, length);
            break;
        }
    }

    return (isEmpty()) ? Error::NONE : Error::BAD_PARAMETER;
}

解析命令,也分3步:
beginCommand 读取命令,看看是什么命令;
parseCommand 解析命令,根据命令,解析具体的数据。比如我们设置z-order的命令处理如下:

bool ComposerClient::CommandReader::parseSetLayerZOrder(uint16_t length)
{
    if (length != CommandWriterBase::kSetLayerZOrderLength) {
        return false;
    }

    auto err = mHal.setLayerZOrder(mDisplay, mLayer, read());
    if (err != Error::NONE) {
        mWriter.setError(getCommandLoc(), err);
    }

    return true;
}

parseCommand 时数据才真正传递到Server中,生效。z-order 通过 mHal 的 setLayerZOrder 设置到 Vendor 的HAL实现中。
endCommand 表示数据读取完,记录读取的位置。
回到 Client 端 execute 函数。Client 端的 Reader 也会读取返回值,


十三、HWC2 中Fence的更改

HWC 2.0 中同步栅栏的含义相对于以前版本的 HAL 已有很大的改变。

在 HWC v1.x 中,释放 Fence 和退出 Fence 是推测性的。在帧 N 中检索到的 Buffer 的释放 Fence 或显示设备的退出 Fence 不会先于在帧 N + 1 中检索到的 Fence 变为触发状态。换句话说,该 Fence 的含义是“不再需要您为帧 N 提供的Buffer内容”。这是推测性的,因为在理论上,SurfaceFlinger 在帧 N 之后的一段不确定的时间内可能无法再次运行,这将使得这些栅栏在该时间段内不会变为触发状态。

在 HWC 2.0 中,释放 Fence 和退出 Fence 是非推测性的。在帧 N 中检索到的释放 Fence 或退出 Fence,将在相关 Buffer 的内容替换帧 N - 1 中缓冲区的内容后立即变为触发状态,或者换句话说,该 Fence 的含义是“您为帧 N 提供的缓冲区内容现在已经替代以前的内容”。这是非推测性的,因为在硬件呈现此帧的内容之后,该栅栏应该在 presentDisplay 被调用后立即变为触发状态。

 

 

 


参考:
Android P 图形显示系统(一)硬件合成HWC2:https://www.jianshu.com/p/824a9ddf68b9%20 //有系列文章