EventThread线程对VSync的接收

本文详细介绍了Android系统中VSync机制的工作原理。包括EventThread如何接收来自硬件的VSync信号并分发给注册者,以及在硬件不支持情况下如何通过VSyncThread模拟VSync信号。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

From: http://blog.chinaunix.net/uid-13164110-id-3481832.html

EventThread被设计用来接收VSync事件通知,并分发VSync通知给系统中的每一个感兴趣的注册者。VSync来源自底层硬件驱动程序的上报,对于Android能看到的接口来说,它是来自HAL层的hwc_composer_device的抽象硬件设备。若硬件不支持,则HWComposer会创建一个线程去模拟产生VSync事件通知。一旦工作起来,VSync将像一个脉搏一样,不断通知到Surfaceflinger中的EventThread线程,由后者再分发给感兴趣的注册者。

先来看一下EventThread何时被创建并开始运行的。

SurfaceFlinger类被创建时,它的onFirstRef函数将被调用(因为最终继承自RefBase),代码如下:


因为SurfaceFlinger继承自线程类Thread(上面的行130)调用了run函数,意味着创建一个新线程的执行。它的过程是:Thread::run将创建一个新线程,名称为SurfaceFlinger(后面我们将称之为SurfaceFlinger线程),线程的入口函数Thread::_threadLoop又将调用子类重载的threadLoop函数,不过在调用重载的threadLoop之前,先调用子类重载的readyToRun(只是初次时)。也就是说,此处的run函数调用将启动一个新线程SurfaceFlinger,然后SurfaceFlingerreadyToRun函数在其里面被调用,从而做一些初始化工作,如前面提到的从ashm分配一块共享控制块surface_flinger_cblk_t,见前面的11.5.2小节。

SurfaceFlingerreadyToRun函数的后面,将会创建EventThread线程(下面的行300):


在其EventThreadonFirstRef中被要求开始执行,见行52(参见文件EventThread.cpp):


这样,EventThead在创建后,马上开始了它的线程循环。

再来看一下HAL层是如何向EventThread发送VSync通知的。

HAL层的文件hardware/libhardware/include/hwcomposer.h中定义了一个供底层向上调用的回调例程hwc_procs结构体,它包含两个例程函数:invalidatevsync。代码如下(见文件hwcomposer.h):

AndroidHWComposer.h中定义了两个结构体:回调callbacks和回调上下文cb_context。其中callbacks继承自上面的hwc_procs,代码如下:


HWComposer的构造函数中,先从库中解析符号,然后打开HWComposer硬件模块(下面的行00063),再根据其open函数打开HAL抽象硬件设备mHwc(下面的行00066),然后再判断硬件设备mHwc是否支持registerProcs函数(下面的行00070)。若硬件平台厂商实现了该函数,并在open函数中将其赋值给了HAL抽象硬件设备hwc_composer_deviceregisterProcs成员,则该成员不为空,表示支持上述的回调例程hwc_procsHWComposer的构造函数将依据此来判断是否支持VSync,代码如下(参见文件HWComposer.cpp):


在硬件平台支持VSync的情况下,通过上面的行0007200073的赋值和行00074的注册回调例程,来自底层HAL的调用,将转到调用钩子函数hook_xxx。其中回调例程hwc_procs中的VSync将指向hook_vsync钩子函数,后者代码如下(参见文件HWComposer.cpp):


它又调用到下面的VSync成员函数(参见文件HWComposer.cpp):


上面行00125mEventHandler实际为DisplayHardware对象。在HWComposer中,定义了一个抽象接口EventHandler,并维护了一个EventHandler类型的对象引用;而DisplayHardware实现了该接口,并在DisplayHardwareinit函数初始化时,创建了HWComposer对象,将其指定给了HWComposer维护的EventHandler对象引用(即mEventHandler,见上面HWComposer构造函数的行00048)。因此,对VSync的调用将调用到DisplayHardwareonVSyncReceived函数,后者的代码如下(参见文件DisplayHardware.cpp):


这将继续调用到DisplayHardware中维护的一个handler,而这个handler实际是EventThread的对象(见EventThread::onFirstRef函数,在该函数中指定)。因此,对VSync的调用实际调用到下面的EventThreadonVSyncReceived成员函数(见EventThread.cpp:


这将唤醒EventThread线程的循环函数threadLoopCondition上的睡眠等待。

EventThread的线程循环函数threadLoop主要用于向系统中对VSync感兴趣的注册者分发VSync事件通知,下节将对其做详细介绍。

对于不支持VSync上报通知的硬件平台来说,则HWComposer会创建一个VSyncThread线程,用于模拟VSync的发送。在HWComposer构造函数的后面,判断出需要模拟一个VSync线程后即创建该线程(下面的行92),代码如下:


VSyncThread的线程循环函数threadLoop后面的部分代码如下(参见文件HWComposer.cpp):


上面的行318睡眠片刻,醒来后即模拟发送VSync事件通知(行322)。睡眠的时间最终由HWComposer中的刷新率变量mRefreshPeriod确定,该值来自framebuffer device或系统设置信息,mRefreshPeriodHWComposer的构造函数中被赋值,见前面其构造函数的代码行55中。如图11-21所示,VSync起源于①,经过一步步调用,执行到②,唤醒可能正在睡眠的线程,然后由线程循环函数去分发它。图中①到②是本小节讲述的,②后面的将在下一小节讲述。


11?21 EventThread接收和分发VSync示意图



本文节选自《深入剖析Android系统》一书

杨长刚著

电子工业出版社出版


<think>我们正在讨论的是理想泳道图和现实泳道图中的Vsync与进程中的Vsync的区别和联系。根据引用,我们知道Vsync在图形系统中是一个关键的同步信号,它控制着每一帧绘制的时机。在理想情况下,图形渲染应该严格遵循Vsync信号,但在现实中,由于各种因素,可能会出现偏差。首先,我们需要明确几个概念:-**理想泳道图**:描述的是理论上的图形渲染流程,每个Vsync周期内,所有的渲染操作(包括应用提交渲染命令、图形系统处理、帧缓冲区更新等)都能按时完成,并在下一个Vsync信号到来时准时显示。这样,每一帧的渲染周期都是连续的,没有丢帧。-**现实泳道图**:实际运行时的图形渲染流程,由于应用处理延迟、系统负载、资源竞争等原因,可能会导致某些渲染操作不能在当前Vsync周期内完成,从而造成丢帧(即某个Vsync周期没有完成渲染,需要跳过,等待下一个Vsync周期)。这会在泳道图中表现为连续的Vsync周期出现中断(如引用[3]中提到的VSyncId不连贯)。-**进程中的Vsync**:在操作系统中,Vsync信号是由硬件(显示器)或系统底层(如显示控制器)生成的,并通过系统机制(如EventThread)传递给需要同步的进程(如应用程序的UI线程、合成进程等)。进程中的Vsync指的是进程接收到的这个同步信号,以及进程内部对该信号的处理逻辑(如触发渲染操作)。现在,我们来分析它们的区别和联系:###1.**相同点(联系)**-**同步机制的核心**:无论是理想还是现实泳道图,亦或是进程中的Vsync,它们都基于同一个物理信号——显示器的垂直同步信号。这个信号以固定周期(如60Hz时为16.6ms)触发,用于协调整个图形流水线[^1][^2]。-**触发渲染流程**:在理想泳道图和现实泳道图中,Vsync信号都是每一帧渲染流程的起点。当进程(如应用程序)接收Vsync信号后,会开始准备下一帧的绘制(如计算布局、生成绘制命令等)[^2]。-**目标一致**:它们都旨在避免图像撕裂、减少抖动,并确保帧率稳定。在理想情况下,现实泳道图应该与理想泳道图一致,即每个Vsync周期都完成一帧的渲染和显示。###2.**不同点(区别)**-**时序精确性**:-理想泳道图中,所有操作(应用处理、渲染、合成)都在一个Vsync周期内完成,严格对齐每个Vsync信号。例如,在90Hz下,每11.1ms完成一帧[^1]。-现实泳道图中,由于进程处理延迟(如应用主线程阻塞、渲染命令执行超时等),可能导致某个Vsync周期内无法完成渲染,从而出现丢帧(如引用[3]中VSyncId从8090直接跳到8093,中间丢失了8091和8092)。此时,进程中的Vsync信号虽然按时到达,但进程未能及时响应。-**进程内部处理差异**:-在理想情况下,进程(如应用程序)会在收到Vsync信号后立即开始渲染,并在周期结束前完成。-现实中,进程可能因为自身逻辑复杂、资源竞争等原因,无法在Vsync周期内完成工作。例如,应用线程在收到`H:ReceiveVsync`事件后,由于处理超时,导致下一个Vsync信号到来时还未完成,从而跳过一帧[^3]。-**系统级影响**:-理想泳道图假设系统资源充足,无其他干扰。-现实泳道图中,系统负载(如多个进程竞争CPU/GPU)、进程调度延迟等都会影响进程对Vsync信号的响应。例如,合成进程(如SurfaceFlinger)可能因高负载而延迟处理Vsync信号,导致现实泳道图中出现合成延迟[^2]。###3.**数学描述**设显示器刷新周期为$T_{\text{vsync}}$(如60Hz时$T_{\text{vsync}}=16.6\text{ms}$),理想泳道图中,每一帧的渲染时间$t_{\text{render}}$满足:$$t_{\text{render}}\leqT_{\text{vsync}}$$而在现实泳道图中,可能存在某些帧的渲染时间$t_{\text{render}}'$超过周期:$$t_{\text{render}}'>T_{\text{vsync}}$$此时,该帧会延迟到下一个或多个Vsync周期后才能显示,造成丢帧。丢帧数$N_{\text{skip}}$可通过泳道图中缺失的VSyncId数量计算(如引用[3]中VSyncId8090到8093之间缺失2帧)[^3]。###4.**性能分析中的体现**在性能优化工具(如SmartPerf-Host)生成的泳道图中:-**理想泳道图**:作为参考基准,通常不会实际记录,而是用于对比。-**现实泳道图**:通过Trace点(如`H:ReceiveVsync`)记录实际Vsync事件和渲染操作的时间点。通过分析这些Trace点之间的间隔,可以定位丢帧位置(如引用[3]和[4])[^3][^4]。###总结-**联系**:理想泳道图、现实泳道图和进程中的Vsync共享同一套同步机制,都依赖Vsync信号驱动渲染流程。-**区别**:理想泳道图描述理论最优情况,现实泳道图反映实际运行状态(可能因进程延迟而丢帧),而进程中的Vsync则是实际触发进程内部渲染逻辑的信号源。若需进一步分析现实泳道图中的丢帧原因(如应用线程阻塞),可结合Trace工具深入追踪[^4]。§§相关问题§§1.如何通过泳道图中的Trace点定位Vsync丢帧的具体原因?2.在HarmonyOS/OpenHarmony中,哪些进程会接收Vsync信号?3.如何优化应用以减少Vsync周期内的渲染延迟?4.现实泳道图中连续丢帧的阈值如何确定?</think>### 理想泳道图 vs. 现实泳道图中的 Vsync 对比分析 #### **1. 核心联系:相同的 Vsync 信号源** - **统一物理信号** 理想和现实泳道图中的 Vsync 均源于同一物理信号:显示器以固定频率(如 60Hz: 16.6ms/周期)发出的垂直同步脉冲[^2]。 - 数学表达:设刷新率为 $f$,则周期 $T_{\text{vsync}} = \frac{1}{f}$ 例如:120Hz → $T_{\text{vsync}} = 8.3 \text{ms}$[^1] - **进程触发机制一致** 两类泳道图中,进程(如 UI 渲染线程、合成进程)均需响应 Vsync 信号启动帧处理: ```mermaid graph LR Vsync信号 --> 应用提交渲染命令 --> 图形系统处理 --> 帧缓冲区写入 ``` #### **2. 关键区别:时序与行为的偏离** | **维度** | **理想泳道图** | **现实泳道图** | **关联进程表现** | |------------------|----------------------------------------|----------------------------------------|-------------------------------------| | **时序精确性** | 严格对齐周期边界 | 周期抖动(Jitter)或偏移 | 进程响应延迟导致现实周期 > $T_{\text{vsync}}$[^3] | | **帧连续性** | 每周期完成 1 帧(VSyncId 连续) | 可能丢帧(VSyncId 跳变,如 8090→8093)[^3] | 进程超时未完成渲染 | | **资源占用** | 无阻塞,渲染命令及时执行 | UI后端引擎队列堆积 | 进程主线程阻塞[^2] | | **可视化表现** | 平滑泳道无断层 | Trace 泳道出现断裂(丢帧标记) | 通过 `H:ReceiveVsync` 定位问题点[^4] | #### **3. 数学描述现实偏差** 现实泳道图中,**实际渲染周期** $T_{\text{actual}}$ 可能偏离理论值: $$ T_{\text{actual}} = T_{\text{vsync}} + \Delta t_{\text{delay}} $$ 其中 $\Delta t_{\text{delay}}$ 包含: - 进程计算延迟(如复杂 UI 布局) - 资源竞争延迟(如 GPU 排队) - 系统调度延迟(如高优先级中断) #### **4. 进程视角的深层差异** - **理想进程行为** 进程在 $T_{\text{vsync}}$ 内完成: `接收Vsync → 生成帧 → 提交缓冲区 → 等待下一Vsync`[^2] - **现实进程问题** - **丢帧检测**:通过 Trace 泳道中缺失的 `VSyncId` 定位进程瓶颈(如应用线程泳道断点)[^3][^4] - **性能优化**:分析 `SmartPerf-Host` 泳道图,识别进程内耗时操作(如渲染命令执行 > $T_{\text{vsync}}$)[^4] #### **5. 总结:本质与表现的辩证关系** | **层面** | **共同点** | **差异根源** | |---------------|---------------------------------------|---------------------------------------| | **信号本质** | 同一物理 Vsync 信号源 | 进程响应能力不同 | | **同步目标** | 实现帧率稳定与无撕裂 | 现实受进程负载/系统干扰影响 | | **优化方向** | 依赖泳道图诊断进程性能[^4] | 需针对性优化进程内延迟(如异步渲染) | > 泳道图本质是 **Vsync 信号在进程执行层面的投影**:理想泳道图描述理论约束,现实泳道图暴露进程在同步机制中的实际行为偏差[^3][^4]。 --- ### **相关问题** 1. 如何通过 Trace 泳道图量化进程对 Vsync 信号的响应延迟? 2. HarmonyOS 的异步渲染机制如何减少现实泳道图中的 Vsync 丢帧? 3. 高刷新率屏幕(120Hz)下,进程优化 Vsync 同步有哪些特殊挑战? 4. 泳道图中的 `VSyncId` 跳变是否一定表示进程性能问题?如何区分系统级与进程级原因?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值