必读:

Android 12(S) 图像显示系统 - 开篇


合成方式

合成类型的定义:/hardware/interfaces/graphics/composer/2.1/IComposerClient.hal

    /** Possible composition types for a given layer. */
/** 建议去看源码中的注释,可以理解每一个type的含义 */
enum Composition : int32_t {
INVALID = 0,
CLIENT = 1,
DEVICE = 2,
SOLID_COLOR = 3,
CURSOR = 4,
SIDEBAND = 5,
};

后端的设计逻辑

有三个类定义

  1. Backend == 一个后端的实现,注册为"generic",主要是定义了ValidateDisplay方法,这个方法用来设置可见的HwcLayer应该采用什么合成方式

  2. BackendClient  ==  一个后端的实现,注册为"client",主要是定义了ValidateDisplay方法,它把所有HwcLayer都设置成立Client合成方式

  3. BackendManager == 后端的管理器,用来根据Device name从已注册的backend列表中选择一个,设置给HwcDisplay;GetBackendByName就是通过Device name来从available_backends_中选择一个匹配的Backend构造函数来构建后端对象。

HWC 中如何为每一个Layer选择合成方式

[drm-hwcomposer/hwc2_device/HwcDisplay.cpp]
HWC2::Error HwcDisplay::ValidateDisplay(uint32_t *num_types,
uint32_t *num_requests) {
if (IsInHeadlessMode()) {
*num_types = *num_requests = 0;
return HWC2::Error::None;
}
return backend_->ValidateDisplay(this, num_types, num_requests); //调用backend的方法
}

去调用到后端的具体validate方法,我的平台就是走到Backend::ValidateDisplay

[drm-hwcomposer/backend/Backend.cpp]
HWC2::Error Backend::ValidateDisplay(HwcDisplay *display, uint32_t *num_types,
uint32_t *num_requests) {
*num_types = 0;
*num_requests = 0;
auto layers = display->GetOrderLayersByZPos(); // 按Z-order顺序排列的HwcLayer的集合
int client_start = -1; // layers中,需要Client合成的layer的起始位置
size_t client_size = 0; // layers中,需要Client合成的layer的个数
if (display->ProcessClientFlatteningState(layers.size() <= 1)) {
display->total_stats().frames_flattened_++;
client_start = 0;
client_size = layers.size();
//设置合成类型,client_start到client_start+client_size之间的设置为Client,其它的设置为Device
MarkValidated(layers, client_start, client_size);
} else {
std::tie(client_start, client_size) = GetClientLayers(display, layers);// 刷选哪些layer需要Client合成
//设置合成类型,client_start到client_start+client_size之间的设置为Client,其它的设置为Device
MarkValidated(layers, client_start, client_size);
bool testing_needed = !(client_start == 0 && client_size == layers.size());
AtomicCommitArgs a_args = {.test_only = true};
if (testing_needed &&
display->CreateComposition(a_args) != HWC2::Error::None) {
++display->total_stats().failed_kms_validate_;
client_start = 0;
client_size = layers.size();
//设置合成类型,client_start到client_start+client_size之间的设置为Client,其它的设置为Device
MarkValidated(layers, 0, client_size);
}
}
*num_types = client_size;
display->total_stats().gpu_pixops_ += CalcPixOps(layers, client_start,
client_size);
display->total_stats().total_pixops_ += CalcPixOps(layers, 0, layers.size());
return *num_types != 0 ? HWC2::Error::HasChanges : HWC2::Error::None;
}

Backend中还有几个辅助方法,简单介绍下

GetClientLayers:刷选出哪些layer需要Client合成,筛选是会经过两层考核 IsClientLayer & GetExtraClientRange

IsClientLayer:判断指定的Layer是否要Client合成,有几个条件:1. HardwareSupportsLayerType硬件不支持的合成方式
                                                2. IsHandleUsable buffer handle无法转为DRM要求的buffer object
                                                3. color_transform_hint !=HAL_COLOR_TRANSFORM_IDENTITY
                                                4. 需要scale or phase,但hwc强制GPU来处理

GetExtraClientRange: 进一步筛选client layer, 当layer的数量多于hwc支持的planes时,需要留出一个给 client target

合成显示

PresentDisplay方法的作用就是把内容呈现到屏幕上去

[drm-hwcomposer/hwc2_device/HwcDisplay.cpp]
HWC2::Error HwcDisplay::PresentDisplay(int32_t *present_fence) {
...
AtomicCommitArgs a_args{};
ret = CreateComposition(a_args);// 调用
...
}

主要是去调用了CreateComposition这个方法

[drm-hwcomposer/hwc2_device/HwcDisplay.cpp]
HWC2::Error HwcDisplay::CreateComposition(AtomicCommitArgs &a_args) {
if (IsInHeadlessMode()) { // 无头模式,不做处理,返回
ALOGE("%s: Display is in headless mode, should never reach here", __func__);
return HWC2::Error::None;
}
int PrevModeVsyncPeriodNs = static_cast<int>(
1E9 / GetPipe().connector->Get()->GetActiveMode().v_refresh());
auto mode_update_commited_ = false; // 是否需要更新/提交
if (staged_mode_ && // staged_mode_ 当前所处的显示模式
staged_mode_change_time_ <= ResourceManager::GetTimeMonotonicNs()) {
client_layer_.SetLayerDisplayFrame( // 设置显示的位置大小
(hwc_rect_t){.left = 0,
.top = 0,
.right = static_cast<int>(staged_mode_->h_display()),
.bottom = static_cast<int>(staged_mode_->v_display())});
configs_.active_config_id = staged_mode_config_id_;
a_args.display_mode = *staged_mode_;
if (!a_args.test_only) {
mode_update_commited_ = true;
}
}
// order the layers by z-order
bool use_client_layer = false; // 是否有GPU合成的图层
uint32_t client_z_order = UINT32_MAX;
std::map<uint32_t, HwcLayer *> z_map;
for (std::pair<const hwc2_layer_t, HwcLayer> &l : layers_) {
switch (l.second.GetValidatedType()) {
case HWC2::Composition::Device:
z_map.emplace(std::make_pair(l.second.GetZOrder(), &l.second)); // z_map中是按照z-order排序的,Device合成的图层
break;
case HWC2::Composition::Client:
// Place it at the z_order of the lowest client layer
use_client_layer = true;
client_z_order = std::min(client_z_order, l.second.GetZOrder()); // 找到GPU合成图层中最小的z-order
break;
default:
continue;
}
}
if (use_client_layer)
z_map.emplace(std::make_pair(client_z_order, &client_layer_)); // GPU合成的Client图层加入z_map集合
if (z_map.empty()) // 空集合,没有要合成的图层
return HWC2::Error::BadLayer;
std::vector<DrmHwcLayer> composition_layers;
// now that they're ordered by z, add them to the composition
for (std::pair<const uint32_t, HwcLayer *> &l : z_map) {
DrmHwcLayer layer;
l.second->PopulateDrmLayer(&layer); // 把HwcLayer转为DrmHwcLayer,主要是一些信息
int ret = layer.ImportBuffer(GetPipe().device); // 1. 把buffer_handle_t转为drm buffer object
// 2. 做drmPrimeFDToHandle处理
if (ret) {
ALOGE("Failed to import layer, ret=%d", ret);
return HWC2::Error::NoResources;
}
composition_layers.emplace_back(std::move(layer));
}
/* Store plan to ensure shared planes won't be stolen by other display
* in between of ValidateDisplay() and PresentDisplay() calls
*/
current_plan_ = DrmKmsPlan::CreateDrmKmsPlan(GetPipe(), // 创建一个计划:合成显示
std::move(composition_layers));
if (!current_plan_) {
if (!a_args.test_only) {
ALOGE("Failed to create DrmKmsPlan");
}
return HWC2::Error::BadConfig;
}
a_args.composition = current_plan_;
// 提交/合成/显示到屏幕 == >DrmAtomicStateManager::ExecuteAtomicCommit
int ret = GetPipe().atomic_state_manager->ExecuteAtomicCommit(a_args);
if (ret) {
if (!a_args.test_only)
ALOGE("Failed to apply the frame composition ret=%d", ret);
return HWC2::Error::BadParameter;
}
if (mode_update_commited_) {
staged_mode_.reset();
vsync_tracking_en_ = false;
if (last_vsync_ts_ != 0) {
hwc2_->SendVsyncPeriodTimingChangedEventToClient(
handle_, last_vsync_ts_ + PrevModeVsyncPeriodNs);
}
}
return HWC2::Error::None;
}

上面出现了一个新的类型

struct DrmHwcLayer {
buffer_handle_t sf_handle = nullptr;
hwc_drm_bo_t buffer_info{};
std::shared_ptr<DrmFbIdHandle> fb_id_handle;
int gralloc_buffer_usage = 0;
DrmHwcTransform transform{};
DrmHwcBlending blending = DrmHwcBlending::kNone;
uint16_t alpha = UINT16_MAX;
hwc_frect_t source_crop;
hwc_rect_t display_frame;
DrmHwcColorSpace color_space;
DrmHwcSampleRange sample_range;
UniqueFd acquire_fence;
int ImportBuffer(DrmDevice *drm_device);
bool IsProtected() const {
return (gralloc_buffer_usage & GRALLOC_USAGE_PROTECTED) ==
GRALLOC_USAGE_PROTECTED;
}
};

ImportBuffer调用的流程:

int DrmHwcLayer::ImportBuffer(DrmDevice *drm_device) {
buffer_info = hwc_drm_bo_t{};
int ret = BufferInfoGetter::GetInstance()->ConvertBoInfo(sf_handle,
&buffer_info);
if (ret != 0) {
ALOGE("Failed to convert buffer info %d", ret);
return ret;
}
fb_id_handle = drm_device->GetDrmFbImporter().GetOrCreateFbId(&buffer_info);
if (!fb_id_handle) {
ALOGE("Failed to import buffer");
return -EINVAL;
}
return 0;
}

进而调用到相关方法
BufferInfoMapperMetadata::ConvertBoInfo
DrmFbImporter::GetOrCreateFbId

DrmAtomicStateManager::CommitFrame方法中应该是最终去显示内容的逻辑。

看一下调用栈信息:

PresentDisplay调用

ValidateDisplay调用

Android 12(S) 图像显示系统 - drm_hwcomposer 简析(下)的更多相关文章

  1. Android 12(S) 图像显示系统 - drm_hwcomposer 简析(上)

    必读: Android 12(S) 图像显示系统 - 开篇 前言 Android源码中有包含drm_hwcomposer:/external/drm_hwcomposer/ drm_hwcompose ...

  2. Android 12(S) 图像显示系统 - GraphicBuffer同步机制 - Fence

    必读: Android 12(S) 图像显示系统 - 开篇 一.前言 前面的文章中讲解Android BufferQueue的机制时,有遇到过Fence,但没有具体讲解.这篇文章,就针对Fence这种 ...

  3. Android 12(S) 图像显示系统 - 基础知识之 BitTube

    必读: Android 12(S) 图像显示系统 - 开篇 一.基本概念 在Android显示子系统中,我们会看到有使用BitTube来进行跨进程数据传递.BitTube的实现很简洁,就是一对&quo ...

  4. Android 12(S) 图像显示系统 - SurfaceFlinger之VSync-上篇(十六)

    必读: Android 12(S) 图像显示系统 - 开篇 一.前言 为了提高Android系统的UI交互速度和操作的流畅度,在Android 4.1中,引入了Project Butter,即&quo ...

  5. Android 12(S) 图像显示系统 - SurfaceFlinger 之 VSync - 中篇(十七)

    必读: Android 12(S) 图像显示系统 - 开篇 1 前言 这一篇文章,将继续讲解有关VSync的知识,前一篇文章 Android 12(S) 图像显示系统 - SurfaceFlinger ...

  6. Android 12(S) 图像显示系统 - SurfaceFlinger GPU合成/CLIENT合成方式 - 随笔1

    必读: Android 12(S) 图像显示系统 - 开篇 一.前言 SurfaceFlinger中的图层选择GPU合成(CLIENT合成方式)时,会把待合成的图层Layers通过renderengi ...

  7. Android 12(S) 图像显示系统 - HWC HAL 初始化与调用流程

    必读: Android 12(S) 图像显示系统 - 开篇 接口定义 源码位置:/hardware/interfaces/graphics/composer/ 在源码目录下可以看到4个版本的HIDL ...

  8. Android 11(R) Power HAL AIDL简析 -- 基本接口

    Android 11(R) Power HAL AIDL将分三篇文章来介绍: Android 11(R) Power HAL AIDL简析 -- 基本接口 Android 11(R) Power HA ...

  9. Android 12(S) 图形显示系统 - createSurface的流程(五)

    题外话 刚刚开始着笔写作这篇文章时,正好看电视在采访一位92岁的考古学家,在他的日记中有这样一句话,写在这里与君共勉"不要等待幸运的降临,要去努力的掌握知识".如此朴实的一句话,此 ...

随机推荐

  1. switch 和 if else if else 有什么区别

    1.  一般情况下,它们两个语句可以相互替换 2.  switch..case语句通常处理case为比较确定值的情况,而if...else...语句更加灵活,常用于范围判断(大于.等于某个范围) 3. ...

  2. Glade To Code 介绍

    Glade To Code 简介 根据 Glade 文件生成指定语言的 GTK 代码的工具 使用说明 python3 glade-to-code.py -l [语言类型] -i [输入 Glade 文 ...

  3. redis中的字典结构是怎样的?

    点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 文章不定期同步公众号,还有各种一线大厂面试原题.我的学习系列笔记. 基础概念 redis支持的5种数据类型中,有hash类型,hash类型的 ...

  4. Gitlab-runner+Docker自动部署SpringBoot项目

    本文基于Gitlab CI/CD及Docker快速实现项目的自动部署. 注意:本文较长,浏览需要12分钟左右. 1.环境要求 以下服务器的操作系统均为Centos7 服务器A:Gitlab 服务器B: ...

  5. Django模板相关

    1.母版 想象一个举着火炬的手,除了火炬这个手还能举棒球棍.举雷神之锤.举拖拉机钥匙等等,举得东西不同给人整体感觉就不同. 母版就相当于这个手(实际为一个html文件),其他相关的html文件就相当于 ...

  6. 分布式存储之GlusterFS

    公众号关注 「开源Linux」 回复「学习」,有我为您特别筛选的学习资料~ 1.glusterfs概述 GlusterFS系统是一个可扩展的网络文件系统,相比其他分布式文件系统,GlusterFS具有 ...

  7. vue3 vite 系统标题 系统名称统一配置

    想要统一配置系统名称 或者其他的,需要在vue3中使用 vite 的环境变量 vite 的环境变量 需要创建两个文件(和 vite.config.js 文件同一目录) .env.development ...

  8. .NET混合开发解决方案11 WebView2加载的网页中JS调用C#方法

    系列目录     [已更新最新开发文章,点击查看详细] WebView2控件应用详解系列博客 .NET桌面程序集成Web网页开发的十种解决方案 .NET混合开发解决方案1 WebView2简介 .NE ...

  9. java高级用法之:JNA中的Structure

    目录 简介 native中的struct Structure 特殊类型的Structure 结构体数组作为参数 结构体数组作为返回值 结构体中的结构体 结构体中的数组 结构体中的可变字段 结构体中的只 ...

  10. 由C# dynamic是否装箱引发的思考

    前言 前几天在技术群里看到有同学在讨论关于dynamic是否会存在装箱拆箱的问题,我当时第一想法是"会".至于为啥会有很多人有这种疑问,主要是因为觉得dynamic可能是因为有点特 ...