SurfaceFlinger的硬件Vsync深入分析-千里马android framework车机手机系统开发

本文主要是介绍SurfaceFlinger的硬件Vsync深入分析-千里马android framework车机手机系统开发,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

背景:

学过或者你看过surfaceflinger相关文章同学都知道,vsync其实都是由surfaceflinger软件层面进行模拟的,但是软件模拟有可能会有误差或偏差,这个时候就需要有个硬件vsync帮忙校准。
故才会在surfaceflinger的systrace出现如下校准波形图,这个可以看到硬件vsync开启后才有hw的vsync的脉冲产生,这个刚好可以看到成对的一上一下脉冲刚好6个,也就是经常看到的6个周期的,经过这个6个硬件vsync的校准后,软件vsync就可以调整正常。
在这里插入图片描述但是。。。一切一切都是好像是这么一回事,具体怎么就产生这些硬件vsync的波形的呢?硬件vsync为啥说他来自硬件呢?怎么就来自硬件呢?好像一切都好虚是不是,那不是肯定的吗?那么今天就来彻底解密一下这个硬件vsync的执行哈,注意下面很多kernel驱动相关代码。。。。。具体相关代码付费课学员直接就会配套有哈。
更多framework实战课可以加我V:androidframework007

在这里插入图片描述

surfaceflinger端硬件Vsync的调用trace:

这个相对很好看到如下图所示:
在这里插入图片描述明显看到这个东西实际是hal进程通过跨进程回调到了surfaceflinger进程的,那么就来看看hal部分

hal端的回调硬件Vsync堆栈

首先来看看我们的trace图形,这个直接surfaceflinger箭头点击一跳就可以了
在这里插入图片描述这个时候就到了graphic的hal进程相关trace图形如下
在这里插入图片描述

可以看到这个确实是hal端的SDM_EventThread线程进行调用的,这里按着代码一直追的化追到如下地方了:

11-04 23:21:53.200   977  1065 D Vsync   : #00 pc 000000000003b65c  /vendor/lib64/hw/hwcomposer.msm8998.so (sdm::HWCCallbacks::Vsync(unsigned long, long)+76)
11-04 23:21:53.200   977  1065 D Vsync   : #01 pc 000000000002f9c8  /vendor/lib64/hw/hwcomposer.msm8998.so (sdm::HWCDisplay::VSync(sdm::DisplayEventVSync const&)+28)
11-04 23:21:53.200   977  1065 D Vsync   : #02 pc 000000000002c818  /vendor/lib64/libsdmcore.so (non-virtual thunk to sdm::DisplayPrimary::VSync(long)+68)
11-04 23:21:53.200   977  1065 D Vsync   : #03 pc 0000000000048dc8  /vendor/lib64/libsdmcore.so (sdm::HWEvents::DisplayEventHandler()+288)
11-04 23:21:53.200   977  1065 D Vsync   : #04 pc 0000000000048b60  /vendor/lib64/libsdmcore.so (sdm::HWEvents::DisplayEventThread(void*)+16)
11-04 23:21:53.200   977  1065 D Vsync   : #05 pc 00000000000b63b0  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208)
11-04 23:21:53.200   977  1065 D Vsync   : #06 pc 00000000000530b8  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)

这里就来从最根部的地方开始看到底hal的这个事件是谁触发的?是hal直接和硬件通讯?哈哈,想想也不可能是吧,因为hal进程也是个应用空间的程序而已,无法直接操作硬件。直接上代码揭晓答案:


//初始化相关的poll的fd
pollfd HWEvents::InitializePollFd(HWEventData *event_data) {char node_path[kMaxStringLength] = {0};char data[kMaxStringLength] = {0};pollfd poll_fd = {0};poll_fd.fd = -1;if (event_data->event_type == HWEvent::EXIT) {// Create an eventfd to be used to unblock the poll system call when// a thread is exiting.poll_fd.fd = Sys::eventfd_(0, 0);poll_fd.events |= POLLIN;exit_fd_ = poll_fd.fd;} else {snprintf(node_path, sizeof(node_path), "%s%d/%s", fb_path_, fb_num_,map_event_to_node_[event_data->event_type]);poll_fd.fd = Sys::open_(node_path, O_RDONLY);poll_fd.events |= POLLPRI | POLLERR;}if (poll_fd.fd < 0) {DLOGW("open failed for display=%d event=%s, error=%s", fb_num_,map_event_to_node_[event_data->event_type], strerror(errno));return poll_fd;}// Read once on all fds to clear data on all fds.Sys::pread_(poll_fd.fd, data , kMaxStringLength, 0);return poll_fd;
}
//设置相关的event_type解析方法
DisplayError HWEvents::SetEventParser(HWEvent event_type, HWEventData *event_data) {DisplayError error = kErrorNone;switch (event_type) {case HWEvent::VSYNC:event_data->event_parser = &HWEvents::HandleVSync;break;case HWEvent::IDLE_NOTIFY:event_data->event_parser = &HWEvents::HandleIdleTimeout;break;case HWEvent::EXIT:event_data->event_parser = &HWEvents::HandleThreadExit;break;case HWEvent::SHOW_BLANK_EVENT:event_data->event_parser = &HWEvents::HandleBlank;break;case HWEvent::THERMAL_LEVEL:event_data->event_parser = &HWEvents::HandleThermal;break;case HWEvent::IDLE_POWER_COLLAPSE:event_data->event_parser = &HWEvents::HandleIdlePowerCollapse;break;default:error = kErrorParameters;break;}return error;
}void HWEvents::PopulateHWEventData() {for (uint32_t i = 0; i < event_list_.size(); i++) {HWEventData event_data;event_data.event_type = event_list_[i];SetEventParser(event_list_[i], &event_data);poll_fds_[i] = InitializePollFd(&event_data);event_data_list_.push_back(event_data);}
}DisplayError HWEvents::Init(int fb_num, HWEventHandler *event_handler,const vector<HWEvent> &event_list) {
//创建线程执行循环pollif (pthread_create(&event_thread_, NULL, &DisplayEventThread, this) < 0) {DLOGE("Failed to start %s, error = %s", event_thread_name_.c_str());return kErrorResources;}return kErrorNone;
}void* HWEvents::DisplayEventThread(void *context) {if (context) {return reinterpret_cast<HWEvents *>(context)->DisplayEventHandler();}return NULL;
}
//真的线程执行体
void* HWEvents::DisplayEventHandler() {char data[kMaxStringLength] = {0};prctl(PR_SET_NAME, event_thread_name_.c_str(), 0, 0, 0);setpriority(PRIO_PROCESS, 0, kThreadPriorityUrgent);//一直循环poll中的数据while (!exit_threads_) {int error = Sys::poll_(poll_fds_.data(), UINT32(event_list_.size()), -1);if (error <= 0) {DLOGW("poll failed. error = %s", strerror(errno));continue;}//poll跳出阻塞说明有数据,识别数据执行相关调用的操作for (uint32_t event = 0; event < event_list_.size(); event++) {pollfd &poll_fd = poll_fds_[event];if (event_list_.at(event) == HWEvent::EXIT) {if ((poll_fd.revents & POLLIN) && (Sys::read_(poll_fd.fd, data, kMaxStringLength) > 0)) {(this->*(event_data_list_[event]).event_parser)(data);}} else {if ((poll_fd.revents & POLLPRI) &&(Sys::pread_(poll_fd.fd, data, kMaxStringLength, 0) > 0)) {(this->*(event_data_list_[event]).event_parser)(data);}}}}pthread_exit(0);return NULL;
}
//会回调到这个方法
void HWEvents::HandleVSync(char *data) {int64_t timestamp = 0;if (!strncmp(data, "VSYNC=", strlen("VSYNC="))) {timestamp = strtoll(data + strlen("VSYNC="), NULL, 0);}event_handler_->VSync(timestamp);
}}  // namespace sdm

上面代码有注释,大家是不是看到熟悉的poll,是不是学了马哥跨进程专题后,这个都不是事分分钟可以看的懂这个逻辑,核心的就是观察相关的vsync的fd,有数据变化了,读取,属于vsync了就触发相关的,vsync回调,这个就是hal的vsync回调

总结其实hal的vsync回调也是监听的fd而已,没啥特殊,其实你说surfaceflinger是不是也可以监听fd直接拿不就行了么。。。哈哈哈确实可以,不过毕竟各个硬件厂商实现不一样,你不能保证其他家也这样实现,所以hal就是这个另一个作用就是解耦system 的aosp部分和vendor厂商的变化部分。

但是问题又来了,请问是谁触发了这个fd有数据的啊?

kernel进行fd的数据通知:

上面hal监听的fd来自哪里?其实大家猜想肯定应该是内核,因为毕竟是硬件vsync,所以可以触碰硬件东西当然是我们的内核驱动。这里最后找到如下代码:
drivers/video/fbdev/msm/mdss_mdp_overlay.c

//中断中调用的
/* function is called in irq context should have minimum processing */
static void mdss_mdp_overlay_handle_vsync(struct mdss_mdp_ctl *ctl,ktime_t t)
{dump_stack();ATRACE_BEGIN("mdss_mdp_overlay_handle_vsync");struct msm_fb_data_type *mfd = NULL;struct mdss_overlay_private *mdp5_data = NULL;if (!ctl) {pr_err("ctl is NULL\n");return;}mfd = ctl->mfd;if (!mfd || !mfd->mdp.private1) {pr_warn("Invalid handle for vsync\n");return;}mdp5_data = mfd_to_mdp5_data(mfd);if (!mdp5_data) {pr_err("mdp5_data is NULL\n");return;}pr_debug("vsync on fb%d play_cnt=%d\n", mfd->index, ctl->play_cnt);mdp5_data->vsync_time = t;sysfs_notify_dirent(mdp5_data->vsync_event_sd);//进行的fd数据通知ATRACE_END("mdss_mdp_overlay_handle_vsync");
}

驱动是vsync是靠相关的硬件中断触发的,具体的call stack如下:

11-05 00:24:24.470     0     0 I Call trace:  
11-05 00:24:24.470     0     0 I         : [<ffffff91f4c8a874>] dump_backtrace+0x0/0x3a8
11-05 00:24:24.470     0     0 I         : [<ffffff91f4c8a86c>] show_stack+0x14/0x1c
11-05 00:24:24.470     0     0 I         : [<ffffff91f50280d0>] dump_stack+0xe4/0x11c
11-05 00:24:24.470     0     0 I         : [<ffffff91f51037b4>] mdss_mdp_overlay_handle_vsync+0x20/0x1dc
11-05 00:24:24.470     0     0 I         : [<ffffff91f50f26d0>] mdss_mdp_cmd_readptr_done+0x154/0x33c
11-05 00:24:24.470     0     0 I         : [<ffffff91f50b9534>] mdss_mdp_isr+0x140/0x3bc
11-05 00:24:24.470     0     0 I         : [<ffffff91f5164d94>] mdss_irq_dispatch+0x50/0x68
11-05 00:24:24.470     0     0 I         : [<ffffff91f50c15cc>] mdss_irq_handler+0x84/0x1c0
11-05 00:24:24.470     0     0 I         : [<ffffff91f4d1a92c>] handle_irq_event_percpu+0x78/0x28c
11-05 00:24:24.470     0     0 I         : [<ffffff91f4d1abc8>] handle_irq_event+0x44/0x74
11-05 00:24:24.470     0     0 I         : [<ffffff91f4d1e714>] handle_fasteoi_irq+0xd8/0x1b0
11-05 00:24:24.470     0     0 I         : [<ffffff91f4d1a118>] __handle_domain_irq+0x7c/0xbc
11-05 00:24:24.470     0     0 I         : [<ffffff91f4c811d0>] gic_handle_irq+0x80/0x144

这里通过的给内核kernel打上trace结合看如下:
在这里插入图片描述
是不是结合trace看起来很方便,就可以清晰知道了整个调用流程:
硬件触发 kernel中断方法,写入对于的fd
—》hal进程监听fd,然后解析回调vsync进行跨进程通讯
----》surfaceflinger收到hal跨进程调用,改变自己相关的参数,调整自己软件vsync

这篇关于SurfaceFlinger的硬件Vsync深入分析-千里马android framework车机手机系统开发的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/354372

相关文章

通信系统网络架构_2.广域网网络架构

1.概述          通俗来讲,广域网是将分布于相比局域网络更广区域的计算机设备联接起来的网络。广域网由通信子网于资源子网组成。通信子网可以利用公用分组交换网、卫星通信网和无线分组交换网构建,将分布在不同地区的局域网或计算机系统互连起来,实现资源子网的共享。 2.网络组成          广域网属于多级网络,通常由骨干网、分布网、接入网组成。在网络规模较小时,可仅由骨干网和接入网组成

硬件基础知识——自学习梳理

计算机存储分为闪存和永久性存储。 硬盘(永久存储)主要分为机械磁盘和固态硬盘。 机械磁盘主要靠磁颗粒的正负极方向来存储0或1,且机械磁盘没有使用寿命。 固态硬盘就有使用寿命了,大概支持30w次的读写操作。 闪存使用的是电容进行存储,断电数据就没了。 器件之间传输bit数据在总线上是一个一个传输的,因为通过电压传输(电流不稳定),但是电压属于电势能,所以可以叠加互相干扰,这也就是硬盘,U盘

Eclipse+ADT与Android Studio开发的区别

下文的EA指Eclipse+ADT,AS就是指Android Studio。 就编写界面布局来说AS可以边开发边预览(所见即所得,以及多个屏幕预览),这个优势比较大。AS运行时占的内存比EA的要小。AS创建项目时要创建gradle项目框架,so,创建项目时AS比较慢。android studio基于gradle构建项目,你无法同时集中管理和维护多个项目的源码,而eclipse ADT可以同时打开

android 免费短信验证功能

没有太复杂的使用的话,功能实现比较简单粗暴。 在www.mob.com网站中可以申请使用免费短信验证功能。 步骤: 1.注册登录。 2.选择“短信验证码SDK” 3.下载对应的sdk包,我这是选studio的。 4.从头像那进入后台并创建短信验证应用,获取到key跟secret 5.根据技术文档操作(initSDK方法写在setContentView上面) 6.关键:在有用到的Mo

android一键分享功能部分实现

为什么叫做部分实现呢,其实是我只实现一部分的分享。如新浪微博,那还有没去实现的是微信分享。还有一部分奇怪的问题:我QQ分享跟QQ空间的分享功能,我都没配置key那些都是原本集成就有的key也可以实现分享,谁清楚的麻烦详解下。 实现分享功能我们可以去www.mob.com这个网站集成。免费的,而且还有短信验证功能。等这分享研究完后就研究下短信验证功能。 开始实现步骤(新浪分享,以下是本人自己实现

Android我的二维码扫描功能发展史(完整)

最近在研究下二维码扫描功能,跟据从网上查阅的资料到自己勉强已实现扫描功能来一一介绍我的二维码扫描功能实现的发展历程: 首页通过网络搜索发现做android二维码扫描功能看去都是基于google的ZXing项目开发。 2、搜索怎么使用ZXing实现自己的二维码扫描:从网上下载ZXing-2.2.zip以及core-2.2-source.jar文件,分别解压两个文件。然后把.jar解压出来的整个c

android 带与不带logo的二维码生成

该代码基于ZXing项目,这个网上能下载得到。 定义的控件以及属性: public static final int SCAN_CODE = 1;private ImageView iv;private EditText et;private Button qr_btn,add_logo;private Bitmap logo,bitmap,bmp; //logo图标private st

Android多线程下载见解

通过for循环开启N个线程,这是多线程,但每次循环都new一个线程肯定很耗内存的。那可以改用线程池来。 就以我个人对多线程下载的理解是开启一个线程后: 1.通过HttpUrlConnection对象获取要下载文件的总长度 2.通过RandomAccessFile流对象在本地创建一个跟远程文件长度一样大小的空文件。 3.通过文件总长度/线程个数=得到每个线程大概要下载的量(线程块大小)。

Python应用开发——30天学习Streamlit Python包进行APP的构建(9)

st.area_chart 显示区域图。 这是围绕 st.altair_chart 的语法糖。主要区别在于该命令使用数据自身的列和指数来计算图表的 Altair 规格。因此,在许多 "只需绘制此图 "的情况下,该命令更易于使用,但可定制性较差。 如果 st.area_chart 无法正确猜测数据规格,请尝试使用 st.altair_chart 指定所需的图表。 Function signa

Linux系统稳定性的奥秘:探究其背后的机制与哲学

在计算机操作系统的世界里,Linux以其卓越的稳定性和可靠性著称,成为服务器、嵌入式系统乃至个人电脑用户的首选。那么,是什么造就了Linux如此之高的稳定性呢?本文将深入解析Linux系统稳定性的几个关键因素,揭示其背后的技术哲学与实践。 1. 开源协作的力量Linux是一个开源项目,意味着任何人都可以查看、修改和贡献其源代码。这种开放性吸引了全球成千上万的开发者参与到内核的维护与优化中,形成了