Android 12(S) 图像显示系统 - drm_hwcomposer 简析(上)
必读:
Android 12(S) 图像显示系统 - 开篇
前言
Android源码中有包含drm_hwcomposer:/external/drm_hwcomposer/
drm_hwcomposer 这个过程下的代码架构变化还是很频繁的,我这里分析直接去 drm_hwcomposer 的官方地址抓取最新的code来做分析了
解析
这个工程编译后会产生 shared library :/vendor/lib/hw/hwcomposer.drm.so
drm_hwcomposer作为一个HAL module,其写作实现还是遵循了旧有的Android HAL Module的接口实现规则。
看看一些结构体的定义以及他们之间的关系:
结构体hw_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_t的定义
[/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;结构体DrmHwc2Device的定义
[drm-hwcomposer/hwc2_device/hwc2_device.cpp]
struct Drmhwc2Device : hwc2_device {
    DrmHwcTwo drmhwctwo;
};按照结构体定义的理解,我们可以认为三个类型,具有如下继承关系

本文作者@二的次方 2022-07-05 发布于博客园
看一个关键的static方法 HookDevOpen,该方法中会去实例化一个Drmhwc2Device对象,其中去创建了一个DrmHwcTwo对象
[drm-hwcomposer/hwc2_device/hwc2_device.cpp]
static int HookDevOpen(const struct hw_module_t *module, const char *name,
                       struct hw_device_t **dev) {
  ...
  auto ctx = std::make_unique<Drmhwc2Device>();
  if (!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;
  *dev = &ctx.release()->common;
  return 0;
}在HWC HAL Service启动时,初始化阶段openDeviceWithAdapter中去调用了open函数,就是call到了HookDevOpen可以参见:
/hardware/interfaces/graphics/composer/2.1/utils/passthrough/include/composer-passthrough/2.1/HwcLoader.h
DrmHwcTwo构造时做了什么工作?
[drm-hwcomposer/hwc2_device/DrmHwcTwo.cpp]
DrmHwcTwo::DrmHwcTwo() : resource_manager_(this){}; // DrmHwcTwo的构造函数定义
[drm-hwcomposer/hwc2_device/DrmHwcTwo.h]
ResourceManager resource_manager_; // DrmHwcTwo类中的成员很简单,就是去实例化一个ResourceManager对象,其构造函数中处理初始化了uevent_listener等成员,也没啥了
frontend_interface_指向DrmHwcTwo对象
[drm-hwcomposer/drm/ResourceManager.cpp]
ResourceManager::ResourceManager(
    PipelineToFrontendBindingInterface *p2f_bind_interface)
    : frontend_interface_(p2f_bind_interface) {
  if (uevent_listener_.Init() != 0) {
    ALOGE("Can't initialize event listener");
  }
}到这里,我大概可以看到ResourceManager是个非常重要的核心类,他应该管理着DRM的资源。
他的定义中也定义了void Init();函数,那这个初始化函数是什么时候调用的呢? 
在这篇博文中:Android 12(S) 图像显示系统 - SurfaceFlinger的启动和消息队列处理机制(四)
讲解SurfaceFlinger的初始化过程时,设置callback给HWC,层层传递后就会调用到DrmHwcTwo::RegisterCallback
进而调用到了 resource_manager_.Init();
ResourceManager 初始化到底初始化了什么呢?
本文作者@二的次方 2022-07-05 发布于博客园
[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));
  } 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();
  });
  UpdateFrontendDisplays();//这里会Send Hotplug Event To Client,SF会收到一次onComposerHalHotplug
                                                  // attached_pipelines_的初始化、更新
  initialized_ = true; // 设置标记,表明已经初始化过了
}重点看几个函数
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/card0
    drms_.push_back(std::move(drm));// 保存到drms_这个vector中
    return ret;
}一个重要的角色登场:DrmDevice,如下其定义

DrmDevice的构造函数中创建一个 DrmFbImporter 对象
[drm-hwcomposer/drm/DrmDevice.cpp]
DrmDevice::DrmDevice() {
  drm_fb_importer_ = std::make_unique<DrmFbImporter>(*this);
}DrmDevice::Init
完成了获取DRM资源的初始化,CRTC、Encoder、Connector、Plane这些资源都获取到了
[drm-hwcomposer/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/card0
    if (!fd_) {
        // NOLINTNEXTLINE(concurrency-mt-unsafe): Fixme
        ALOGE("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;
    }
    // 设置开启 writeback
#ifdef DRM_CLIENT_CAP_WRITEBACK_CONNECTORS
    ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
    if (ret != 0) {
        ALOGI("Failed to set writeback cap %d", ret);
    }
#endif
    uint64_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 Modifiers
    // 设置master mode
    drmSetMaster(GetFd());
    if (drmIsMaster(GetFd()) == 0) {
        ALOGE("DRM/KMS master access required");
        return -EACCES;
    }
    // 获取 drmModeRes
    auto 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;
        }
        // wirteback如何理解?
        if (conn->IsWriteback()) {
            writeback_connectors_.emplace_back(std::move(conn));
        } else {
            connectors_.emplace_back(std::move(conn));
        }
    }
    // 获取drmModePlaneRes
    auto 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;
}回到ResourceManager::Init()中,最后调用了一次UpdateFrontendDisplays()
[drm-hwcomposer/drm/ResourceManager.cpp]
void ResourceManager::UpdateFrontendDisplays() {
    // internal displays放前面,external放后面的排序connectors
    auto ordered_connectors = GetOrderedConnectors();
    for (auto *conn : ordered_connectors) {
        conn->UpdateModes();
        bool connected = conn->IsConnected();
        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,绑定资源
                auto pipeline = DrmDisplayPipeline::CreatePipeline(*conn);
                if (pipeline) {
                    //frontend_interface_指向DrmHwcTwo对象
                    frontend_interface_->BindDisplay(pipeline.get());
                    attached_pipelines_[conn] = std::move(pipeline);//存入map
                }
            } else { // connected==false and attached == true,解绑资源
                auto &pipeline = attached_pipelines_[conn];
                frontend_interface_->UnbindDisplay(pipeline.get());
                attached_pipelines_.erase(conn);// map中删除
            }
        }
    }
    frontend_interface_->FinalizeDisplayBinding();
}DrmHwcTwo中的两个成员:
[drm-hwcomposer/hwc2_device/DrmHwcTwo.h]  
std::map<hwc2_display_t, std::unique_ptr<HwcDisplay>> displays_;
std::map<DrmDisplayPipeline *, hwc2_display_t> display_handles_;出现了三个函数:
DrmHwcTwo::BindDisplay
主要是创建HwcDisplay,
DrmHwcTwo::UnbindDisplay
删除HwcDisplay
DrmHwcTwo::FinalizeDisplayBinding
完成显示绑定,大概看是Creating null-display for headless mode , send hotplug events to the client,displays_for_removal_list_
本文作者@二的次方 2022-07-05 发布于博客园
重点看一看创建HwcDisplay和SetPipeline做了啥子吧
HwcDisplay的构造函数很简单,就是初始化一些成员
[drm-hwcomposer/hwc2_device/HwcDisplay.cpp]
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) {
  // clang-format off
  color_transform_matrix_ = {1.0, 0.0, 0.0, 0.0,
                             0.0, 1.0, 0.0, 0.0,
                             0.0, 0.0, 1.0, 0.0,
                             0.0, 0.0, 0.0, 1.0};
  // clang-format on
}HwcDisplay::SetPipeline
[drm-hwcomposer/hwc2_device/HwcDisplay.cpp]
void HwcDisplay::SetPipeline(DrmDisplayPipeline *pipeline) {
    Deinit();
    pipeline_ = pipeline;
    if (pipeline != nullptr || handle_ == kPrimaryDisplay) {
        Init(); // 初始化
        hwc2_->ScheduleHotplugEvent(handle_, /*connected = */ true);
    } else {
        hwc2_->ScheduleHotplugEvent(handle_, /*connected = */ false);
    }
}再看HwcDisplay::Init
[drm-hwcomposer/hwc2_device/HwcDisplay.cpp]
HWC2::Error HwcDisplay::Init() {
    ChosePreferredConfig(); //选择一个最佳的config,然后SetActiveConfig
    // VSYNC相关的代码省略不看
    if (!IsInHeadlessMode()) {//设置后端 backend
        ret = BackendManager::GetInstance().SetBackendForDisplay(this);
        if (ret) {
            ALOGE("Failed to set backend for d=%d %d\n", int(handle_), ret);
            return HWC2::Error::BadDisplay;
        }
    }
    client_layer_.SetLayerBlendMode(HWC2_BLEND_MODE_PREMULTIPLIED);
    return HWC2::Error::None;
}又出现了新的名词: Backend
谁是 front end ? 谁是back end ? 扮演的角色功能分别是什么?
初步看起来貌似是:
front end 对外提供调用的接口,外部使用者呼叫 front end 暴漏出的接口来呼叫某一功能;
back end 内部的实现逻辑,是前端接口功能的内部实现,是真正做事的地方;
本文作者@二的次方 2022-07-05 发布于博客园
HwcDisplay类中有成员 == HwcLayer client_layer_,有个疑问 这个client layer 是如何与SF中的GPU合成的图层关联起来的?
他是一个特例,特殊的专门的的layer,转用于处理显示 CLIENT -- GPU 合成的 buffer, SetClientTarget传递buffer数据给他

小结
以上内容,主要讲述分析的是开机阶段,DRM HWC的初始化的一些流程。大概就是获取DRM的资源,创建并初始化必要模块。
Android 12(S) 图像显示系统 - drm_hwcomposer 简析(上)的更多相关文章
- Android 12(S) 图像显示系统 - 	drm_hwcomposer 简析(下)
		必读: Android 12(S) 图像显示系统 - 开篇 合成方式 合成类型的定义:/hardware/interfaces/graphics/composer/2.1/IComposerClien ... 
- Android 12(S) 图像显示系统 - GraphicBuffer同步机制 - Fence
		必读: Android 12(S) 图像显示系统 - 开篇 一.前言 前面的文章中讲解Android BufferQueue的机制时,有遇到过Fence,但没有具体讲解.这篇文章,就针对Fence这种 ... 
- Android 12(S) 图像显示系统 - 基础知识之 BitTube
		必读: Android 12(S) 图像显示系统 - 开篇 一.基本概念 在Android显示子系统中,我们会看到有使用BitTube来进行跨进程数据传递.BitTube的实现很简洁,就是一对&quo ... 
- Android 12(S) 图像显示系统 - SurfaceFlinger之VSync-上篇(十六)
		必读: Android 12(S) 图像显示系统 - 开篇 一.前言 为了提高Android系统的UI交互速度和操作的流畅度,在Android 4.1中,引入了Project Butter,即&quo ... 
- Android 12(S) 图像显示系统 - SurfaceFlinger 之 VSync - 中篇(十七)
		必读: Android 12(S) 图像显示系统 - 开篇 1 前言 这一篇文章,将继续讲解有关VSync的知识,前一篇文章 Android 12(S) 图像显示系统 - SurfaceFlinger ... 
- Android 12(S) 图像显示系统 - SurfaceFlinger  GPU合成/CLIENT合成方式 - 随笔1
		必读: Android 12(S) 图像显示系统 - 开篇 一.前言 SurfaceFlinger中的图层选择GPU合成(CLIENT合成方式)时,会把待合成的图层Layers通过renderengi ... 
- Android 12(S) 图像显示系统 - HWC HAL 初始化与调用流程
		必读: Android 12(S) 图像显示系统 - 开篇 接口定义 源码位置:/hardware/interfaces/graphics/composer/ 在源码目录下可以看到4个版本的HIDL ... 
- Android 11(R) Power HAL AIDL简析 -- 基本接口
		Android 11(R) Power HAL AIDL将分三篇文章来介绍: Android 11(R) Power HAL AIDL简析 -- 基本接口 Android 11(R) Power HA ... 
- Android 12(S) 图形显示系统 - createSurface的流程(五)
		题外话 刚刚开始着笔写作这篇文章时,正好看电视在采访一位92岁的考古学家,在他的日记中有这样一句话,写在这里与君共勉"不要等待幸运的降临,要去努力的掌握知识".如此朴实的一句话,此 ... 
随机推荐
- Envoy熔断限流实践(一)基于Rainbond插件实现熔断
			Envoy 可以作为 Sevice Mesh 微服务框架中的代理实现方案,Rainbond 内置的微服务框架同样基于 Envoy 实现.本文所描述的熔断实践基于 Rainbond 特有的插件机制实现. ... 
- [AcWing 2816] 判断子序列
			点击查看代码 #include<iostream> using namespace std; const int N = 1e5 + 10; int a[N], b[N]; int mai ... 
- Halo 开源项目学习(六):事件监听机制
			基本介绍 Halo 项目中,当用户或博主执行某些操作时,服务器会发布相应的事件,例如博主登录管理员后台时发布 "日志记录" 事件,用户浏览文章时发布 "访问文章" ... 
- LVM 逻辑卷学习
			一个执着于技术的公众号 前言 每个Linux使用者在安装Linux时都会遇到这样的困境:在为系统分区时,如何精确评估和分配各个硬盘分区的容量,因为系统管理员不但要考虑到 当前某个分区需要的容量,还要预 ... 
- 北航内核操作系统-lab1
			1.实验目的. 2.实验内容. 2.1Exercise 1.1 请修改 include.mk 文件,使交叉编译器的路径正确.之后执行 make指令,如果配置一切正确,则会在gxemul 目录下生成v ... 
- C#/VB.NET 实现Word和ODT文档相互转换
			ODT文档格式一种开放文档格式(OpenDocument Text).通常,ODT格式的文件可以使用LibreOffice Writer.MS Word或其他一些文档编辑器来打开.我们在处理文档时,可 ... 
- 【java并发编程】Lock & Condition 协调同步生产消费
			一.协调生产/消费的需求 本文内容主要想向大家介绍一下Lock结合Condition的使用方法,为了更好的理解Lock锁与Condition锁信号,我们来手写一个ArrayBlockingQueue. ... 
- CF1588F Jumping Through the Array
			在讲正解之前,先播一个小故事: xay 复杂度错误过题.将操作按照时间分块,块内他令所有置换环都必须有至少一个"黑点". 可以通过没有修改 \(p\) 操作,同时 \(p_i=i\ ... 
- 渗透测试之sql注入点查询
			一切教程在于安全防范,不在于攻击别人黑别人系统为目的 寻找sql注入点方法: 拿到网页后进行查找注入点: 1.通过单引号 ' ; 在 url 后面输入单引号进行回车(如果报错可能存在sql注入为 ... 
- CabloyJS v3.1.0支持集群及更多 🎉
			在抗疫期间,CabloyJS v3.1.0设计并开发了大量特性,并且所有相关文档已集齐.强烈建议大家试用,拍砖 特性 - 后端核心 集群: 集群现在已经成为CabloyJS的一等公民.也就是说,Cab ... 
