

Android 12(S) 图像显示系统 - drm_hwcomposer 简析(上) - 二的次方
source link: https://www.cnblogs.com/roger-yu/p/16425520.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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的资源,创建并初始化必要模块。
Recommend
-
37
在未来,每个企业、每个行业、每个国际和地区,需要的土壤是不一样的。需要的土壤养分会是定制化和经过复杂排列组合的——这是华为的未来。
-
9
这道题可以利用二进制,就可以快速解决了。 <!-- more --> 原题 实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。 示例...
-
12
苹果2项HMD专利曝光,或将包含散热及局部光学调节显示系统_VR陀螺
-
13
把显示器“穿”在身上!复旦团队自主研发全柔性织物显示系统_IT新闻_博客园 把显示器“穿”在身上!复旦团队自主研发全柔性织物显示系统 投递人
-
13
CROSS 联合主办加密艺术链上展《道的 3 次方》将于 5 月 7 日开幕,HECO 提供底层技术支持全球大型加密艺术链上展《道的 3 次方》将于 5 月 7 日开幕,展览长达 23 天,持续至 5 月 30 日。该展览由加密艺术平台 HNFT Gallery 发起,中央美院国...
-
63
Android 12(S) 图像显示系统 - SurfaceFlinger之VSync-上篇(十六) 必读:
-
59
在项目的开发中,为了定位Android显示异常的原因:GPU渲染 or GPU合成 or HWC合成送显异常的问题。我们通常会把图层的原始数据写到文件,然后通过RGB或YUV的软件工具来查看这些原始的图像数据,从而确定问题发生的大体阶段。 本文就将介绍如何dump Android渲...
-
28
必读: Android 12(S) 图像显示系统 - 开篇 源码位置:/hardware/interfaces/graphics/...
-
3
最近客户反馈了一些OOM的问题,很早之前自己也有简单了解过OOM的知识,但时间久远,很多东西都记不清了。 现在遇到这个OOM问题,也即趁此搜索了一些资料,对OOM问题做一些探究,把资料记录于此,一遍后续查阅。本文内容大量借鉴参考了网络上经典文章的内容,...
-
5
DRM framebuffer显示图像 - 迷途小书童的Note迷途小书童的Note 迷途小书童的Note >
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK