当前位置: 首页 > news >正文

Android下HWC以及drm_hwcomposer普法(上)

Android下HWC以及drm_hwcomposer普法(上)



引言

按摩得全套,错了,做事情得全套,普法分析也是如此。drm_hwcomposer如果对Android图形栈有一定研究的童鞋们应该知道它是Android提供的一个的图形后端合成处理HAL模块的实现。但是在分析这个之前我们非常有必要了解一下Android的HWC前世今生,然后再来看drm_hwcomposer是如何配合HWC框架的



一.普法HWC

这里我们对HWC的普法主要从如下接方面展开进行:

  • HWC的概述
  • HWC的进化
  • HWC中重要概念和实现逻辑

1.1 HWC概述

我们知道SurfaceFlinger可以使用OpenGLES合成Layer,但是这需要占用并消耗大量的GPU资源。大多数GPU都没有针对图层合成进行优化,当SurfaceFlinger通过GPU合成图层时,应用程序无法使用GPU进行自己的渲染。为了解放GPU的绘制能力,很多芯片厂家会提供硬件叠加合成,如果硬件叠加器支持的场景都可以走硬件叠加,解放GPU的绘制能力专心绘制,提升的渲染性能同时还能大幅度的降低功耗(GPU强绘制,叠加搬移并不擅长,高功耗)这个时候我们的HWC就登场了,HWC(hwcomposer)是Android中进行窗口(Layer)合成和显示的HAL层模块,其实现是特定于设备的,而且通常由显示设备制造商(OEM)完成,为SurfaceFlinger服务提供硬件支持。而HWC通过硬件设备进行图层合成,可以减轻GPU的合成压力。应用把要显示的layers交给SurfaceFlinger,SurfaceFlinger直接把这些layers交给hwc,hwc就可以在自己能力范围内做好合成,再把合成好的结果拿去显示。如果芯片显示硬件模块功能较弱,不支持某些合成场景,就会用CPU(纯软件合成)或者GPU去做。

在这里插入图片描述

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

  • SurfaceFlinger向HWC提供所有Layer的完整列表,让HWC根据其硬件能力,决定如何处理这些Layer。

  • HWC会为每个Layer标注合成方式,是通过GPU还是通过HWC合成。

  • SurfaceFlinger负责先把所有注明GPU合成的Layer合成到一个输出Buffer,然后把这个输出Buffer和其他Layer(注明HWC合成的Layer)一起交给HWC,让HWC完成剩余Layer的合成和显示。
    虽然每个显示设备的能力不同,但是官方要求每个HWC硬件模块都应该支持以下能力:

  • 至少支持4个叠加层:状态栏、系统栏、应用本身和壁纸或者背景。

  • 叠加层可以大于显示屏,例如:壁纸

  • 同时支持预乘每像素(per-pixel)Alpha混合和每平面(per-plane)Alpha混合。

  • 为了支持受保护的内容,必须提供受保护视频播放的硬件路径。
    RGBA packing order, YUV formats, and tiling, swizzling, and stride properties

虽然上述官网要求如此,但是,但是很多hwc是没有达到上述要求的,有的叠加只支持一层的primary plane,也不支持各种混合,各种特性,毕竟建议还是建议。
Tiling:可以把Image切割成MxN个小块,最后渲染时,再将这些小块拼接起来,就像铺瓷砖一样。
Swizzling:一种拌和技术,表示向量单元可以被任意地重排或重复。 但是并非所有情况下HWC都比GPU更高效,例如:当屏幕上没有任何变化时,尤其是叠加层有透明像素并且需要进行图层透明像素混合时。在这种情况下,HWC可以要求部分或者全部叠加层都进行GPU合成,然后HWC持有合成的结果Buffer,如果SurfaceFlinger要求合成相同的叠加图层列表,HWC可以直接显示之前合成的结果Buffer,这有助于提高待机设备的电池寿命。

HWC也提供了VSync事件,用于管理渲染和图层合成时机。


1.2 HWC的进化

Android的HWC模块经历了HWC和HWC2两个版本,现在高版本默认使用HWC2,然后其加载方式也由原来的的SurfaceFlinger直接通过loader加载HAL模块,变成现在的通过HIDL调用到composer service单独加载HAL模块实现。活是越来越整得复杂了。

无论通过和中方式加载HAL的实现,我们需要知道的一点就是HAL加载的流程是,先加载hwcomposer模块得到hw_module_t,再打开composer设备得到hw_device_t。hw_module_t和hw_device_t定义在hardware/libhardware/include/hardware/hardware.h,表示一个HAL层模块和属于该模块的一个实现设备。注意这里是先有HAL模块,再有实现此模块的硬件设备。

struct hw_module_t;
struct hw_module_methods_t;
struct hw_device_t;typedef struct hw_module_t {uint32_t tag;uint16_t module_api_version;
#define version_major module_api_version
#define version_minor hal_api_version/** Identifier of module */const char *id;/** Name of this module */const char *name;/** Author/owner/implementor of the module */const char *author;/** Modules methods */struct hw_module_methods_t* methods;/** module's dso */void* dso;#ifdef __LP64__uint64_t reserved[32-7];
#else/** padding to 128 bytes, reserved for future use */uint32_t reserved[32-7];
#endif} hw_module_t;typedef struct hw_module_methods_t {/** Open a specific device */int (*open)(const struct hw_module_t* module, const char* id,struct hw_device_t** device);} hw_module_methods_t;typedef struct hw_device_t {/** tag must be initialized to HARDWARE_DEVICE_TAG */uint32_t tag;uint32_t version;/** reference to the module this device belongs to */struct hw_module_t* module;/** padding reserved for future use */
#ifdef __LP64__uint64_t reserved[12];
#elseuint32_t reserved[12];
#endif/** Close this device */int (*close)(struct hw_device_t* device);} hw_device_t;#ifdef __cplusplus
#define TO_HW_DEVICE_T_OPEN(x) reinterpret_cast<struct hw_device_t**>(x)
#else
#define TO_HW_DEVICE_T_OPEN(x) (struct hw_device_t**)(x)
#endif/*** Name of the hal_module_info*/
#define HAL_MODULE_INFO_SYM         HMI/*** Name of the hal_module_info as a string*/
#define HAL_MODULE_INFO_SYM_AS_STR  "HMI"int hw_get_module(const char *id, const struct hw_module_t **module);int hw_get_module_by_class(const char *class_id, const char *inst,const struct hw_module_t **module);__END_DECLS#endif  /* ANDROID_INCLUDE_HARDWARE_HARDWARE_H */

我们是基于HWC2协议实现,则需要实现hwcomposer2.h中定义的hwc2_device_t接口,而我们后续要分析的drm_hwcomposer就是基于HWC2的实现。每个HAL层模块实现都要定义一个HAL_MODULE_INFO_SYM数据结构,并且该结构的第一个字段必须是hw_module_t。这里关于它们之间的对应关系,和一些数据结构这里暂时不分析,太多了,这里我们不做过多分析。


1.3 HWC中重要的概念和核心调用逻辑

这里我们的重点不是SurfaceFlinger里面HWC相关的代码部分分析,但是该有的概念和一些核心调用逻辑还是必须提前知道:

但是我们如下的几个重要的概念我们必须要有:

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

在进行接下来的分析前,我们先来一个Layer的合成方式是怎么确定的那?大致流程如下所示!

在这里插入图片描述

其基本流程可以归纳总结为如下:

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

  • HWC2::Layer的创建流程
SurfaceFlinger::onMessageReceivedonMessageRefresh()//Android 13上面是通过andler::handleMessage compositor.compositemCompositionEngine->present(refreshArgs)output->prepare(args, latchedLayers)Output::rebuildLayerStacksOutput::collectVisibleLayersOutput::ensureOutputLayerIfVisibleBaseOutput::createOutputLayer(layerFE)Display::createOutputLayerhwc.createLayermComposer.createLayerHwcDisplay::CreateLayer//drm_hwcomposer
05-26 01:57:17.427  2264  2264 D createOutputLayer: #00 pc 000000000013f994  /system/lib64/libsurfaceflinger.so (android::compositionengine::impl::Display::createOutputLayer(android::sp<android::compositionengine::LayerFE> const&) const+84)
05-26 01:57:17.428  2264  2264 D createOutputLayer: #01 pc 0000000000140dfc  /system/lib64/libsurfaceflinger.so (std::__1::shared_ptr<android::compositionengine::impl::Display> android::compositionengine::impl::createOutputTemplated<android::compositionengine::impl::Display, android::compositionengine::CompositionEngine>(android::compositionengine::CompositionEngine const&)::Output::ensureOutputLayer(std::__1::optional<unsigned long>, android::sp<android::compositionengine::LayerFE> const&)+80)
05-26 01:57:17.428  2264  2264 D createOutputLayer: #02 pc 00000000001475e0  /system/lib64/libsurfaceflinger.so (android::compositionengine::impl::Output::ensureOutputLayerIfVisible(android::sp<android::compositionengine::LayerFE>&, android::compositionengine::Output::CoverageState&)+1692)
05-26 01:57:17.428  2264  2264 D createOutputLayer: #03 pc 0000000000146e60  /system/lib64/libsurfaceflinger.so (android::compositionengine::impl::Output::collectVisibleLayers(android::compositionengine::CompositionRefreshArgs const&, android::compositionengine::Output::CoverageState&)+128)
05-26 01:57:17.428  2264  2264 D createOutputLayer: #04 pc 0000000000146d38  /system/lib64/libsurfaceflinger.so (android::compositionengine::impl::Output::rebuildLayerStacks(android::compositionengine::CompositionRefreshArgs const&, std::__1::unordered_set<android::sp<android::compositionengine::LayerFE>, android::compositionengine::LayerFESpHash, std::__1::equal_to<android::sp<android::compositionengine::LayerFE> >, std::__1::allocator<android::sp<android::compositionengine::LayerFE> > >&)+340)
05-26 01:57:17.428  2264  2264 D createOutputLayer: #05 pc 0000000000146a3c  /system/lib64/libsurfaceflinger.so (android::compositionengine::impl::Output::prepare(android::compositionengine::CompositionRefreshArgs const&, std::__1::unordered_set<android::sp<android::compositionengine::LayerFE>, android::compositionengine::LayerFESpHash, std::__1::equal_to<android::sp<android::compositionengine::LayerFE> >, std::__1::allocator<android::sp<android::compositionengine::LayerFE> > >&)+56)
05-26 01:57:17.428  2264  2264 D createOutputLayer: #06 pc 000000000013eb3c  /system/lib64/libsurfaceflinger.so (android::compositionengine::impl::CompositionEngine::present(android::compositionengine::CompositionRefreshArgs&)+116)
05-26 01:57:17.428  2264  2264 D createOutputLayer: #07 pc 0000000000111c08  /system/lib64/libsurfaceflinger.so (android::SurfaceFlinger::onMessageRefresh()+1524)
05-26 01:57:17.428  2264  2264 D createOutputLayer: #08 pc 000000000010ee5c  /system/lib64/libsurfaceflinger.so (android::SurfaceFlinger::onMessageReceived(int, long)+88)
05-26 01:57:17.428  2264  2264 D createOutputLayer: #09 pc 0000000000019b8c  /system/lib64/libutils.so (android::Looper::pollInner(int)+372)
05-26 01:57:17.428  2264  2264 D createOutputLayer: #10 pc 00000000000199b0  /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+112)
05-26 01:57:17.428  2264  2264 D createOutputLayer: #11 pc 00000000000f7850  /system/lib64/libsurfaceflinger.so (android::impl::MessageQueue::waitMessage()+84)
05-26 01:57:17.428  2264  2264 D createOutputLayer: #12 pc 0000000000108594  /system/lib64/libsurfaceflinger.so (android::SurfaceFlinger::run()+20)
05-26 01:57:17.428  2264  2264 D createOutputLayer: #13 pc 0000000000002394  /system/bin/surfaceflinger (main+844)
05-26 01:57:17.428  2264  2264 D createOutputLayer: #14 pc 000000000008506c  /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init+108)


  • HWC2::Layer设置buffer的流程
//通过Composer::setLayerBuffer调用堆栈,将buffer\_handle\_t信息传递到hwc2的HAL层实现的
onMessageRefresh()mCompositionEngine->present(refreshArgs)output->present(args)  //Output.cppOutput::updateAndWriteCompositionState(refreshArgs)layer->writeStateToHWCwriteOutputIndependentPerFrameStateToHWCOutputLayer.cpp:471 writeBufferStateToHWCHwcLayer::SetLayerBuffer //drm_hwcomposer
05-26 01:57:51.122  2280  2280 D setLayerBuffer: #00 pc 00000000000b4028  /system/lib64/libsurfaceflinger.so (android::Hwc2::impl::Composer::setLayerBuffer(unsigned long, unsigned long, unsigned int, android::sp<android::GraphicBuffer> const&, int)+96)
05-26 01:57:51.122  2280  2280 D setLayerBuffer: #01 pc 00000000001514c8  /system/lib64/libsurfaceflinger.so (android::compositionengine::impl::OutputLayer::writeBufferStateToHWC(android::HWC2::Layer*, android::compositionengine::LayerFECompositionState const&)+344)
05-26 01:57:51.122  2280  2280 D setLayerBuffer: #02 pc 0000000000150e74  /system/lib64/libsurfaceflinger.so (android::compositionengine::impl::OutputLayer::writeOutputIndependentPerFrameStateToHWC(android::HWC2::Layer*, android::compositionengine::LayerFECompositionState const&)+484) OutputLayer.cpp:471 writeBufferStateToHWC
05-26 01:57:51.122  2280  2280 D setLayerBuffer: #03 pc 00000000001500ac  /system/lib64/libsurfaceflinger.so (android::compositionengine::impl::OutputLayer::writeStateToHWC(bool)+180) OutputLayer.cpp:342 writeOutputIndependentPerFrameStateToHWC
05-26 01:57:51.122  2280  2280 D setLayerBuffer: #04 pc 00000000001479a4  /system/lib64/libsurfaceflinger.so (android::compositionengine::impl::Output::updateAndWriteCompositionState(android::compositionengine::CompositionRefreshArgs const&)+356) Output.cpp:607 layer->writeStateToHWC
05-26 01:57:51.122  2280  2280 D setLayerBuffer: #05 pc 0000000000146b2c  /system/lib64/libsurfaceflinger.so (android::compositionengine::impl::Output::present(android::compositionengine::CompositionRefreshArgs const&)+64) Output.cpp:313  updateAndWriteCompositionState(refreshArgs)
05-26 01:57:51.122  2280  2280 D setLayerBuffer: #06 pc 000000000013ebe8  /system/lib64/libsurfaceflinger.so (android::compositionengine::impl::CompositionEngine::present(android::compositionengine::CompositionRefreshArgs&)+220) CompositionEngine.cpp output->present(args)
05-26 01:57:51.122  2280  2280 D setLayerBuffer: #07 pc 0000000000111c4c  /system/lib64/libsurfaceflinger.so (android::SurfaceFlinger::onMessageRefresh()+1524) mCompositionEngine->present(refreshArgs);
05-26 01:57:51.122  2280  2280 D setLayerBuffer: #08 pc 000000000010eea0  /system/lib64/libsurfaceflinger.so (android::SurfaceFlinger::onMessageReceived(int, long)+88)   onMessageRefresh()
05-26 01:57:51.122  2280  2280 D setLayerBuffer: #09 pc 0000000000019b8c  /system/lib64/libutils.so (android::Looper::pollInner(int)+372)
05-26 01:57:51.122  2280  2280 D setLayerBuffer: #10 pc 00000000000199b0  /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+112)
05-26 01:57:51.122  2280  2280 D setLayerBuffer: #11 pc 00000000000f7894  /system/lib64/libsurfaceflinger.so (android::impl::MessageQueue::waitMessage()+84)
05-26 01:57:51.122  2280  2280 D setLayerBuffer: #12 pc 00000000001085d8  /system/lib64/libsurfaceflinger.so (android::SurfaceFlinger::run()+20)
05-26 01:57:51.122  2280  2280 D setLayerBuffer: #13 pc 0000000000002394  /system/bin/surfaceflinger (main+844)
05-26 01:57:51.122  2280  2280 D setLayerBuffer: #14 pc 000000000008506c  /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init+108)



二. drm_hwcomposer普法

drm_hwcomposer作为HWC框架的HAL实现,它是怎么承接HWC的接口,并且实现相关逻辑的呢。这就离不开它的设计逻辑了,分为frontend前端和backend后端处理逻辑。

  • frontend 是对外提供调用的接口,,外部使用者呼叫 front end 暴漏出的接口来呼叫某一功能;
  • backend 主要是调用内核的实现逻辑,是前端接口功能的内部实现,是真正做事的地方

其中最最重要的就是后端的设计逻辑,分为三种情况

  • Backend: 一个后端的实现,注册为"generic",主要是定义了ValidateDisplay方法,这个方法用来设置可见的HwcLayer应该采用什么合成方式
  • BackendClient: 一个后端的实现,注册为"client",主要是定义了ValidateDisplay方法,它把所有HwcLayer都设置成立Client合成方式
  • BackendManager:后端的管理器,用来根据Device name从已注册的backend列表中选择一个,设置给HwcDisplay;GetBackendByName就是通过Device name来从available_backends_中选择一个匹配的Backend构造函数来构建后端对象。

接下来我们就从代码触发,看看它是如何使用上述的框架实现的!


2.1 drm_hwcomposer源码初探

这块我们只分析其核心实现,对于具体的一些逻辑我们带过!有些具体代码逻辑,臣妾真的是心有余而力不足啊,分析不了啊。路漫漫啊,不知道能不能走到终点! 在分析源码前,我们来看下drm_hwcomposer的目录,如下:

drm_hwcomposer
├── Android.bp   //编译脚本
├── backend      //hwcomposer后端,是前端接口功能的内部实现,是真正做事的地方
├── bufferinfo  //对应不同vendor的buffer接口
├── build_deploy.sh
├── compositor  //kms处理送显逻辑代码
├── drm         //drm,各个子模块代码
├── hwc2_device //对接Android hwc相关源码
├── include     //头文件
├── MODULE_LICENSE_APACHE2
├── NOTICE
├── presubmit.sh
├── README.md
└── utils

2.2 drm_hwcomposer的HAL模块实现

HAL是个很神秘的东东,经常会看到一些求职网站显示需要招聘会HAL开发的Android工程师,但是绝大部分的Android开发人员只会使用,很少能独立开发一个属于自己的HAL。幸运的是我也是其中的绝大部分,不幸的是我也么有开发过属于自己的HAL实现。

//hwc2_device/hwc2_device.cpp
/*** @brief * * @param module * @param name * @param dev * @return int *  HookDevOpen,该方法中会去实例化一个Drmhwc2Device对象,其中去创建了一个DrmHwcTwo对象*/
static int HookDevOpen(const struct hw_module_t *module, const char *name,struct hw_device_t **dev) {if (strcmp(name, HWC_HARDWARE_COMPOSER) != 0) {ALOGE("Invalid module name- %s", name);return -EINVAL;}auto ctx = std::make_unique<Drmhwc2Device>();//详见章节5.6if (!ctx) {ALOGE("Failed to allocate DrmHwcTwo");return -ENOMEM;}ctx->common.tag = HARDWARE_DEVICE_TAG;ctx->common.version = HWC_DEVICE_API_VERSION_2_0;ctx->common.close = HookDevClose;// NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)ctx->common.module = (hw_module_t *)module;ctx->getCapabilities = HookDevGetCapabilities;ctx->getFunction = HookDevGetFunction;//核心接口,详见5.5*dev = &ctx.release()->common;return 0;
}static struct hw_module_methods_t hwc2_module_methods = {.open = android::HookDevOpen,
};/*** @brief * HAL标准化实现流程*/
hw_module_t HAL_MODULE_INFO_SYM = {//注册HAL.tag = HARDWARE_MODULE_TAG,.module_api_version = HARDWARE_MODULE_API_VERSION(2, 0),.id = HWC_HARDWARE_MODULE_ID,//hwcomposer.name = "DrmHwcTwo module",.author = "The Android Open Source Project",.methods = &hwc2_module_methods,.dso = nullptr,.reserved = {0},
};

这一块的逻辑比较简单,就是上层hwcomposer服务加载HAL模块的时候,通过套路化的HAL编程实现加载HWC HAL的功能,并且上层调用open时候会初始化一个Drmhwc2Device返回给hwcomposer服务。其调用堆栈如下:

01-01 00:00:18.617  2262  2262 D HWC_DRM : #00 pc 000000000002bfd4  /vendor/lib64/hw/hwcomposer.drm.so (android::HookDevOpen(hw_module_t const*, char const*, hw_device_t**)+80)
01-01 00:00:18.617  2262  2262 D HWC_DRM : #01 pc 0000000000006684  /vendor/bin/hw/android.hardware.graphics.composer@2.1-service (android::hardware::graphics::composer::V2_1::passthrough::HwcLoader::openDeviceWithAdapter(hw_module_t const*, bool*)+376)
01-01 00:00:18.617  2262  2262 D HWC_DRM : #02 pc 000000000000639c  /vendor/bin/hw/android.hardware.graphics.composer@2.1-service (android::hardware::graphics::composer::V2_1::passthrough::HwcLoader::createHalWithAdapter(hw_module_t const*)+48)
01-01 00:00:18.617  2262  2262 D HWC_DRM : #03 pc 00000000000062cc  /vendor/bin/hw/android.hardware.graphics.composer@2.1-service (android::hardware::graphics::composer::V2_1::passthrough::HwcLoader::load()+164)
01-01 00:00:18.617  2262  2262 D HWC_DRM : #04 pc 0000000000006138  /vendor/bin/hw/android.hardware.graphics.composer@2.1-service (main+240)
01-01 00:00:18.617  2262  2262 D HWC_DRM : #05 pc 000000000008506c  /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init+108)

其中Drmhwc2Device的层级关系如下:

在这里插入图片描述

其代码定义如下:


//hardware/libhardware/include/hardware/hwcomposer2.h
typedef struct hwc2_device {
/* Must be the first member of this struct, since a pointer to this struct* will be generated by casting from a hw_device_t* */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;//hardware/libhardware/include/hardware/hardware.h
typedef struct hw_device_t {tag; /** tag must be initialized to HARDWARE_DEVICE_TAG */uint32_t version;struct hw_module_t* module;uint64_t reserved[12];int (*close)(struct hw_device_t* device);
} hw_device_t;hwc2_device/hwc2_device.cpp
struct Drmhwc2Device : hwc2_device {DrmHwcTwo drmhwctwo;
};

这里可以看到hwc2_device在hw_device_t的基础上扩展了,两个函数接口,其中最最核心的就是getFunction,原来在HWC时代,上层调用HAL是直接通过固化的函数硬对接上去的,现在通过getFunction可以进行许多扩展,增强可实用性。


2.3 HookDevGetFunction功能

HookDevGetFunction,我们可以把它理解为是HAL层对上提供的功能函数接口表。这些函数具体可以分为三类:

  • Device functions:其核心功能函数包括:

    • 注册回调事件 registerCallback
    • 创建虚拟通路(非显示通路,如录屏)createVirtualDisplay
    • 热拔插事件onHotplug
  • Display functions:其核心功能函数包括:

    • 输入layer的管理createLayer / destroyLayer
    • 叠加输出buffer设置setClientTarget / setOutputBuffer
    • 显示触发present
    • 设置vsync使能setVsyncEnabled
  • Layer functions:其核心功能函数包括:

    • 设置层的buffer setBuffer:
    • 设置层的属性 setBlendMode/setColor/setDataspace/setDisplayFrame(显示区域)/setPlaneAlpha/setSourceCrop/setZOrder
    • 设置层的sidband流 setSidebandStream
    • 设置叠加方式setCompositionType:Device表示硬件叠加,Client表示GPU叠加
//hwc2_device/hwc2_device.cppstatic hwc2_function_pointer_t HookDevGetFunction(struct hwc2_device * /*dev*/,int32_t descriptor) {auto func = static_cast<HWC2::FunctionDescriptor>(descriptor);switch (func) {// Device functions...case HWC2::FunctionDescriptor::RegisterCallback://注册回调,热插拔事件就是通过它回调上去的return ToHook<HWC2_PFN_REGISTER_CALLBACK>(DeviceHook<int32_t, decltype(&DrmHwcTwo::RegisterCallback),&DrmHwcTwo::RegisterCallback, int32_t,hwc2_callback_data_t, hwc2_function_pointer_t>);    // Display functionscase HWC2::FunctionDescriptor::AcceptDisplayChanges:return ToHook<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>(DisplayHook<decltype(&HwcDisplay::AcceptDisplayChanges),&HwcDisplay::AcceptDisplayChanges>);case HWC2::FunctionDescriptor::CreateLayer:return ToHook<HWC2_PFN_CREATE_LAYER>(DisplayHook<decltype(&HwcDisplay::CreateLayer),&HwcDisplay::CreateLayer, hwc2_layer_t *>);case HWC2::FunctionDescriptor::DestroyLayer:...// Layer functionscase HWC2::FunctionDescriptor::SetCursorPosition:return ToHook<HWC2_PFN_SET_CURSOR_POSITION>(LayerHook<decltype(&HwcLayer::SetCursorPosition),&HwcLayer::SetCursorPosition, int32_t, int32_t>);case HWC2::FunctionDescriptor::SetLayerBlendMode:return ToHook<HWC2_PFN_SET_LAYER_BLEND_MODE>(LayerHook<decltype(&HwcLayer::SetLayerBlendMode),&HwcLayer::SetLayerBlendMode, int32_t>);case HWC2::FunctionDescriptor::SetLayerBuffer:return ToHook<HWC2_PFN_SET_LAYER_BUFFER>(LayerHook<decltype(&HwcLayer::SetLayerBuffer),&HwcLayer::SetLayerBuffer, buffer_handle_t, int32_t>);...}
}

在这里插入图片描述


2.4 DrmHwcTwo的构建和初始化

兄弟你为啥叫DrmHwcTwo而不是叫DrmHwc2呢!你到底是李鬼还是李逵,让人傻傻分不清楚啊!让我们看看DrmHwcTwo的构造究竟干了些啥!

//drm-hwcomposer/hwc2_device/DrmHwcTwo.h
class DrmHwcTwo : public PipelineToFrontendBindingInterface {public:DrmHwcTwo();~DrmHwcTwo() override = default;std::pair<HWC2_PFN_HOTPLUG, hwc2_callback_data_t> hotplug_callback_{};std::pair<HWC2_PFN_VSYNC, hwc2_callback_data_t> vsync_callback_{};
#if PLATFORM_SDK_VERSION > 29std::pair<HWC2_PFN_VSYNC_2_4, hwc2_callback_data_t> vsync_2_4_callback_{};std::pair<HWC2_PFN_VSYNC_PERIOD_TIMING_CHANGED, hwc2_callback_data_t>period_timing_changed_callback_{};
#endifstd::pair<HWC2_PFN_REFRESH, hwc2_callback_data_t> refresh_callback_{};// Device functionsHWC2::Error CreateVirtualDisplay(uint32_t width, uint32_t height,int32_t *format, hwc2_display_t *display);HWC2::Error DestroyVirtualDisplay(hwc2_display_t display);void Dump(uint32_t *outSize, char *outBuffer);uint32_t GetMaxVirtualDisplayCount();HWC2::Error RegisterCallback(int32_t descriptor, hwc2_callback_data_t data,hwc2_function_pointer_t function);auto GetDisplay(hwc2_display_t display_handle) {return displays_.count(display_handle) != 0? displays_[display_handle].get(): nullptr;}auto &GetResMan() {return resource_manager_;}void ScheduleHotplugEvent(hwc2_display_t displayid, bool connected) {deferred_hotplug_events_[displayid] = connected;}// PipelineToFrontendBindingInterfacebool BindDisplay(DrmDisplayPipeline *pipeline) override;bool UnbindDisplay(DrmDisplayPipeline *pipeline) override;void FinalizeDisplayBinding() override;void SendVsyncEventToClient(hwc2_display_t displayid, int64_t timestamp,uint32_t vsync_period) const;void SendVsyncPeriodTimingChangedEventToClient(hwc2_display_t displayid,int64_t timestamp) const;private:void SendHotplugEventToClient(hwc2_display_t displayid, bool connected);//ResourceManager是个非常重要的核心类,他应该管理着DRM的资源ResourceManager resource_manager_;// DrmHwcTwo类中的成员std::map<hwc2_display_t, std::unique_ptr<HwcDisplay>> displays_;std::map<DrmDisplayPipeline *, hwc2_display_t> display_handles_;std::string mDumpString;std::map<hwc2_display_t, bool> deferred_hotplug_events_;std::vector<hwc2_display_t> displays_for_removal_list_;uint32_t last_display_handle_ = kPrimaryDisplay;
};//drm-hwcomposer/hwc2_device/DrmHwcTwo.cpp
DrmHwcTwo::DrmHwcTwo() : resource_manager_(this){}; // DrmHwcTwo的构造函数定义//hwc2_device/hwc2_device.cpp
struct Drmhwc2Device : hwc2_device {DrmHwcTwo drmhwctwo;
};auto ctx = std::make_unique<Drmhwc2Device>();

DrmHwcTwo它是如此的潇洒,非常简单就是实例化了一个ResourceManager对象,然后将自己传递给了ResourceManager。


2.5 ResourceManager构造以及初始化流程

这里我们直接上代码,客官请看:

//drm/ResourceManager.cpp
ResourceManager::ResourceManager(PipelineToFrontendBindingInterface *p2f_bind_interface): frontend_interface_(p2f_bind_interface) {//p2f_bind_interface指向DrmHwcTwo对象if (uevent_listener_.Init() != 0) {ALOGE("Can't initialize event listener");}
}

很简单,就是去实例化一个ResourceManager对象,其构造函数中处理初始化了uevent_listener等成员,也没啥了frontend_interface_指向DrmHwcTwo对象。到这里,是不是读者感觉到一脸懵逼,Resourceanager感觉啥也木有做啊,其实它是个非常重要的核心类,它管理着DRM的资源(对于libdrm编程熟悉的小伙伴,应该对于Resource肯定很熟悉了)。

那这里的ResourceManager::Init啥时候被调用呢!其实是在SurfaceFlinger的初始化过程时,设置callback给HWC,层层传递后就会调用到DrmHwcTwo::RegisterCallback进而调用到了 resource_manager_.Init();

//hwc2_device/hwc2_device.cppcase HWC2::FunctionDescriptor::RegisterCallback:return ToHook<HWC2_PFN_REGISTER_CALLBACK>(DeviceHook<int32_t, decltype(&DrmHwcTwo::RegisterCallback),&DrmHwcTwo::RegisterCallback, int32_t,hwc2_callback_data_t, hwc2_function_pointer_t>);//hwc2_device/DrmHwcTwo.cpp                   
HWC2::Error DrmHwcTwo::RegisterCallback(int32_t descriptor,hwc2_callback_data_t data,hwc2_function_pointer_t function) {switch (static_cast<HWC2::Callback>(descriptor)) {case HWC2::Callback::Hotplug: {hotplug_callback_ = std::make_pair(HWC2_PFN_HOTPLUG(function), data);if (function != nullptr) {//  ResourceManager resource_manager_;resource_manager_.Init();} else {resource_manager_.DeInit();/* Headless display may still be here, remove it */displays_.erase(kPrimaryDisplay);}break;}case HWC2::Callback::Refresh: {refresh_callback_ = std::make_pair(HWC2_PFN_REFRESH(function), data);break;}case HWC2::Callback::Vsync: {vsync_callback_ = std::make_pair(HWC2_PFN_VSYNC(function), data);break;}
#if PLATFORM_SDK_VERSION > 29case HWC2::Callback::Vsync_2_4: {vsync_2_4_callback_ = std::make_pair(HWC2_PFN_VSYNC_2_4(function), data);break;}case HWC2::Callback::VsyncPeriodTimingChanged: {period_timing_changed_callback_ = std::make_pair(HWC2_PFN_VSYNC_PERIOD_TIMING_CHANGED(function), data);break;}
#endifdefault:break;}return HWC2::Error::None;
}                     

我们看下ResourceManager::Init实现:

ResourceManager 初始化到底初始化了什么呢?

[drm-hwcomposer/drm/ResourceManager.cpp]
void ResourceManager::Init() {if (initialized_) {ALOGE("Already initialized"); // 已经初始化了,避免重复初始化return;}char path_pattern[PROPERTY_VALUE_MAX];// Could be a valid path or it can have at the end of it the wildcard %// which means that it will try open all devices until an error is met.int path_len = property_get("vendor.hwc.drm.device", path_pattern,"/dev/dri/card%");if (path_pattern[path_len - 1] != '%') {AddDrmDevice(std::string(path_pattern));//详见章节2.6} else {path_pattern[path_len - 1] = '\0';for (int idx = 0;; ++idx) {std::ostringstream path;path << path_pattern << idx;struct stat buf {};if (stat(path.str().c_str(), &buf) != 0)break;if (DrmDevice::IsKMSDev(path.str().c_str())) {AddDrmDevice(path.str());}}}/**上面一大坨代码,简单理解就是找到DRM的设备节点,然后打开它,在我的设备上是/dev/dri/card0 *//** AddDrmDevice中去初始化DRM各种各样的资源 **/char scale_with_gpu[PROPERTY_VALUE_MAX];property_get("vendor.hwc.drm.scale_with_gpu", scale_with_gpu, "0");scale_with_gpu_ = bool(strncmp(scale_with_gpu, "0", 1));// 使用GPU缩放的标志if (BufferInfoGetter::GetInstance() == nullptr) {ALOGE("Failed to initialize BufferInfoGetter"); // 初始化BufferInfoGetter,用于从Gralloc Mapper中获取buffer的属性信息return;}uevent_listener_.RegisterHotplugHandler([this] {// 注册热插拔的回调const std::lock_guard<std::mutex> lock(GetMainLock());UpdateFrontendDisplays();});//详见章节2.7UpdateFrontendDisplays();//这里会Send Hotplug Event To Client,SF会收到一次onComposerHalHotplug// attached_pipelines_的初始化、更新initialized_ = true; // 设置标记,表明已经初始化过了
}

重点来看下AddDrmDevice:

AddDrmDevice

[drm-hwcomposer/drm/ResourceManager.cpp]
int ResourceManager::AddDrmDevice(const std::string &path) {auto drm = std::make_unique<DrmDevice>();//创建DrmDevice对象int ret = drm->Init(path.c_str());//初始化DrmDevice,path一般就是/dev/dri/card0drms_.push_back(std::move(drm));//保存到drms_中,便于后续的DeleteDrmDevices删除return ret;
}

2.6 DrmDevice的实现

一个重要的角色登场DrmDevice,我们可以理解它是对DRM设备进行抽象描述的一个类,用来后续的送显示,如下是其定义:

image

这里我们先看DrmDevice的实现,DrmDevice的构造函数中创建一个 DrmFbImporter 对象

[drm-hwcomposer/drm/DrmDevice.cpp]
DrmDevice::DrmDevice() {drm_fb_importer_ = std::make_unique<DrmFbImporter>(*this);
}

这里的DrmFbImporter后面会用到,用于后续DRM/KMS显示流程!

接下来我们继续往下看,看看DrmDevice::Init的实现逻辑,它主要完成了完成了获取DRM资源的初始化,CRTC、Encoder、Connector、Plane这些资源都获取到了!完美抽象出下面的框图!

image

//drm/DrmDevice.cpp
auto DrmDevice::Init(const char *path) -> int {/* TODO: Use drmOpenControl here instead */fd_ = UniqueFd(open(path, O_RDWR | O_CLOEXEC));//打开设备,一般是/dev/dri/card0if (!fd_) {//异常处理// NOLINTNEXTLINE(concurrency-mt-unsafe): FixmeALOGE("Failed to open dri %s: %s", path, strerror(errno));return -ENODEV;}//通用设置  设置DRM_CLIENT_CAP_UNIVERSAL_PLANES,获取所有支持的Plane资源int ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);if (ret != 0) {ALOGE("Failed to set universal plane cap %d", ret);return ret;}//通用设置 设置DRM_CLIENT_CAP_ATOMIC,告知DRM驱动该应用程序支持Atomic操作ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_ATOMIC, 1);if (ret != 0) {ALOGE("Failed to set atomic cap %d", ret);return ret;}#ifdef DRM_CLIENT_CAP_WRITEBACK_CONNECTORS// 设置开启 writebackret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);if (ret != 0) {ALOGI("Failed to set writeback cap %d", ret);}
#endifuint64_t cap_value = 0;if (drmGetCap(GetFd(), DRM_CAP_ADDFB2_MODIFIERS, &cap_value) != 0) {ALOGW("drmGetCap failed. Fallback to no modifier support.");cap_value = 0;}HasAddFb2ModifiersSupport_ = cap_value != 0;//是否支持Add Fb2 ModifiersdrmSetMaster(GetFd());if (drmIsMaster(GetFd()) == 0) {ALOGE("DRM/KMS master access required");return -EACCES;}//获取DrmModeResauto res = MakeDrmModeResUnique(GetFd());if (!res) {ALOGE("Failed to get DrmDevice resources");return -ENODEV;}// 最小和最大的分辨率min_resolution_ = std::pair<uint32_t, uint32_t>(res->min_width,res->min_height);max_resolution_ = std::pair<uint32_t, uint32_t>(res->max_width,res->max_height);// 获取所有的CRTC,创建DrmCrtc对象,并加入crtcs_这个vector<unique_ptr<DrmCrtc>> for (int i = 0; i < res->count_crtcs; ++i) {// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)auto crtc = DrmCrtc::CreateInstance(*this, res->crtcs[i], i);if (crtc) {crtcs_.emplace_back(std::move(crtc));}}//获取Encoder 建DrmEncoder对象,并加入encoders_这个vector<unique_ptr<DrmEncoder>>for (int i = 0; i < res->count_encoders; ++i) {// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)auto enc = DrmEncoder::CreateInstance(*this, res->encoders[i], i);if (enc) {encoders_.emplace_back(std::move(enc));}}// 获取所有的Connector,创建DrmConnector对象,并加入connectors_这个vector<unique_ptr<DrmConnector>>// 或放入writeback_connectors_这个vector中for (int i = 0; i < res->count_connectors; ++i) {// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)auto conn = DrmConnector::CreateInstance(*this, res->connectors[i], i);if (!conn) {continue;}if (conn->IsWriteback()) {writeback_connectors_.emplace_back(std::move(conn));} else {connectors_.emplace_back(std::move(conn));}}// 获取drmModePlaneResauto plane_res = MakeDrmModePlaneResUnique(GetFd());if (!plane_res) {ALOGE("Failed to get plane resources");return -ENOENT;}// 获取所有的Plane,创建DrmPlane对象,并加入planes_这个vector<unique_ptr<DrmPlane>>for (uint32_t i = 0; i < plane_res->count_planes; ++i) {// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)auto plane = DrmPlane::CreateInstance(*this, plane_res->planes[i]);if (plane) {planes_.emplace_back(std::move(plane));}}return 0;
}

2.7 UpdateFrontendDisplays

让我们梦回大唐,错了让我们回到ResourceManager::Init()中,在最后的逻辑中调用了UpdateFrontendDisplays方法.

//drm/ResourceManager.cppauto ResourceManager::GetOrderedConnectors() -> std::vector<DrmConnector *> {/* Put internal displays first then external to* ensure Internal will take Primary slot*/std::vector<DrmConnector *> ordered_connectors;for (auto &drm : drms_) {for (const auto &conn : drm->GetConnectors()) {// 判断当前连接器是否为内部连接器// 如果是内部连接器,则将其添加到ordered_connectors中if (conn->IsInternal()) {ordered_connectors.emplace_back(conn.get());}}}for (auto &drm : drms_) {for (const auto &conn : drm->GetConnectors()) {// 判断当前连接器是否为外部连接器// 如果是外部连接器,则将其添加到ordered_connectors中if (conn->IsExternal()) {ordered_connectors.emplace_back(conn.get());}}}return ordered_connectors;
}void ResourceManager::UpdateFrontendDisplays() {//获取所有的连接器,并按照连接器的优先级进行排序  internal displays放前面,external放后面的排序connectorsauto ordered_connectors = GetOrderedConnectors();for (auto *conn : ordered_connectors) {conn->UpdateModes();bool connected = conn->IsConnected();// std::map<DrmConnector *, std::unique_ptr<DrmDisplayPipeline>>attached_pipelines_;bool attached = attached_pipelines_.count(conn) != 0;// 判断map中是否存在key为conn的元素if (connected != attached) {ALOGI("%s connector %s", connected ? "Attaching" : "Detaching",conn->GetName().c_str());if (connected) {// connected==true and attached == false,// 说明当前连接器需要被绑定到frontend上// 创建一个DrmDisplayPipeline对象,并将其绑定到frontend_interface_上// 然后将该对象添加到attached_pipelines_中// 最后调用frontend_interface_的BindDisplay方法将该对象绑定到frontend上auto pipeline = DrmDisplayPipeline::CreatePipeline(*conn);//frontend_interface_指向DrmHwcTwo对象frontend_interface_->BindDisplay(pipeline.get());attached_pipelines_[conn] = std::move(pipeline);} else {// connected==false and attached == true,解除// 当前连接器与frontend的绑定关系// 首先从attached_pipelines_中移除该连接器// 然后调用frontend_interface_的UnbindDisplay方法解除绑定auto &pipeline = attached_pipelines_[conn];frontend_interface_->UnbindDisplay(pipeline.get());attached_pipelines_.erase(conn);}}}// 最后调用frontend_interface_的FinalizeDisplayBinding方法完成绑定操作frontend_interface_->FinalizeDisplayBinding();
}

我们接着继续看下DrmDisplayPipeline::CreatePipeline是如何构建DrmDisplayPipeline的,以及它DrmDisplayPipeline的定义!

//drm/DrmDisplayPipeline.h
/*** @brief * 主要用于封装drm相关的组件,包括plane、encoder、crtc等* 1. 创建一个DrmDisplayPipeline对象* 2. 获取可用的planes* 3. 返回一个DrmDisplayPipeline对象*/
struct DrmDisplayPipeline {static auto CreatePipeline(DrmConnector &connector)-> std::unique_ptr<DrmDisplayPipeline>;auto GetUsablePlanes()-> std::vector<std::shared_ptr<BindingOwner<DrmPlane>>>;DrmDevice *device;std::shared_ptr<BindingOwner<DrmConnector>> connector;std::shared_ptr<BindingOwner<DrmEncoder>> encoder;std::shared_ptr<BindingOwner<DrmCrtc>> crtc;std::shared_ptr<BindingOwner<DrmPlane>> primary_plane;std::unique_ptr<DrmAtomicStateManager> atomic_state_manager;
};//drm/DrmDisplayPipeline.cpp
DrmDisplayPipeline::CreatePipelineTryCreatePipelineUsingEncoderTryCreatePipelinestatic auto TryCreatePipeline(DrmDevice &dev, DrmConnector &connector,DrmEncoder &enc, DrmCrtc &crtc)-> std::unique_ptr<DrmDisplayPipeline> {/* Check if resources are available */auto pipe = std::make_unique<DrmDisplayPipeline>();pipe->device = &dev;pipe->connector = connector.BindPipeline(pipe.get());pipe->encoder = enc.BindPipeline(pipe.get());pipe->crtc = crtc.BindPipeline(pipe.get());if (!pipe->connector || !pipe->encoder || !pipe->crtc) {return {};}            

这里的DrmDisplayPipeline,就是drm下kms显示通路的一个管道,将crtc,encoder,connector组合起来的一个类!


2.8 DrmHwcTwo如何处理HwcDisplay的构建

我们接着接续看下DrmHwcTwo是如何继续处理HwcDisplay,这其中牵涉到几个重要的成员和方法:

//hwc2_device/DrmHwcTwo.h
inline constexpr uint32_t kPrimaryDisplay = 0;   class HwcDisplay {    uint32_t last_display_handle_ = kPrimaryDisplay;//typedef uint64_t hwc2_display_t;//typedef uint64_t hwc2_display_t;std::map<hwc2_display_t, std::unique_ptr<HwcDisplay>> displays_;std::map<DrmDisplayPipeline *, hwc2_display_t> display_handles_;// PipelineToFrontendBindingInterface//创建HwcDisplaybool BindDisplay(DrmDisplayPipeline *pipeline) override;//销毁HwcDisplaybool UnbindDisplay(DrmDisplayPipeline *pipeline) override;//完成显示绑定,发送display插拔事件给SurfaceFlinger,告知SurfaceFlinger当前显示设备的状态void FinalizeDisplayBinding() override;
}

我们先看看BindDisplay和UnbindDisplay的实现!

//hwc2_device/DrmHwcTwo.cpp
bool DrmHwcTwo::BindDisplay(DrmDisplayPipeline *pipeline) {if (display_handles_.count(pipeline) != 0) {ALOGE("%s, pipeline is already used by another display, FIXME!!!: %p",__func__, pipeline);return false;}uint32_t disp_handle = kPrimaryDisplay;if (displays_.count(kPrimaryDisplay) != 0 &&!displays_[kPrimaryDisplay]->IsInHeadlessMode()) {disp_handle = ++last_display_handle_;}if (displays_.count(disp_handle) == 0) {/* Create a new HwcDisplay */auto disp = std::make_unique<HwcDisplay>(disp_handle,HWC2::DisplayType::Physical, this);//填充displays_容器displays_[disp_handle] = std::move(disp);}ALOGI("Attaching pipeline '%s' to the display #%d%s",pipeline->connector->Get()->GetName().c_str(), (int)disp_handle,disp_handle == kPrimaryDisplay ? " (Primary)" : "");//给HwcDisplay设置pipelinedisplays_[disp_handle]->SetPipeline(pipeline);//填充display_handles_容器display_handles_[pipeline] = disp_handle;return true;
}bool DrmHwcTwo::UnbindDisplay(DrmDisplayPipeline *pipeline) {if (display_handles_.count(pipeline) == 0) {ALOGE("%s, can't find the display, pipeline: %p", __func__, pipeline);return false;}auto handle = display_handles_[pipeline];display_handles_.erase(pipeline);ALOGI("Detaching the pipeline '%s' from the display #%i%s",pipeline->connector->Get()->GetName().c_str(), (int)handle,handle == kPrimaryDisplay ? " (Primary)" : "");if (displays_.count(handle) == 0) {ALOGE("%s, can't find the display, handle: %" PRIu64, __func__, handle);return false;}displays_[handle]->SetPipeline(nullptr);/* We must defer display disposal and removal, since it may still have pending* HWC_API calls scheduled and waiting until ueventlistener thread releases* main lock, otherwise transaction may fail and SF may crash*/if (handle != kPrimaryDisplay) {displays_for_removal_list_.emplace_back(handle);}return true;
}

上述的主要逻辑就是通过前面构建出来的DrmDisplayPipeline和新构建的HwcDisplay填充displays_和display_handles_容器。这里我们接着看下HwcDisplay的实现!

//hwc2_device/HwcDisplay.h
/*** @brief * 这里的HwcDisplay是HWC2::Display在HAL层的实现,* 它负责管理一个Display的配置和状态,* 包括创建和销毁Layer,* 以及处理Display的配置和状态变化。* * 它还负责处理Display的HWC Hooks,* 例如AcceptDisplayChanges、CreateLayer、DestroyLayer等。* * 它还负责处理Display的VSync事件,* 例如处理VSync事件、发送VSync事件等。* * 它还负责处理Display的HWC2::Error,* 例如处理HWC2::Error等。**/
class HwcDisplay {HwcDisplay(hwc2_display_t handle, HWC2::DisplayType type, DrmHwcTwo *hwc2);/* SetPipeline should be carefully used only by DrmHwcTwo hotplug handlers */void SetPipeline(DrmDisplayPipeline *pipeline);HWC2::Error CreateComposition(AtomicCommitArgs &a_args);std::vector<HwcLayer *> GetOrderLayersByZPos();// HWC HooksHWC2::Error AcceptDisplayChanges();HWC2::Error CreateLayer(hwc2_layer_t *layer);  const Backend *backend() const;void set_backend(std::unique_ptr<Backend> backend);  
}

这里要怎么理解这个HwcDisplay呢?这里的HwcDisplay是HWC2::Display在HAL层的实现,它的核心功能主要是:

  • 它负责管理一个Display的配置和状态,包括创建和销毁Layer,以及处理Display的配置和状态变化。
  • 它还负责处理Display的HWC Hooks,例如AcceptDisplayChanges、CreateLayer、DestroyLayer等。
  • 它还负责处理Display的VSync事件,例如处理VSync事件、发送VSync事件等。
  • 它还负责处理Display的HWC2::Error,例如处理HWC2::Error等。

我们接着继续看下HwcDisplay的构造,比较简单:

HwcDisplay::HwcDisplay(hwc2_display_t handle, HWC2::DisplayType type,DrmHwcTwo *hwc2): hwc2_(hwc2),//关联的DrmHwcTwo对象handle_(handle),//typedef uint64_t hwc2_display_t;   handle本质就是一个uint64_t整数值type_(type),// Physical 物理屏幕color_transform_hint_(HAL_COLOR_TRANSFORM_IDENTITY) {...
}

接着继续来看下HwcDisplay::SetPipeline

void HwcDisplay::SetPipeline(DrmDisplayPipeline *pipeline) {pipeline_ = pipeline;if (pipeline != nullptr) {//会进入这个分支ChosePreferredConfig();Init();//调用HwcDisplay::Init()逻辑hwc2_->ScheduleHotplugEvent(handle_, /*connected = */ true);} else {...}
}HWC2::Error HwcDisplay::Init() {if (!IsInHeadlessMode()) {//通过后端管理为HwcDisplay设置后端,这个后端是干什么的呢  ret = BackendManager::GetInstance().SetBackendForDisplay(this);if (ret) {ALOGE("Failed to set backend for d=%d %d\n", int(handle_), ret);return HWC2::Error::BadDisplay;}}
}

这里又是后端管理BackendManager,又是后端Backend!要怎么理解呢?

既然有了backend,那么肯定有frontend,那么谁是backend ,谁是frontend? 扮演的角色功能分别是什么?

初步看起来貌似是:

  • frontend 对外提供调用的接口,外部使用者呼叫 front end 暴漏出的接口来呼叫某一功能;
  • backend 内部的实现逻辑,是前端接口功能的内部实现,是真正做事的地方;

太多了,太多了。完全放在一篇里面不够。今天先这样了,后续drm_hwcomposer另起一篇完成余下的相关分析!


相关文章:

Android下HWC以及drm_hwcomposer普法(上)

Android下HWC以及drm_hwcomposer普法(上) 引言 按摩得全套&#xff0c;错了&#xff0c;做事情得全套&#xff0c;普法分析也是如此。drm_hwcomposer如果对Android图形栈有一定研究的童鞋们应该知道它是Android提供的一个的图形后端合成处理HAL模块的实现。但是在分析这个之前…...

OpenCV学习 基础图像操作(十七):泛洪与分水岭算法

原理 泛洪填充算法和分水岭算法是图像处理中的两种重要算法&#xff0c;主要用于区域分割&#xff0c;但它们的原理和应用场景有所不同&#xff0c;但是他们的基础思想都是基于区域迭代实现的区域之间的划分。 泛洪算法 泛洪填充算法&#xff08;Flood Fill&#xff09;是一…...

Docker基础命令(三)

同步docker容器中的时间和本地时间一致 背景: 在很多时候, 训练模型的时候, 记录的log日志中标记的时间和实际的时间不一致, 往往是容器时间和本地时间不一致照成的. 方案 场景一: 正在运行的容器&#xff0c;可以宿主机直接执行命令给某个容器同步时间 #方法1 直接在宿主机…...

Python的第三方库OS库

读者大大们好呀&#xff01;&#xff01;!☀️☀️☀️ &#x1f525; 欢迎来到我的博客 &#x1f440;期待大大的关注哦❗️❗️❗️ &#x1f680;欢迎收看我的主页文章➡️寻至善的主页 文章目录 &#x1f525;前言&#x1f680;OS/SHUTIL 的方法描述&#x1f680;OS/SHUTIL…...

两个数相加减高级实现

不使用实现两数相加 #include <stdio.h> int plus(int a, int b) {int sum0,addication0;while(b!0){sum a^b;addication (a&b)<<1;a sum;b addication;}return a; }不使用-实现两数相减 #include <stdio.h> int sub(int a, int b) {int sum0,abdc…...

小白跟做江科大32单片机之对射式红外传感器计次

原理部分 1中断示意图&#xff0c;中断会打断主函数的执行&#xff0c;终端执行完成之后再返回主函数继续执行 2.STM32中断 这些灰色的是内核中断 这些白色的是普通中断 3.NVIC统一管理中断&#xff0c;每个中断通道都拥有16个可编程的优先等级&#xff0c;可对优先级进行分组…...

安装Kubernetes v3 ----以docker的方式部署

以docker的方式部署 docker run -d \ --restartunless-stopped \ --namekuboard \ -p 80:80/tcp \ -p 10081:10081/tcp \ -e KUBOARD_ENDPOINT"http://192.168.136.55:80" \ -e KUBOARD_AGENT_SERVER_TCP_PORT"10081" \ -v /root/kuboard-data:/data \ e…...

新游启航 失落的方舟台服注册指南 一文教会你方舟台服注册

新游启航&#xff01;失落的方舟台服注册指南&#xff01;一文教会你方舟台服注册 失落的方舟作为本月最受期待游戏之一&#xff0c;在上线之际许多玩家已经有点急不可待了。这款游戏是由开发商Smile gate开发的一款MMORPG类型游戏&#xff0c;这款游戏的基本玩法与其他MMORPG…...

运维开发详解

运维开发&#xff08;DevOps&#xff09;是开发&#xff08;Development&#xff09;和运维&#xff08;Operations&#xff09;的结合&#xff0c;旨在通过自动化和协作提高软件开发和交付的效率、速度和可靠性。DevOps强调文化、实践和工具的融合&#xff0c;以实现持续集成&…...

英伟达(NVIDIA)H100性能及应用场景

英伟达H100是一款性能强大的GPU芯片&#xff0c;其关键性能参数和应用领域可以归纳如下&#xff1a; 一、性能参数 架构&#xff1a;H100采用了新一代的Hopper架构&#xff0c;拥有高达1.8万亿次/秒的张量处理能力和高达840 TFLOPS的FP8张量性能。CUDA核心数&#xff1a;H100…...

充电宝怎么选?充电宝目前什么牌子质量好耐用?盘点好用充电宝

充电宝怎么选&#xff1f;是不是很多朋友在选充电宝上非常的纠结&#xff1f;在买充电宝上面还是非常有讲究的&#xff01;市面上的充电宝虽然多&#xff0c;但是不排除很多存在安全隐患的&#xff0c;如果稍微没有挑选好充电宝的话&#xff0c;买来的充电宝极大可能是存在非常…...

智能视频监控技术为游泳馆安全护航,助力安全管理新升级

随着社会的进步和科技的发展&#xff0c;视频监控技术在各行各业的应用越来越广泛。游泳馆作为公共场所&#xff0c;每天都会有大量的游泳者进出。在这样的环境中&#xff0c;有时难免会发生一些意外事故&#xff0c;如溺水、摔倒等。因此&#xff0c;视频监控建设的必要性尤为…...

Windows通过cmd运行快速启动应用

Windows如何通过cmd运行快速启动应用&#xff1f; 在Windows操作系统中&#xff0c;可以通过配置环境变量的方式将文件的路径配置到环境变量的path中&#xff0c;配置完成后可以在cmd中输入对应的应用名称即可启动应用&#xff0c;具体操作如下&#xff1a; 1. 添加应用程序路径…...

271 基于matlab的可调Q因子小波变换故障诊断

基于matlab的可调Q因子小波变换故障诊断&#xff0c;可用在轴承、齿轮、活塞等故障诊断中&#xff0c;程序中包含了原始TQWT工具箱和轴承振动信号信号的谱包络的求取。通过仿真数据、实际轴承数据说明了方法的效果。程序已调通&#xff0c;可直接运行。 271 可调Q因子小波变换 …...

吴恩达深度学习个人笔记

1. 上一个视频提到的房地产领域,我们不就使用了一个普遍标准神经网络架构吗 而对于图像识别处理问题,我们则要使用卷积神经网络(Convolution Neural Network),即CNN。 对于序列数据,例如音频,有一个时间组件,随着时间的推移,音频被播放出来, 所以音频是最自然的表…...

Spring的依赖注入DI

调用构造器方法注入: 需要有对应匹配的构造器 不然出现异常No matching constructor Spring构造器注入有三种不同方式 name :按照参数名称 <bean id"stu" class"com.xja.bean.Student"> <constructor-arg name"name" value"rk&…...

Java 获取和修改期日与时间的各种操作方法

LocalDateTime获取当地日期和时间 import java.time.LocalDateTime; /*LocalDateTime.now() 获取当前时间*/ public class LocalDateTimeDemo {public static void main(String[] args) {LocalDateTime time1 LocalDateTime.now();System.out.println(time1);//2024-06-01T13…...

【ubuntu20】--- 定时同步文件

在编程的艺术世界里&#xff0c;代码和灵感需要寻找到最佳的交融点&#xff0c;才能打造出令人为之惊叹的作品。而在这座秋知叶i博客的殿堂里&#xff0c;我们将共同追寻这种完美结合&#xff0c;为未来的世界留下属于我们的独特印记。 【Linux命令】--- 多核压缩命令大全&…...

网吧|基于SprinBoot+vue的网吧管理系统(源码+数据库+文档)

网吧管理系统 目录 基于SprinBootvue的网吧管理系统 一、前言 二、系统设计 三、系统功能设计 1 管理员功能模块 2 网管功能模块 3 会员功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#…...

[C/C++] -- Libcurl开发

libcurl 是一个功能强大的 C 语言库&#xff0c;用于实现各种网络传输协议的客户端功能。它是 Curl 工具的核心&#xff0c;并提供了一个简单、灵活、高效的 API&#xff0c;允许开发人员在他们的应用程序中轻松地执行网络操作。 以下是 libcurl 的一些主要特点和功能&#xf…...

Streamsets-JDBC模式使用更新时间字段数据同步

StreamSets的开源地址&#xff1a;https://github.com/streamsets/datacollector-oss Streamsets官网地址&#xff1a;https://streamsets.com/ Streamsets文档地址&#xff1a;https://docs.streamsets.com/portal/datacollector/3.16.x/help/index.html 我又来写Streamsets了…...

Nodejs-- 网络编程

网络编程 构建tcp服务 TCP tcp全名为传输控制协议。再osi模型中属于传输层协议。 tcp是面向连接的协议&#xff0c;在传输之前需要形成三次握手形成会话 只有会话形成了&#xff0c;服务端和客户端才能想发送数据&#xff0c;在创建会话的过程中&#xff0c;服务端和客户…...

React@16.x(14)context 举例 - Form 表单

目录 1&#xff0c;目标2&#xff0c;实现2.1&#xff0c;index.js2.2&#xff0c;context.js2.2&#xff0c;Form.Input2.3&#xff0c;Form.Button 3&#xff0c;使用 1&#xff0c;目标 上篇文章说到&#xff0c;context 上下文一般用于第3方组件库&#xff0c;因为使用场景…...

十几款基于ChatGPT的免费神器,每个都是王炸!

十几款基于ChatGPT的免费神器&#xff0c;每个都是王炸&#xff01; 1、ChatGPT ChatGPT非常强大&#xff0c;但注册需要魔法和国外的手机号&#xff0c;大部分人都没法使用。还好有一些基于API开发的体验版&#xff0c;我收集了一些可以直接使用的站点分享给大家&#xff0c…...

devicemotion 或者 deviceorientation在window.addEventListener 事件中不生效,没有输出内容

问题&#xff1a;devicemotion 或者 deviceorientation 在window.addEventListener 事件中不生效&#xff0c;没有输出内容 原因&#xff1a; 1、必须在Https协议下才可使用 2、必须用户手动点击click事件中调用 &#xff0c;进行权限申请 源码&#xff1a; <!DOCTYPE h…...

java单元测试如何断言异常

​ 在junit单元测试中&#xff0c;我们可以使用 org.junit.Assert.assertThrows 包下的 assertThrows() 方法 这个方法返回了一个泛型的异常 public static <T extends Throwable> assertThrows(Class<T> expectedType, Executable executable)​ 假设我们有以下…...

C语言| n的阶乘相加

逻辑性较强&#xff0c;建议记住。 分析思路&#xff1a; 假如n4&#xff1a;m m * i; sum sum m; 1&#xff09;当i1时&#xff0c;m1, sum1。 2&#xff09;当i2时&#xff0c;m12, sum112。 3&#xff09;当i3时&#xff0c;m123, sum112123。 4&#xff09;当i4时&…...

cwiseMax、cwiseMin函数

一、cwiseMax含义 cwiseMax是Eigen库中的一个函数&#xff0c;用于求两个矩阵或向量的逐元素最大值。它的作用类似于std::max函数&#xff0c;但是可以同时处理多个元素&#xff0c;且支持矩阵和向量。 举例&#xff1a; 例如&#xff0c;对于两个向量a和b&#xff0c;cwiseMax…...

【thinkphp问题栏】tp5.1重写URL,取消路径上的index.php

在Apache运行thinkphp5.1时&#xff0c;发现系统默认生成的.htaccess不生效。 首先先查看怎么修改伪静态 1、修改Apache的配置文件 在Apache的安装目录下&#xff0c;打开config/httpd.conf。 搜索rewrite.so&#xff0c;将前面的#删掉&#xff0c;表示开启URL重写功能 2、…...

缓冲字符流

BufferedReader/BufferedWriter增加了缓存机制&#xff0c;大大提高了读写文本文件的效率。 字符输入缓冲流 BufferedReader是针对字符输入流的缓冲流对象&#xff0c;提供了更方便的按行读取的方法&#xff1a;readLine();在使用字符流读取文本文件时&#xff0c;我们可以使…...

Django中使用Celery和APScheduler实现定时任务

在之前的文章我们已经学习了Celery和APScheduler的基本使用&#xff0c;下面让我们来了解一下如何在Django中使用Celery和APScheduler Celery 1.前提工作 python 3.7 pip install celery pip install eventlet #5.0版本以下 pip install importlib-metadata4.8.3&#xff08…...

Kivy.uix.textinput

一个小小的输入框&#xff0c;纵上下数页文档已不能全不概括&#xff0c;当去源码慢慢寻找&#xff0c;才知道其中作用&#xff0c;才能运用灵活。 Text Input — Kivy 2.3.0 documentation # -*- encoding: utf-8 -*-Text Input .. versionadded:: 1.0.4.. image:: images/te…...

基于IoTDB 平台的学习和研究

Apache IoTDB&#xff08;物联网数据库&#xff09;是一个针对物联网领域的高性能原生数据库&#xff0c;适用于数据管理和分析&#xff0c;并可在边缘计算和云端部署。由于它轻量级的架构、高性能和丰富的功能集&#xff0c;以及与Apache Hadoop、Spark和Flink的深度集成&…...

nessus plugins目录为空的问题

想要避免这种问题&#xff0c;可以将nessus服务设置为手动&#xff0c;并且先停止nessus服务。 批处理脚本&#xff1a; 下面的/~/Nessus/plugin_feed_info.inc替换成你配置好的 plugin_feed_info.inc 所在的路径 service nessusd stop; cp /~/Nessus/plugin_feed_info.inc …...

FDW(Foreign Data Wrapper)

在上一篇博客里&#xff0c;最末尾提到了 FDW。pg 实现了数百个 fdw 插件&#xff0c;用于访问外部数据。 FDW 到底是什么呢&#xff1f; 标准 FDW&#xff08;Foreign Data Wrapper&#xff09;遵循了 SQL/MED 标准&#xff0c;标准全称&#xff1a;ISO/IEC 9075-9 Managem…...

Flutter开发指南

Flutter开发指南&#xff08;Android 开发角度&#xff09; 与Android 的对比 1.Android 的View 与Flutter 的对应关系&#xff1a; a.在android 中&#xff0c;view 是屏幕显示的基础&#xff0c;比如 button&#xff0c;文本&#xff0c;列表&#xff0c;输入框都是 view。…...

SpringCloud学习笔记万字整理(无广版在博客)

在此感谢黑马程序员的SpringCloud课程 所有笔记、生活分享首发于个人博客 想要获得最佳的阅读体验&#xff08;无广告且清爽&#xff09;&#xff0c;请访问本篇笔记 认识微服务 随着互联网行业的发展&#xff0c;对服务的要求也越来越高&#xff0c;服务架构也从单体架构逐渐…...

c++(七)

c&#xff08;七&#xff09; 内联函数内联函数的特点为什么要有内联函数内联函数是如何工作的呢 类型转换异常处理智能指针单例模式懒汉模式饿汉模式 VS中数据库的相关配置 内联函数 修饰类的成员函数&#xff0c;关键字&#xff1a;inline inline 返回值类型 函数名(参数列…...

SQL语言

SQL语言 导航 文章目录 SQL语言导航一、SQL概述SQL 二、数据库定义SQL 数据类型 三、数据操作视图更新 四、SQL的授权五、存储过程六、嵌入式SQL主语言与数据库通信 七、动态SQL 一、SQL概述 SQL 支持三级模式结构 视图->外模式 基本表->模式 存储文件->内模式 二…...

【PPT】修改新建文本框默认字体

【PPT】修改新建文本框默认字体...

智能办公本如何选择

智能办公本如何选择 引言 随着科技的不断发展&#xff0c;智能办公本已成为现代职场人士的重要工具。它们不仅具备传统纸质笔记本的书写体验&#xff0c;还融入了先进的智能技术&#xff0c;让办公变得更加高效便捷。在选择智能办公本时&#xff0c;我们需要关注多个方面&…...

Spark基础:Scala变量与数据类型

在Scala中&#xff0c;变量和数据类型是编程的基础。Scala作为一种强大的静态类型语言&#xff0c;支持多种数据类型&#xff0c;并提供了可变&#xff08;var&#xff09;和不可变&#xff08;val&#xff09;两种类型的变量声明方式。以下是在Scala中变量和数据类型的基础知识…...

php 实现:给图片加文字水印,图片水印,压缩图片

演示环境&#xff1a; 1、windows10 2、phpstudy 3、php7.4 一、案例演示&#xff1a; 二、素材准备 1、准备一张原始图片 2、准备一张水印图片&#xff08;透明底图的最好&#xff09; 3、字体库&#xff08;windows系统自带的字体库&#xff0c;路径在&#xff1a;C:\Window…...

免费实现网站HTTPS访问

HTTPS&#xff08;Hypertext Transfer Protocol Secure&#xff09;是一种基于SSL协议的HTTP安全协议&#xff0c;旨在为客户端&#xff08;浏览器&#xff09;与服务器之间的通信提供加密通道&#xff0c;确保数据在传输过程中的保密性、完整性和身份验证。与传统的HTTP相比&a…...

vue3使用vue3-print-nb打印

打印效果 1.下载插件 Vue2.0版本安装方法 npm install vue-print-nb --saveVue3.0版本安装方法&#xff1a; npm install vue3-print-nb --save2.main.js引入 vue2引入 import Print from vue-print-nb Vue.use(Print)vue3引入 import print from vue3-print-nb // 打印…...

R语言ggplot2包绘制网络地图

重要提示&#xff1a;数据和代码获取&#xff1a;请查看主页个人信息&#xff01;&#xff01;&#xff01; 载入R包 rm(listls()) pacman::p_load(tidyverse,assertthat,igraph,purrr,ggraph,ggmap) 网络节点和边数据 nodes <- read.csv(nodes.csv, row.names 1) edges…...

php获取今天凌晨零点的时间

不废话直接上代码 //使用strtotime $midnightToday strtotime("today midnight"); //输出&#xff1a;1716998400 //如果是明天 $midnightToday 86400 //后天 $midnightToday 86400*2//ORM中比对使用 $row ModelVisit::where(uid,$this->uid)->where(visi…...

CATIA进阶操作——创成式曲面设计入门(1)线架设计,三维点、直线、平面、曲线

目录 引出三维空间点生成三维直线三维平面三维曲线总结异形弹簧新建几何体草图编辑&#xff0c;画一条样条线进行扫掠&#xff0c;圆心和半径画出曲面上的螺旋线再次选择扫掠&#xff0c;圆心和半径 其他自定义信号和槽1.自定义信号2.自定义槽3.建立连接4.进行触发 自定义信号重…...

thinkphp6中怎么查看ThinkPHP版本号

<?php namespace app\controller; use app\BaseController; use think\app; //这句 class Index extends BaseController { public function test() { echo App::VERSION; //还有这句 } }...

第十二章 创建Web客户端

文章目录 第十二章 创建Web客户端SOAP向导的概述使用SOAP向导 第十二章 创建Web客户端 web客户端是访问web服务的软件。web客户端提供了一组代理方法&#xff0c;每个方法对应于web服务的一个方法。代理方法使用与它所对应的web服务方法相同的签名&#xff0c;并在被请求时调用…...