神刀安全网

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

硬件合成HWC2

Hardware Composer HAL (HWC) 是 SurfaceFlinger 用来将 Surface 合成到屏幕。HWC 可以抽象出叠加层和 2D 位块传送器等,其主要是帮忙GPU完成一些工作。

SurfaceFlinger是一个系统服务,其作用是接受来自多个源的Buffer数据,对它们进行合成,然后发送到显示设备进行显示。在之前的Android版本中,显示基本都是基于硬件的FrameBuffer来实现的,例如/dev/graphics/fb0,但是在后来的版本中,实现可以多样化了,比如高通采用SDM,其他有些平台采用ADF,DRM等。

SurfaceFlinger和HWC的相关配合,实现Android系统的合成与显示~

SurfaceFlinger概述

大多数应用通常在屏幕上有三个层:屏幕顶部的状态栏、底部或侧面的导航栏以及应用的界面。有些应用会拥有更多或更少的层(例如,默认主屏幕应用有一个单独的壁纸层,而全屏游戏可能会隐藏状态栏)。每个层都可以单独更新。状态栏和导航栏由系统进程渲染,而应用层由应用渲染,两者之间不进行协调。

下面我们通过一张图,来进行说明:

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

Android显示系统

从上图,我们可以看出,在长按Power键,弹出关机对话框时,有4层Layer,可以立即为有4个窗口。4个窗口经过SurfaceFlinger进行合成后,再送到显示器进行显示。

我们来看看SurfaceFlinger的类定义:

class SurfaceFlinger : public BnSurfaceComposer,                        public PriorityDumper,                        private IBinder::DeathRecipient,                        private HWC2::ComposerCallback { 

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

  • ** ISurfaceComposer接口实现**
    ISurfaceComposer是提供给上层Client端的接口,ISurfaceComposer接口包括:
* frameworks/native/libs/gui/include/gui/ISurfaceComposer.h      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_TRANSACTION_STATE,         AUTHENTICATE_SURFACE,         GET_SUPPORTED_FRAME_TIMESTAMPS,         GET_DISPLAY_CONFIGS,         GET_ACTIVE_CONFIG,         SET_ACTIVE_CONFIG,         CONNECT_DISPLAY,         CAPTURE_SCREEN,         CAPTURE_LAYERS,         CLEAR_ANIMATION_FRAME_STATS,         GET_ANIMATION_FRAME_STATS,         SET_POWER_MODE,         GET_DISPLAY_STATS,         GET_HDR_CAPABILITIES,         GET_DISPLAY_COLOR_MODES,         GET_ACTIVE_COLOR_MODE,         SET_ACTIVE_COLOR_MODE,         ENABLE_VSYNC_INJECTIONS,         INJECT_VSYNC,         GET_LAYER_DEBUG_INFO,         CREATE_SCOPED_CONNECTION     }; 

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

  • ComposerCallback接口实现
    ComposerCallback是HWC2的callback接口,包括以下接口:
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); 

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

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

  • 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(); } 

在repaintEverythingLocked中,注意这里的mRepaintEverything,repaintEverythingLocked的值为1。signalTransaction将触发一次刷新,重新进行合成显示。

重新绘制,说明底层配置,参数等有变动,SurfaceFlinger前面给的数据不能用,得重新根据变动后的配置进行进行合成,给适合当前配置的显示数据。

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

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

  • 显示周期Vsync

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

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

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

  • 合成方式

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

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

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

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

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

Android显示系统数据流图

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

  • 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合成方式。

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

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

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

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

SurfaceFlinger相关类图

其他的都好理解,有两个地方需要注意:
1.SurfaceFlinger有两个状态,Layer也有两个状态,一个mCurrentState,一个mDrawingState。注意这里State类定义是不一样的。
2.两个EventThread,一个是给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) 执行窗口合成任务的备用路径,但由于以下几个原因,此路径并不理想:

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

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

合成类型 耗电情况 性能情况 Alpha处理 DRM内容处理 其他限制
Device合成(HWC) 耗电高 性能高 很多Vendor的HWC不支持Alpha的处理和合成 基本都能访问DRM内容 能合成的Surface层数有限,对每种Surface类型处理层数有限
Client合成(GPU) 耗电低 性能低 能处理每个像素的Alpha及每个Layear的Alpha 早期版本GPU不能访问DRM的内容 目前的处理层数没有限制

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

HWC 常规准则

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

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

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

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

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

为常见用例做准备,如:

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

HWC2 框架

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

这样的架构让HWC的架构也变的复杂,HWC属于Binderized的HAL类型。Binderized类型的HAL,将上层Androd和底层HAL分别采用两个不用的进程实现,中间采用Binder进行通信,为了和前面的Binder进行区别,这里采用HwBinder。

因此,我们可以将HWC再进行划分,可以分为下面这几个部分(空间Space),如下图:

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

HWC2实现

  • Client端
    Client也就是SurfaceFlinger,不过SurfaceFlinger采用前后端的设计,以后和HWC相关的逻辑应该都会放到SurfaceFlinger后端也就是SurfaceFlingerBE中。代码位置:
frameworks/native/services/surfaceflinger 
  • HWC2 Client端
    这一部分属于SurfaceFlinger进程,其直接通过Binder通信,和HWC2的HAL Server交互。这部分的代码也在SurfaceFlinger进程中,但是采用Hwc2的命名空间。

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

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

代码位置:

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

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

  1. 添加宏定义 TARGET_USES_HWC2
  2. 编译打包HWC2相关的so库
  3. SeLinux相关的权限添加
  4. 配置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相关的类图:

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

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在每一层中的代码的实现不一样,基本上Laye的理念都是一样的。

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,但是在具体的代码中要具体区分。

类型

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

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

图层Layer类型

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

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

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

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

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

属性

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

  • 位置属性
    定义层在其显示设备上的显示位置。包括层边缘的位置及其相对于其他层的 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的集合,其间的关系如下图:

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

图层Layer区域

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就是变换矩阵,它是一个3×3的矩阵。相关类的关系如下:

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

Transform类图

每一个Layer的都有两个状态:mCurrentStatemDrawingState,mCurrentState是给SurfaceFlinger的前端准备数据,mDrawingState是给将数据给到合成;每个状态有两个Geometry的描述requestedactive,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设置下来的。

  • 内容属性。定义显示内容如何呈现,显示的内容也就是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;     }; 

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

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

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

Android显示区域间的关系

看图说话:

  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旋转,移动等后才得出的,要是混淆了就理解不对了。

  • 合成属性。定义层应如何与其他层合成。包括混合模式和用于 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, }; 
  1. Client 相对HWC2硬件合成的概念,主要是处理BufferLayer数据,用GPU处理。
  2. Device HWC2硬件设备,主要处理BufferLayer数据,用HWC处理
  3. SolidColor 固定颜色合成,主要处理ColorLayer数据,用HWC处理或GPU处理。
  4. Cursor 鼠标标识合成,主要处理鼠标等图标,用HWC处理或GPU处理
  5. 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。

  • 优化属性。提供一些非必须的参数,以供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定义如下,就是一个枚举列表。

/* Function descriptors for use with getFunction */ typedef enum {     HWC2_FUNCTION_INVALID = 0,     HWC2_FUNCTION_ACCEPT_DISPLAY_CHANGES,     HWC2_FUNCTION_CREATE_LAYER,     HWC2_FUNCTION_CREATE_VIRTUAL_DISPLAY,     HWC2_FUNCTION_DESTROY_LAYER,     HWC2_FUNCTION_DESTROY_VIRTUAL_DISPLAY,     HWC2_FUNCTION_DUMP,     HWC2_FUNCTION_GET_ACTIVE_CONFIG,     HWC2_FUNCTION_GET_CHANGED_COMPOSITION_TYPES,     HWC2_FUNCTION_GET_CLIENT_TARGET_SUPPORT,     HWC2_FUNCTION_GET_COLOR_MODES,     HWC2_FUNCTION_GET_DISPLAY_ATTRIBUTE,     HWC2_FUNCTION_GET_DISPLAY_CONFIGS,     HWC2_FUNCTION_GET_DISPLAY_NAME,     HWC2_FUNCTION_GET_DISPLAY_REQUESTS,     HWC2_FUNCTION_GET_DISPLAY_TYPE,     HWC2_FUNCTION_GET_DOZE_SUPPORT,     HWC2_FUNCTION_GET_HDR_CAPABILITIES,     HWC2_FUNCTION_GET_MAX_VIRTUAL_DISPLAY_COUNT,     HWC2_FUNCTION_GET_RELEASE_FENCES,     HWC2_FUNCTION_PRESENT_DISPLAY,     HWC2_FUNCTION_REGISTER_CALLBACK,     HWC2_FUNCTION_SET_ACTIVE_CONFIG,     HWC2_FUNCTION_SET_CLIENT_TARGET,     HWC2_FUNCTION_SET_COLOR_MODE,     HWC2_FUNCTION_SET_COLOR_TRANSFORM,     HWC2_FUNCTION_SET_CURSOR_POSITION,     HWC2_FUNCTION_SET_LAYER_BLEND_MODE,     HWC2_FUNCTION_SET_LAYER_BUFFER,     HWC2_FUNCTION_SET_LAYER_COLOR,     HWC2_FUNCTION_SET_LAYER_COMPOSITION_TYPE,     HWC2_FUNCTION_SET_LAYER_DATASPACE,     HWC2_FUNCTION_SET_LAYER_DISPLAY_FRAME,     HWC2_FUNCTION_SET_LAYER_PLANE_ALPHA,     HWC2_FUNCTION_SET_LAYER_SIDEBAND_STREAM,     HWC2_FUNCTION_SET_LAYER_SOURCE_CROP,     HWC2_FUNCTION_SET_LAYER_SURFACE_DAMAGE,     HWC2_FUNCTION_SET_LAYER_TRANSFORM,     HWC2_FUNCTION_SET_LAYER_VISIBLE_REGION,     HWC2_FUNCTION_SET_LAYER_Z_ORDER,     HWC2_FUNCTION_SET_OUTPUT_BUFFER,     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。

HWC服务分两部分:

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;     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 
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端通信,基本通过以下相关的途径:

  • 通过IComposerClient.hal接口
  • 通过IComposer.hal接口
  • 通过command Buffer

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

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

我们先来看看IComposerClient.hal接口

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,                            PixelFormat format,                            Dataspace dataspace)                 generates (Error error);      getColorModes(Display display)        generates (Error error,                   vec<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立即响应的接口。

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接口也是实时接口。

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函数进行注册。

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。

相关的类图如下:

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

命令读写器

相关的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步:

  • 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命令的长度。

  • 写具体的值
    display是64bit的,所以直接用write64的接口;将被拆分为两个32位,用32位的接口write写入Buffer。写入后,mDataWritten相应的增加。
    void write(uint32_t val)     {         mData[mDataWritten++] = val;     } 
  • 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主要做了下面几件事:

  • 准备命令队列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;     } 
  • 设置新的命令队列
    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会根据描述符创建对应的命令队形。

  • 执行命令队列
    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 被调用后立即变为触发状态。

小结

这里主要是总结性的介绍一下HWC2,很多流程,稍后我们在代码中具体来分析。

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » Android P 图形显示系统(一)硬件合成HWC2

分享到:更多 ()