android屏幕刷新之五
前言
接续 class NativeDisplayEventReceiver : public DisplayEventDispatcher,DisplayEventDispatcher作为显示事件的调度器,负责将AP侧的Vsync请求向下传递,以及将native层的Vsync事件向上分发。
DisplayEventDispatcher作为native层的操作对象,其路径为 framework/native/libs/gui/DisplayEventDispatcher.cpp。其作为NativeDisplayEventReceiver的父类对象,定义了诸多的虚函数,在native层调用时可传递信息至NativeDisplayEventReceiver的方法实现,借此完成native层的Vsync事件向上(java层)分发。
1 DisplayEventDispatcher.h
DisplayEventDispatcher的头文件定义了类的成员变量,函数类型,结构体等。路径为native/include/gui/DisplayEventDispatcher.h。定义了 android::DisplayEventDispatcher 类,它是 Java 层 DisplayEventReceiver 与 native SurfaceFlinger 分发 VSync、Hotplug、ModeChanged 等事件的桥梁。
主要封装了:
- DisplayEventReceiver:用于从 SurfaceFlinger 收取 display 相关事件;
- LooperCallback:用于将事件通过 epoll 机制分发回主线程;
- 抽象的事件回调接口 dispatch* 系列函数;
- VSync 监听与调度逻辑。
1.1 成员变量说明
参数 说明 sp mLooper native Looper,对应 Java 中 MessageQueue DisplayEventReceiver mReceiver 核心对象,内部包含 Binder IPC 连接与 fd bool mWaitingForVsync 标记当前是否处于等待 vsync 的状态 uint32_t mLastVsyncCount 上一次收到的 VSync 编号 nsecs_t mLastScheduleVsyncTime 上一次请求 VSync 的时间 std::vector mFrameRateOverrides 当前帧率覆盖信息 1.2 功能概述
功能点 说明 LooperCallback 被挂接到 Looper 的 epoll fd 回调对象 DisplayEventReceiver 实际与 SurfaceFlinger binder 通信的类,负责接收 display 事件 initialize 完成 binder 连接并注册 fd 到 looper scheduleVsync 请求下次 VSync handleEvent epoll 回调,处理所有事件并分发给子类 dispatchVsync 等 纯虚函数,由子类实现(如 NativeDisplayEventReceiver)来调用 Java 方法 2 DisplayEventDispatcher.cpp
2.1 初始化
2.1.1 DisplayEventDispatcher初始化引用
在NativeDisplayEventReceiver对象构建时引用了DisplayEventDispatcher,并为初始化DisplayEventDispatcher传递了重要参数:looper、vsyncSource、configChanged。据此判断,初始化DisplayEventDispatcher则是在构建NativeDisplayEventReceiver对象时进行的。
2.1.2 初始化了哪些元素
① 调用 DisplayEventDispatcher 的构造方法来构造对象,同时将传入的 Looper 赋值给头文件中的 mLooper 保存。
② 调用 DisplayEventReceiver 的构造方法,传入 VSync 信号源等参数,并将构建的 DisplayEventReceiver 赋值给 mReceiver。
③ 将头文件中的 mWaitingForVsync 赋值为 false。
2.1.3 构造函数
DisplayEventDispatcher 初始化调度器本身及内部用于接收 VSync / Hotplug / ModeChanged 等显示事件的核心组件 —— DisplayEventReceiver。
DisplayEventDispatcher::DisplayEventDispatcher(const sp& looper, gui::ISurfaceComposer::VsyncSource vsyncSource, EventRegistrationFlags eventRegistration, const sp& layerHandle) : mLooper(looper), mReceiver(vsyncSource, eventRegistration, layerHandle), mWaitingForVsync(false), mLastVsyncCount(0), mLastScheduleVsyncTime(0) { ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this); }
参数解析:
参数 类型 作用 looper sp Native 层 Looper,用于 epoll 机制调度 fd 事件(与 Java 的 MessageQueue 一一对应) vsyncSource ISurfaceComposer::VsyncSource 指定接收哪一类 VSync,通常是 eVsyncSourceApp(主线程用) eventRegistration EventRegistrationFlags 注册感兴趣的事件类型,如 VSync、Hotplug、ModeChanged layerHandle sp 绑定的 Layer,用于 per-layer vsync 精细调度(可为 null) 成员变量的意义:
-
mLooper(looper)
保存 Native Looper 对象,稍后会在 initialize() 中调用 looper->addFd() 注册监听事件。
-
mReceiver(vsyncSource, eventRegistration, layerHandle)
接收事件的核心组件,与 SurfaceFlinger 的 Binder 通信(通过 ISurfaceComposer::createDisplayEventReceiver());对应的 epoll fd;接收事件缓冲区;但并未完成通信注册,通信是在 initialize() 中完成的。
-
mWaitingForVsync(false)
表示当前是否在等待 VSync,在 scheduleVsync() 中设置为 true,事件到达后设为 false。
-
mLastVsyncCount(0)
上一次收到的 VSync 编号;每收到一次 VSync,计数 +1,用于识别连续性或丢帧等
-
mLastScheduleVsyncTime(0)
记录上一次调用 scheduleVsync() 的时间戳;用于做时间间隔检测、节流等。
sp& looper如何传递?
2.2 initialize()
完成 DisplayEventDispatcher 的初始化工作,包括检查 DisplayEventReceiver 是否可用,以及将其关联的文件描述符注册到 Looper 中以便接收 epoll 事件(如 VSync)。
返回一个 status_t 状态码:
- OK (0) 表示初始化成功
- 其他值表示错误(如 UNKNOWN_ERROR, BAD_VALUE 等
status_t DisplayEventDispatcher::initialize() { // 检查 mReceiver 是否初始化成功 status_t result = mReceiver.initCheck(); // 创建 binder 连接到 SurfaceFlinger(通过 mReceiver.initCheck()),initCheck() 是在构造后检查是否成功连接到服务和创建了 eventfd if (result) { ALOGW("Failed to initialize display event receiver, status=%d", result); return result; } if (mLooper != nullptr) { // 注册 mReceiver 的 fd 到 Looper 监听 VSync 事件, 调用 mLooper->addFd(...),将 mReceiver.getFd() 添加到 epoll int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, this, NULL); if (rc
【注1】
① DisplayEventReceiver对象自检,mReceiver.initCheck() 检查 DisplayEventReceiver 中的 mDataChannel 是否为空,从前面的分析可知 mDataChannel 的类型为 gui::BitTube。
② 判断 mLooper 是否为空,如不为空,则调用 Looper 的 addFd() 方法将 DisplayEventReceiver 中的 mDataChannel 的 mReceiveFd 加入到 mLooper 中,同时设置回调方法,用于监测事件。注意: addFd() 方法中第四个 this 是一个 LooperCallback 回调、这里传入当前 DisplayEventDispatcher,因为 DisplayEventDispatcher 继承 LooperCallback,当 mReceiveFd 有可读事件时将触发回调。
【注2】
监听 VSync 事件说明:
-
调用 mReceiver.getFd() 获取其底层 epoll/eventfd 文件描述符;
-
将该 fd 注册到 Looper 中,监听 EVENT_INPUT 事件;
-
当 fd 可读(即 SurfaceFlinger 写入事件)时,Looper 会调用 handleEvent();
【注】关于mLooper->addFd,详细可以看 Handler消息机制-Looper(system) 关于looper的分析。
参数 含义 int fd VSync 使用的 eventfd int ident 标识符,一般为 0 int events EVENT_INPUT 表示监听可读事件(VSync 就是写入通知) Looper_callbackFunc callback this当前对象作为回调,实现了 handleEvent() void* data 用户数据,这里为 NULL 2.3 scheduleVsync()
请求下一帧的 VSync,并设置等待状态。在 Android 的渲染机制中,Choreographer 和其他组件会调用这个函数,以注册对下一次 VSync 信号 的监听 —— 通常用于驱动一次渲染流程。
status_t DisplayEventDispatcher::scheduleVsync() { // 表示当前是否已请求等待一个 VSync,避免重复调度,每一帧只调度一次。 if (!mWaitingForVsync) { ALOGV("dispatcher %p ~ Scheduling vsync.", this); // Drain all pending events. nsecs_t vsyncTimestamp; PhysicalDisplayId vsyncDisplayId; uint32_t vsyncCount; VsyncEventData vsyncEventData; // 尝试从 DisplayEventReceiver 的内部缓冲区中拉取已经积压但尚未处理的事件 if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncEventData)) { ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", this, ns2ms(static_cast(vsyncTimestamp))); } // 向 SurfaceFlinger 请求下一次 VSync status_t status = mReceiver.requestNextVsync(); if (status) { ALOGW("Failed to request next vsync, status=%d", status); return status; } mWaitingForVsync = true; // 设置等待状态,防止下一次重复调用 mLastScheduleVsyncTime = systemTime(SYSTEM_TIME_MONOTONIC); // 记录调度时间,可用于后续分析帧间隔、掉帧等行为 } return OK; }
2.4 handleEvent(int, int events, void*)
DisplayEventDispatcher::handleEvent(…) 是 DisplayEventDispatcher 类作为 LooperCallback 的核心回调函数,负责响应 epoll 事件(fd 可读等)并处理 VSync 信号。
在Looper对 epoll 事件进行处理的过程中,会取出每个 Request 并调用其 callback 回调的 handleEvent() 方法。该回调就是向 Looper 中添加 fd 时作为 LooperCallback 回调一并传入的 DisplayEventDispatcher 实现类。因此,也就是在epoll 事件进行处理时回调 DisplayEventDispatcher::handleEvent 函数。如果读取到 VSync,会调用子类实现的 dispatchVsync(…) 通知 Java 层。
int DisplayEventDispatcher::handleEvent(int, int events, void * )
- 第一个参数是 fd(未使用)
- 第二个是 events,表示触发的 epoll 事件掩码
- 第三个是用户数据 void*(此处未用)
int DisplayEventDispatcher::handleEvent(int, int events, void*) { // 处理错误事件 if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { ALOGE("Display event receiver pipe was closed or an error occurred. " "events=0x%x", events); return 0; // remove the callback } // 非 EVENT_INPUT 事件(不应触发) if (!(events & Looper::EVENT_INPUT)) { ALOGW("Received spurious callback for unhandled poll event. " "events=0x%x", events); return 1; // keep the callback } // Drain all pending events, keep the last vsync. // 定义一组变量用于保存最近一次 VSync 信息 nsecs_t vsyncTimestamp; PhysicalDisplayId vsyncDisplayId; uint32_t vsyncCount; VsyncEventData vsyncEventData; // 调用 processPendingEvents(...) 会尝试从 DisplayEventReceiver 中读取事件并填充上述参数 if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncEventData)) { ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", displayId=%s, count=%d, vsyncId=%" PRId64, this, ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount, vsyncEventData.preferredVsyncId()); // 有 Vsync 响应 mWaitingForVsync = false; // 将 mWaitingForVsync 设为 false,表示当前 VSync 已处理 mLastVsyncCount = vsyncCount; // 记录帧数; dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData); // 调用 dispatchVsync(...),将数据上报到 Java 层 } // VSync 丢失容错机制 if (mWaitingForVsync) { // -----> 若 mWaitingForVsync 仍为 true(即 scheduleVsync() 后未收到实际事件) WAITING_FOR_VSYNC_TIMEOUT) { ALOGW("Vsync time out! vsyncScheduleDelay=%" PRId64 "ms", ns2ms(vsyncScheduleDelay)); // 手动构造一个“伪造”的 VSync,调用 dispatchVsync() 保证应用不中断。 mWaitingForVsync = false; dispatchVsync(currentTime, vsyncDisplayId /* displayId is not used */, ++mLastVsyncCount, vsyncEventData /* empty data */); } } // 1 表示 Looper 继续监听该 FD return 1; // keep the callback }
总结:
内容 描述 主动触发 被 epoll(Looper)唤醒,表示 DisplayEventReceiver 有输入 核心行为 读取 VSync 事件并转发(或热插拔等) 特殊情况 管道断开、epoll 错误、伪造 VSync(超时) 最终派发 调用虚函数 dispatchVsync(),由子类(如 NativeDisplayEventReceiver)实现并通知 Java 2.5 processPendingEvents
Native 层用于处理 Display 事件缓存队列(主要是 VSync)的关键方法。这个方法的目的是从 DisplayEventReceiver 的内部事件缓冲队列中读取并解析所有事件,并根据类型分发处理(VSync、Hotplug、ModeChange、FrameRateOverride 等)。如果有 VSYNC 事件,还会通过输出参数传出其时间戳、显示 ID、计数、附加数据。
2.5.1 函数定义
bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount, VsyncEventData* outVsyncEventData)
参数 类型 说明 outTimestamp nsecs_t* 输出参数,用于返回最新的 vsync 时间戳 outDisplayId PhysicalDisplayId* 输出参数,返回产生该事件的物理显示 ID outCount uint32_t* 输出参数,vsync 的帧计数(递增) outVsyncEventData VsyncEventData* 输出参数,vsync 附带的时序数据 2.5.2 源码分析
bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount, VsyncEventData* outVsyncEventData) { // gotVsync 标志用于判断是否读取到了 VSYNC 事件 bool gotVsync = false; // 定义事件读取缓存 buf,容量为 EVENT_BUFFER_SIZE(默认值 100) DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; ssize_t n; // 循环读取事件(阻塞在 epoll 上已触发、非阻塞读取) // mReceiver.getEvents(...) 从底层内核中的 event 缓存读取最多 EVENT_BUFFER_SIZE 个事件 while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { ALOGV("dispatcher %p ~ Read %d events.", this, int(n)); mFrameRateOverrides.reserve(n); // 为帧率覆盖事件预留空间(防止 vector 多次扩容) for (ssize_t i = 0; i VSYNC 事件 // Later vsync events will just overwrite the info from earlier // ones. That's fine, we only care about the most recent. gotVsync = true; // 设置标志 gotVsync = true // 将 timestamp / displayId / vsync count / vsync event data 输出出去 *outTimestamp = ev.header.timestamp; *outDisplayId = ev.header.displayId; *outCount = ev.vsync.count; *outVsyncEventData = ev.vsync.vsyncData; // 注意:while 循环中,如果有多个 VSync 事件,只保留最后一个(覆盖掉旧的),因为应用只关心最近一次 VSync break; case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: // ===> 热插拔事件(HDMI、屏幕连接) // 回调 dispatchHotplug --> NativeDisplayEventReceiver 的虚函数实现会调 Java 的 onHotplug() dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected); break; case DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE: // ===> 显示模式变更(刷新率、分辨率) // dispatchModeChanged --> Java 的 onModeChanged() dispatchModeChanged(ev.header.timestamp, ev.header.displayId, ev.modeChange.modeId, ev.modeChange.vsyncPeriod); break; case DisplayEventReceiver::DISPLAY_EVENT_NULL: // ===> Null Event(特殊用于 poke Looper) dispatchNullEvent(ev.header.timestamp, ev.header.displayId); break; case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE: // ===> 帧率覆盖信息(某个 UID 运行时强制帧率) mFrameRateOverrides.emplace_back(ev.frameRateOverride); break; case DisplayEventReceiver::DISPLAY_EVENT_FRAME_RATE_OVERRIDE_FLUSH: // ===> 帧率覆盖信息的刷新时机 // 将已收集的 FrameRateOverride 向上分发,使用 std::move 避免拷贝 dispatchFrameRateOverrides(ev.header.timestamp, ev.header.displayId, std::move(mFrameRateOverrides)); break; default: ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type); break; } } } if (n
总结:
内容 描述 功能 读取并分发 SurfaceFlinger 通过 binder 发送过来的 VSync 等事件 输入来源 DisplayEventReceiver::getEvents(),从内核 epoll 的 socket 缓冲区读取 支持事件 VSync / Hotplug / ModeChange / Null / FrameRateOverride 多个事件 会逐个处理,最终只保留最新一次 VSync 信息给输出参数 被谁调用 scheduleVsync() 和 handleEvent() 都会调用它来清空旧事件 2.6 DisplayEventDispatcher 的核心职责
✅ 小结
职责 说明 封装 epoll 注册 使用 Looper 管理 fd 封装 binder 接收 使用 DisplayEventReceiver 从 SF 接收事件 统一事件分发 提供虚接口供子类实现,确保所有事件均回调 事件打包解码 通过 processPendingEvents() 解码事件 支持 VSync 调度 提供 scheduleVsync() 向 SF 注册下一次请求
-
-