HWC概述

HWC(hwcomposer)是Android中进行窗口(Layer)合成和显示的HAL层模块,其实现是特定于设备的,而且通常由显示设备制造商 (OEM)完成,为SurfaceFlinger服务提供硬件支持。

SurfaceFlinger可以使用OpenGL ES合成Layer,这需要占用并消耗GPU资源。大多数GPU都没有针对图层合成进行优化,当SurfaceFlinger通过GPU合成图层时,应用程序无法使用GPU进行自己的渲染。而HWC通过硬件设备进行图层合成,可以减轻GPU的合成压力。

显示设备的能力千差万别,很难直接用API表示硬件设备支持合成的Layer数量,Layer是否可以进行旋转和混合模式操作,以及对图层定位和硬件合成的限制等。因此HWC描述上述信息的流程是这样的:

  1. SurfaceFlingerHWC提供所有Layer的完整列表,让HWC根据其硬件能力,决定如何处理这些Layer
  2. HWC会为每个Layer标注合成方式,是通过GPU还是通过HWC合成。
  3. SurfaceFlinger负责先把所有注明GPU合成的Layer合成到一个输出Buffer,然后把这个输出Buffer和其他Layer(注明HWC合成的Layer)一起交给HWC,让HWC完成剩余Layer的合成和显示。

虽然每个显示设备的能力不同,但是官方要求每个HWC硬件模块都应该支持以下能力:

  1. 至少支持4个叠加层:状态栏、系统栏、应用本身和壁纸或者背景。
  2. 叠加层可以大于显示屏,例如:壁纸
  3. 同时支持预乘每像素(per-pixel)Alpha混合和每平面(per-plane)Alpha混合。
  4. 为了支持受保护的内容,必须提供受保护视频播放的硬件路径。
  5. RGBA packing order, YUV formats, and tiling, swizzling, and stride properties

Tiling:可以把Image切割成MxN个小块,最后渲染时,再将这些小块拼接起来,就像铺瓷砖一样。

Swizzling:一种拌和技术,表示向量单元可以被任意地重排或重复。

但是并非所有情况下HWC都比GPU更高效,例如:当屏幕上没有任何变化时,尤其是叠加层有透明像素并且需要进行图层透明像素混合时。在这种情况下,HWC可以要求部分或者全部叠加层都进行GPU合成,然后HWC持有合成的结果Buffer,如果SurfaceFlinger要求合成相同的叠加图层列表,HWC可以直接显示之前合成的结果Buffer,这有助于提高待机设备的电池寿命。

HWC也提供了VSync事件,用于管理渲染和图层合成时机,后续文章会进行介绍。

HWC2实现

Android7.0提供了HWC和HWC2两个版本,默认使用HWC,但是手机厂商也可以选择HWC2,如下所示:

// frameworks\native\services\surfaceflinger\Android.mk
USE_HWC2 := false
ifeq ($(USE_HWC2),true)LOCAL_CFLAGS  = -DUSE_HWC2LOCAL_SRC_FILES  = \SurfaceFlinger.cpp \DisplayHardware/HWComposer.cpp
elseLOCAL_SRC_FILES  = \SurfaceFlinger_hwc1.cpp \DisplayHardware/HWComposer_hwc1.cpp
endif

SurfaceFlinger通过HWComposer使用HWC硬件能力,HWComposer构造函数通过loadHwcModule方法加载HWC模块,并封装成HWC2::Device结构,如下所示:

// Load and prepare the hardware composer module,HWComposer构造函数中通过此方法加载HWC模块
void HWComposer::loadHwcModule()
{// 定义在hardware.h中,表示一个硬件模块,是HAL层的灵魂hw_module_t const* module;// 加载硬件厂商提供的hwcomposer模块,HWC_HARDWARE_MODULE_ID定义在hwcomposer_defs.h中,表示"hwcomposer"if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) {ALOGE("%s module not found, aborting", HWC_HARDWARE_MODULE_ID);abort();}hw_device_t* device = nullptr;// 通过硬件厂商提供的open函数打开一个"composer"硬件设备,HWC_HARDWARE_COMPOSER也定义在hwcomposer_defs.h中,表示"composer"int error = module->methods->open(module, HWC_HARDWARE_COMPOSER, &device); if (error != 0) {ALOGE("Failed to open HWC device (%s), aborting", strerror(-error));abort();}uint32_t majorVersion = (device->version >> 24) & 0xF;// mHwcDevice是HWC2.h中定义的HWC2::Device,所有与HWC的交互都通过mHwcDeviceif (majorVersion == 2) { // HWC2,hwc2_device_t是hwcomposer2.h中的结构体mHwcDevice = std::make_unique<HWC2::Device>(reinterpret_cast<hwc2_device_t*>(device));} else { // 设备是基于HWC1,这里用HWC2去适配,Android7.0及以前默认都是HWC1,hwc_composer_device_1_t是hwcomposer.h中的结构体mAdapter = std::make_unique<HWC2On1Adapter>(reinterpret_cast<hwc_composer_device_1_t*>(device));mHwcDevice = std::make_unique<HWC2::Device>(static_cast<hwc2_device_t*>(mAdapter.get()));}// 获取硬件支持的最大虚拟屏幕数量,VirtualDisplay可用于录屏mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount();
}

先加载hwcomposer模块得到hw_module_t,再打开composer设备得到hw_device_thw_module_thw_device_t定义在hardwarelibhardwareincludehardwarehardware.h,表示一个HAL层模块和属于该模块的一个实现设备。注意这里是先有HAL模块,再有实现此模块的硬件设备。

上述通过hw_get_module方法(hardwarelibhardwarehardware.c)加载hwcomposer模块,此模块由硬件厂商提供实现,例如:hardwarelibhardwaremoduleshwcomposerhwcomposer.cpp是hwcomposer模块基于HWC1的default实现,对应的共享库是hwcomposer.default.so;hardwareqcomdisplaymsm8994libhwcomposerhwc.cpp是高通MSM8994基于HWC1的实现,对应的共享库是hwcomposer.msm8994.so。如果是基于HWC2协议实现,则需要实现hwcomposer2.h中定义的hwc2_device_t接口,例如:class VendorComposer : public hwc2_device_t。Android7.0的hwcomposer模块默认都是基于HWC1协议实现的。每个HAL层模块实现都要定义一个HAL_MODULE_INFO_SYM数据结构,并且该结构的第一个字段必须是hw_module_t,下面是高通MSM8994hwcomposer模块的定义:

// 高通MSM8994
hwc_module_t HAL_MODULE_INFO_SYM = {// common表示hw_module_t模块common: {tag: HARDWARE_MODULE_TAG,version_major: 2,version_minor: 0,id: HWC_HARDWARE_MODULE_ID, // hwcomposername: "Qualcomm Hardware Composer Module",author: "CodeAurora Forum",methods: &hwc_module_methods,dso: 0,reserved: {0},}
};

最重要的一点:HWComposer::loadHwcModule方法最终把HWC模块封装成了HWC2::Device

frameworksnativeservicessurfaceflingerDisplayHardwareHWC2.h主要定义了以下三个结构体:

  • HWC2::Device:表示硬件合成显示设备
  • HWC2::Display:表示一个显示屏幕,可以是物理显示屏(可以热插拔接入或者移除),也可以是虚拟显示屏,现在的游戏录屏一般都是基于虚拟屏幕实现的。
  • HWC2::Layer:表示一个叠加图层,对应与应用侧的Surface。

它们是对HWC硬件模块的进一步封装,方便进行调用。HWC2::Device持有一个hwc2_device_t,用于连接硬件设备,它包含了很多HWC2_PFN开头的函数指针变量,这些函数指针定义在`hwcomposer2.h`。在HWC2::Device的构造函数中,会通过Device::loadFunctionPointers -> loadFunctionPointer(FunctionDescriptor desc, PFN& outPFN) -> hwc2_device_t::getFunction的调用链从硬件设备中获取具体的函数指针实现。关键模板函数如下所示:

// 模板函数,用于向硬件设备查询具体的函数指针实现
template <typename PFN>
[[clang::warn_unused_result]] bool loadFunctionPointer(FunctionDescriptor desc, PFN& outPFN) {// desc表示一个枚举类型值auto intDesc = static_cast<int32_t>(desc);// mHwcDevice表示hwc2_device_t,是硬件驱动提供的实现auto pfn = mHwcDevice->getFunction(mHwcDevice, intDesc);if (pfn != nullptr) {// 强转函数指针outPFN = reinterpret_cast<PFN>(pfn); return true;} else {ALOGE("Failed to load function %s", to_string(desc).c_str());return false;}
}

这些函数指针主要分为三类:

  1. 硬件设备(Device)相关的函数指针
  2. 显示屏幕(Display)相关的函数指针
  3. 叠加图层(Layer)相关的函数指针

通过上述函数指针可以与hwc2_device_t表示的硬件合成模块进行交互。三类指针分别选取了一个示例:

// Device方法:获得设备支持的最大虚拟屏幕个数
uint32_t Device::getMaxVirtualDisplayCount() const
{return mGetMaxVirtualDisplayCount(mHwcDevice);
}
// Display方法:为指定Device的指定Display创建一个Layer
Error Display::createLayer(std::shared_ptr<Layer>* outLayer)
{// 表示创建的layer的唯一标识符hwc2_layer_t layerId = 0;// mDevice.mHwcDevice表示hwc2_device_t,即真正的硬件设备,mId表示当前Display的唯一标识符,即为指定Device的指定Display创建一个Layerint32_t intError = mDevice.mCreateLayer(mDevice.mHwcDevice, mId, &layerId);auto error = static_cast<Error>(intError);if (error != Error::None) {return error;}// 基于layerId创建HWC2::Layerauto layer = std::make_shared<Layer>(shared_from_this(), layerId);// 保存当前Display所有的LayermLayers.emplace(layerId, layer);// 返回创建的HWC2::Layer*outLayer = std::move(layer);return Error::None;
}
// Layer方法:为指定Device的指定Display的指定Layer指定合成方式
Error Layer::setCompositionType(Composition type)
{auto intType = static_cast<int32_t>(type);// 为指定Device的指定Display的指定Layer指定合成方式int32_t intError = mDevice.mSetLayerCompositionType(mDevice.mHwcDevice,mDisplayId, mId, intType);return static_cast<Error>(intError);
}

可以通过类图,直观感受下引用关系。

HWC2::Device构造函数除了完成获取函数指针实现以外,还会通过Device::registerCallbacks向硬件设备注册三个Display的回调:热插拔,刷新和VSync信号,如下所示:

// HWC硬件的几种回调描述符
// hardware\libhardware\include\hardware\hwcomposer2.h
enum class Callback : int32_t {Invalid = HWC2_CALLBACK_INVALID,// 显示屏幕(Display)的热插拔Hotplug = HWC2_CALLBACK_HOTPLUG,// 显示屏幕(Display)的刷新Refresh = HWC2_CALLBACK_REFRESH,// 显示屏幕(Display)的VSync信号Vsync = HWC2_CALLBACK_VSYNC,
};
// 注册热插拔/刷新/VSync回调
void Device::registerCallbacks()
{   // Callback枚举类型如上所示registerCallback<HWC2_PFN_HOTPLUG>(Callback::Hotplug, hotplug_hook);registerCallback<HWC2_PFN_REFRESH>(Callback::Refresh, refresh_hook);registerCallback<HWC2_PFN_VSYNC>(Callback::Vsync, vsync_hook);
}// 模板函数,用于向硬件设备(hwc2_device_t)注册函数回调
template <typename PFN, typename HOOK>
void registerCallback(Callback callback, HOOK hook) {// Callback枚举类型如上所示auto intCallback = static_cast<int32_t>(callback);// this表示HWC2::Device, hwc2_callback_data_t表示void指针类型auto callbackData = static_cast<hwc2_callback_data_t>(this);// 把函数指针强转成统一的void (*)() 函数指针类型auto pfn = reinterpret_cast<hwc2_function_pointer_t>(hook);// 向hwc2_device_t注册函数回调,callbackData表示透传的资源mRegisterCallback(mHwcDevice, intCallback, callbackData, pfn);
}// 以VSync的静态函数为例
static void vsync_hook(hwc2_callback_data_t callbackData,hwc2_display_t displayId, int64_t timestamp) {// callbackData表示透传的void指针,实际指HWC2::Device        auto device = static_cast<HWC2::Device*>(callbackData);// 通过displayId获取对应的HWC2::Displayauto display = device->getDisplayById(displayId);if (display) {// 向外回调device->callVsync(std::move(display), timestamp);} else {ALOGE("Vsync callback called with unknown display %" PRIu64, displayId);}
}void Device::callVsync(std::shared_ptr<Display> display, nsecs_t timestamp)
{// 通过std::function可调用对象mVsync向外回调,该可调用对象实际是HWComposer通过Device::registerVsyncCallback方法注册的if (mVsync) {mVsync(std::move(display), timestamp);} else {mPendingVsyncs.emplace_back(std::move(display), timestamp);}
}   

总结一下,HWC2::Device构造函数向硬件设备注册三个Display回调:热插拔,刷新和VSync信号。当HWC2::Device收到这些回调时,会通过监听器向外回调到对应的HWComposer函数:HWComposer::hotplug/HWComposer::invalidate/HWComposer::vsync。HWComposer再通过这些信息驱动对应工作,后续文章进行介绍。

HWC Layer合成方式

上文提到HWC2::Device中的函数指针是hardwarelibhardwareincludehardwarehwcomposer2.h中定义的,除此之外,该头文件还定义了一些重要的结构体,这里介绍两个比较重要的:

// 显示屏类型
enum class DisplayType : int32_t {Invalid = HWC2_DISPLAY_TYPE_INVALID,// 物理显示屏,显示设备有一个主屏幕,然后可以通过热插拔添加或者删除外接显示屏Physical = HWC2_DISPLAY_TYPE_PHYSICAL,// 虚拟显示屏,内容会渲染到离屏缓冲区,Android录屏功能就是基于虚拟屏实现的Virtual = HWC2_DISPLAY_TYPE_VIRTUAL,
};// Layer合成类型,HWC2_COMPOSITION_XX取自hwc2_composition_t枚举
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,
};

DisplayType表示显示屏类型,上面注释已经介绍,重点看下Layer合成类型:

  1. Client:这里的Client是相对于HWC硬件设备来说的,即不通过HWC来合成图层,而是通过GPU先把所有的这类图层合成到client target buffer(一个离屏的图形缓冲区,bufferhandlet表示指向这块显存的指针,显存由Gralloc模块分配),然后再通过Display::setClientTarget把这块图形Buffer的地址传递给HWC设备,最后由HWC设备把其他Layer和这个图形Buffer进一步合成,并最终展示在Display上。
  2. Device:通过HWC硬件来合成图层,默认情况下,SurfaceFlinger会配置每个Layer都通过Device方式合成,但是HWC设备会根据硬件设备的性能改变某些图层的合成方式。
  3. SolidColor:HWC设备将通过Layer::setColor设置的颜色渲染这个图层,如果HWC设备不支持这种合成方式,那么将会请求SurfaceFlinger改变合成方式为Client。
  4. Cursor:与Device类似,但是这个图层的位置可以通过setCursorPosition异步设置。如果HWC设备不支持这种合成方式,那么将会请求SurfaceFlinger改变合成方式为Client或者Device。
  5. Sideband:HWC硬件会处理该类图层的合成,以及它的缓冲区更新和内容同步,但是只有拥有HWC2_CAPABILITY_SIDEBAND_STREAM能力的设备才支持这种图层,若设备不支持,那么将会请求SurfaceFlinger改变合成方式为Client或者Device。

那么一个Layer的合成方式是怎么确定的那?大致流程如下所示:

  1. 当VSync信号到来时,SurfaceFlinger被唤醒,处理Layer的新建,销毁和更新,并且为相应Layer设置期望的合成方式。
  2. 所有Layer更新后,SurfaceFlinger调用validateDisplay,让HWC决定每个Layer的合成方式。
  3. SurfaceFlinger调用getChangedCompositionTypes检查HWC是否对任何Layer的合成方式做出了改变,若是,那么SurfaceFlinger则调整对应Layer的合成方式,并且调用acceptDisplayChanges通知HWC。
  4. SurfaceFlinger把所有Client类型的Layer合成到Target图形缓冲区,然后调用setClientTarget把Target Buffer设置给HWC。(如果没有Client类型的Layer,则可以跳过该方法)
  5. 最后,SurfaceFlinger调用presentDisplay,让HWC完成剩余Layer的合成,并且在显示屏上展示出最终的合成结果。

总结

本篇文章只是简单介绍了HWC模块的相关类:HWComposerHWC2::DeviceHWC2::DisplayHWC2::Layer,以及它们的关系。此外,还着重介绍了Layer的合成方式和合成流程。后续文章会更加全面的介绍SurfaceFlinger是如何通过HWC模块完成Layer合成和上屏的(虚拟屏幕是到离屏缓冲区)。

相关源码

  1. hardwarelibhardwareincludehardwarehardware.h
  2. hardwarelibhardwarehardware.c
  3. hardwarelibhardwareincludehardwarehwcomposer_defs.h
  4. hardwarelibhardwareincludehardwarehwcomposer.h
  5. hardwarelibhardwareincludehardwarehwcomposer2.h
  6. frameworksnativeservicessurfaceflingerDisplayHardwareHWC2.h
  7. frameworksnativeservicessurfaceflingerDisplayHardwareHWC2.cpp
  8. frameworksnativeservicessurfaceflingerDisplayHardwareHWComposer.h
  9. frameworksnativeservicessurfaceflingerDisplayHardwareHWComposer.cpp
  10. frameworksnativeservicessurfaceflingerSurfaceFlinger.h
  11. frameworksnativeservicessurfaceflingerSurfaceFlinger.cpp

Android图形系统系统篇之HWC相关推荐

  1. Android图形系统之SurfaceFlinger/OpenGL/HWC/Gralloc/FrameBufer/ION/GPU等关系(十三)

    简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案.音视频.编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列[原创干货持续更新中--]

  2. Android图形系统篇总结摘要

    Android图形系统学习框架:Android图形系统 简单总结下Activity启动后布局显示过程: SurfaceFlinger 是在init.rc解析的时候被创建的,执行其main方法,实例化了 ...

  3. Simpleperf分析之Android系统篇

    [译]Simpleperf分析之Android系统篇 妙菡小说网 https://www.ouqu.info 译者按: Simpleperf是用于Native的CPU性能分析工具,主要用来分析代码执行 ...

  4. 深入浅出 - Android系统移植与平台开发(十)- Android编译系统与定制Android平台系统(瘋耔修改篇二)...

    第四章.Android编译系统与定制Android平台系统 4.1Android编译系统 Android的源码由几十万个文件构成,这些文件之间有的相互依赖,有的又相互独立,它们按功能或类型又被放到不同 ...

  5. Android图形系统之HWComposer

    Android图形系统之HWComposer 1.HWC 1.1 HWC作用 1.2 Overlay 1.3HWC实现 1.4 HWC功能 1.5 HWC效率 1.6 Compose方式 2.Fenc ...

  6. 【朝花夕拾】Android自定义View篇之(一)View绘制流程

    前言 转载请申明转自[https://www.cnblogs.com/andy-songwei/p/10955062.html]谢谢! 自定义View.多线程.网络,被认为是Android开发者必须牢 ...

  7. 【Android显示系统初探】surface初相识

    Android显示系统非常复杂,从早期版本演化至今有很大的变化和改进,所以从当前的版本直接去查看会很困难. 初学者看到繁多的概念和类会感到无从下手. 这里我们将从实践应用的方式来展开这一系列,试图对A ...

  8. 一篇文章看明白 Android 图形系统 Surface 与 SurfaceFlinger 之间的关系

    Android - SurfaceFlinger 图形系统 相关系列 一篇文章看明白 Android 系统启动时都干了什么 一篇文章了解相见恨晚的 Android Binder 进程间通讯机制 一篇文 ...

  9. 深入Android系统(十二)Android图形显示系统-2-SurfaceFlinger与图像输出

    最近有些忙,切实体验了一把拖更的羞耻感 ( *︾▽︾) 本文和上一篇深入Android系统(十二)Android图形显示系统-1-显示原理与Surface关系比较密切,撸完前篇更易理解本文啦 (๑‾ ...

最新文章

  1. python实现全角和半角互相转换
  2. extjs video
  3. 《MySQL—— 业务高峰期的性能问题的紧急处理的手段 》
  4. python继承语法_python中继承父类的例子(python3的语法)
  5. gulp临时服务器显示html页面,用Gulp实现CSS压缩和页面自动刷新
  6. Python3.2官方文件翻译-工具列表和十进制浮点计算
  7. 行高引起的行内块级元素间距
  8. jmeter 连接 sqlite 进行压力测试
  9. web前端开发初学者必看的学习路线图课程内容分享
  10. c语言在线编译 tool.lu,【第229期】在线工具——程序员的工具箱
  11. 安装Windows NT4.0
  12. asp:get请求写法
  13. python中链表是什么_python 单链表的实现
  14. CVE-2020-11946 ManageEngine OpManager 命令执行
  15. MySQL卸载不干净或Mysql安装失败的解决办法
  16. 大楼通信综合布线系统_综合布线系统设计方案时需要注意的事项
  17. Dem地形数据转换为cass支持的dat格式教程
  18. Unity使用VS2019打开代码出现不兼容的解决方法
  19. ICO图标在线生成,php生成ICO图标在线制作源码
  20. 跨越“数字鸿沟”,日本老年智能化服务的解法

热门文章

  1. python basemap 在地图标点
  2. A Better Tomorrow
  3. Python动态心形图形-快去表白吧
  4. 计算机二级考试word试题及其讲解,计算机二级Word篇-实操真题详解21
  5. 苹果手机人脸识别不了是什么原因_为什么安卓手机越用越卡,而苹果手机就不会?原因有这几点...
  6. python基础(四)python中的位运算
  7. 细粒度识别——LIO方法概述
  8. Supermarket [堆]
  9. 实战内容(15)- 简单几招教你玩转音视频 DIY
  10. 华东之旅--西塘第一天