vsync 信号分发源码分析
Posted Android 面试官
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vsync 信号分发源码分析相关的知识,希望对你有一定的参考价值。
上周发了一篇 ,光知道生成怎么行呢?vsync 信号分发源码深度分析奉上,由底层硬件到上层 Choreographer,让我们一起来揭开 vsync 的神秘面纱吧
-
SurfaceFlinger>>DispSync -
DispSync>>DispSyncSource -
DispSyncSource>>EventThread -
Connection>>SurfaceFlinger -
Connection>>app
1. SurfaceFlinger>>DispSync
vsync 信号由 HWComposer 类生成后,会回调 SurfaceFlinger.cpp 的 onVSyncReceived 方法:
void SurfaceFlinger::onVSyncReceived(int32_t type, nsecs_t timestamp) {
mPrimaryDispSync.addResyncSample(timestamp);
}
mPrimaryDispSync 为 DispSync 类型,DispSync 在构造时开启了一个 DispSyncThread 工作线程,专门负责 vsync 信号的分发:
DispSync::DispSync(const char* name) :
mName(name),
mRefreshSkipCount(0),
mThread(new DispSyncThread(name)) {
mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
...
}
接着看 DispSync 的 addResyncSample 方法是如何处理 vsync 信号的:
bool DispSync::addResyncSample(nsecs_t timestamp) {
mResyncSamples[idx] = timestamp; //vsync 信号时间戳
updateModelLocked();
}
void DispSync::updateModelLocked() {
mThread->updateModel(mPeriod, mPhase, mReferenceTime);
}
DispSync 内部记录下 vsync 信号时间戳后,通过 updateModel 方法通知内部工作线程 DispSyncThread:
void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) {
Mutex::Autolock lock(mMutex); //锁
mPeriod = period;
mCond.signal(); //唤醒
}
注意当前还处于 vsync 信号生成线程,若是软件生成则还处于 HWComposer 的 VSyncThread 线程,为了不影响 vsync 信号的生成工作,这里切换到另外的线程去分发。
具体而言,就是通过 mCond.signal() 方法通知 DispSyncThread 线程去进一步分发,DispSyncThread 的分发工作在其 threadLoop 方法中:
virtual bool threadLoop() {
while (true) {
{
Mutex::Autolock lock(mMutex); //锁
if (mPeriod == 0) {
mCond.wait(mMutex); //没 vsync 信号就阻塞
continue;
}
now = systemTime(SYSTEM_TIME_MONOTONIC);
// 收集注册了需要接受 vsync 信号的回调
callbackInvocations = gatherCallbackInvocationsLocked(now);
}
if (callbackInvocations.size() > 0) {
//分发回调
fireCallbackInvocations(callbackInvocations);
}
}
}
至此 DispSync 中对 vsync 信号的分发工作执行完毕。
2. DispSync>>DispSyncSource
在 DispSync 内部工作线程 DispSyncThread 中,对注册需要接受 vsync 信号的回调进行调用,完成了 vsync 信号分发工作。
那具体是谁注册了监听?是谁需要进一步接受 vsync 信号呢?还是要从 DispSyncThread 的 threadLoop 方法入手,它通过 gatherCallbackInvocationsLocked 方法获取到当前已注册的监听,代码如下:
Vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
Vector<CallbackInvocation> callbackInvocations;
for (size_t i = 0; i < mEventListeners.size(); i++) {
CallbackInvocation ci;
ci.mCallback = mEventListeners[i].mCallback;
callbackInvocations.push(ci);
}
return callbackInvocations;
}
然后通过 fireCallbackInvocations 方法回调了监听:
void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) {
for (size_t i = 0; i < callbacks.size(); i++) {
callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
}
}
DispSync 内部对监听进行了封装,我们只关注外部传来的 mCallback,其为 DispSync::Callback 类型。通过以上代码可以知道 mCallback 被记录在 mEventListeners 中,那只要再看下哪里将监听添加到 mEventListeners 中,就能知道到底是谁注册的监听了。
在 SurfaceFlinger 初始化时 DispSyncSource 与 DispSync(mPrimaryDispSync) 产生关联:
void SurfaceFlinger::init() {
sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
vsyncPhaseOffsetNs, true, "app");
mEventThread = new EventThread(vsyncSrc, *this);
sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
sfVsyncPhaseOffsetNs, true, "sf");
mSFEventThread = new EventThread(sfVsyncSrc, *this);
...
}
也就是说 DispSyncSource 内部持有 DispSync,在 DispSyncSource 内部将监听注册到 DispSync 中:
virtual void setVSyncEnabled(bool enable) {
Mutex::Autolock lock(mVsyncMutex);
if (enable) {
status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
static_cast<DispSync::Callback*>(this));
} else {
status_t err = mDispSync->removeEventListener(
static_cast<DispSync::Callback*>(this));
}
mEnabled = enable;
}
DispSyncSource 本身继承了 DispSync::Callback 接口,即直接将自身作为监听注册到 DispSync 中。
也就是说,在 DispSync 内部工作线程中通过 fireCallbackInvocations 分发 vsync 信号,实际上调用了 DispSyncSource 的 onDispSyncEvent 方法。
需要注意的是,SurfaceFlinger init 时创建了两个 DispSyncSource,分别是上层 app 和 surfaceFlinger 自身,为什么要分这两种呢?仅仅是代码上的逻辑分离吗?
仔细观察 SurfaceFlinger init 中对这两个 DispSyncSource 的构造入参,分别传入了 vsyncPhaseOffsetNs 和 sfVsyncPhaseOffsetNs,这是要实现 vsync 信号的错时分发,即不同时分发 vsync 信号给上层 app 和 surfaceFlinger。
3. DispSyncSource>>EventThread
接着来看 DispSyncSource 的 onDispSyncEvent 方法是如何处理 vsync 信号的:
virtual void onDispSyncEvent(nsecs_t when) {
sp<VSyncSource::Callback> callback;
{
Mutex::Autolock lock(mCallbackMutex);
callback = mCallback;
}
if (callback != NULL) {
callback->onVSyncEvent(when);
}
}
又是一个 callBack 回调了出去,这个 callBack 又是谁呢?可以再看一下上面的 SurfaceFlinger init 方法,类似于 DispSyncSource 向 DispSync 注册监听,EventThread 构造时传入了 DispSyncSource,EventThread 向 DispSyncSource 注册了监听。
callBack 类型为 VSyncSource::Callback,而 EventThread 自身也实现了这个接口:
class EventThread : public Thread, private VSyncSource::Callback
接着来看 EventThread 内部实现的 onVSyncEvent 方法:
void EventThread::onVSyncEvent(nsecs_t timestamp) {
Mutex::Autolock _l(mLock);
mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
mVSyncEvent[0].header.id = 0;
mVSyncEvent[0].header.timestamp = timestamp; //vsync 时间戳
mVSyncEvent[0].vsync.count++;
mCondition.broadcast();
}
注意当前还处于 DispSync 的工作线程,这里将 vsync 信号记录到 DisplayEventReceiver::Event 类型的 mVSyncEvent 数组中后,通过 mCondition.broadcast() 通知 EventThread 工作线程去处理,即从 DispSync 工作线程切换到了 EventThread 工作线程。
下面来看 EventThread 的工作线程中是如何处理的:
bool EventThread::threadLoop() {
DisplayEventReceiver::Event event;
Vector< sp<EventThread::Connection> > signalConnections;
signalConnections = waitForEvent(&event);
const size_t count = signalConnections.size();
for (size_t i=0 ; i<count ; i++) {
const sp<Connection>& conn(signalConnections[i]);
status_t err = conn->postEvent(event);
}
return true;
}
同样是通过返回 true 来实现线程无限循环调用 threadLoop 方法,其中比较关键的是 waitForEvent 方法,关键逻辑如下:
Vector< sp<EventThread::Connection> > EventThread::waitForEvent(
DisplayEventReceiver::Event* event){
Vector< sp<EventThread::Connection> > signalConnections;
do {
检测 mVSyncEvent 数组中是否有 vsync 信号
如果有传递给 event 参数,并开始遍历所有注册的 connection
如果 connection.count >= 0,就加到 signalConnections 中
if(waitForVSync){
mCondition.waitRelative(mLock, timeout); //休眠等待
}
} while (signalConnections.isEmpty());
return signalConnections;
}
梳理一下 EventThread 工作线程中的逻辑:通过 waitForEvent 方法进入休眠等待 vsync 信号,如果有 vsync 信号会唤醒,并将 vsync 信号传递给 event,返回 Connection,随后将 vsync 信号分发给了 Connection。
现在我们的关注点变为 EventThread::Connection,它是谁注册的?其 postEvent 方法又是如何进一步分发 vsync 信号的呢?
4. Connection>>SurfaceFlinger
依然聚焦于 vsync 的传递为脉络,接着来看 Connection 的 postEvent 方法:
status_t EventThread::Connection::postEvent(
const DisplayEventReceiver::Event& event) {
DisplayEventReceiver::sendEvents(mChannel, &event, 1);
}
ssize_t DisplayEventReceiver::sendEvents(const sp<BitTube>& dataChannel,
Event const* events, size_t count){
return BitTube::sendObjects(dataChannel, events, count);
}
ssize_t BitTube::sendObjects(const sp<BitTube>& tube,
void const* events, size_t count, size_t objSize){
const char* vaddr = reinterpret_cast<const char*>(events);
ssize_t size = tube->write(vaddr, count*objSize);
}
可以看到 Connection 内部持有一个 BitTube,BitTube 是用来处理进程间通讯的机制,和管道类似,基于 SocketPair 封装实现。SocketPair 用来创建一对未命名、互相连接的套接字,套接字的一端可以进行读和写的操作,用来实现全双工的通讯。
在 Connection 中将 vsync 信号数据写入了 BitTube,那在哪里监听读呢?这就要分 SurfaceFlinger 与 app 两路了,首先来看 SurfaceFlinger 是如何接收的。再回到 SurfaceFlinger 的 init 方法:
void SurfaceFlinger::init() {
sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
vsyncPhaseOffsetNs, true, "app");
mEventThread = new EventThread(vsyncSrc, *this);
sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
sfVsyncPhaseOffsetNs, true, "sf");
mSFEventThread = new EventThread(sfVsyncSrc, *this);
mEventQueue.setEventThread(mSFEventThread);
...
}
在构造完 SurfaceFlinger 的 EventThread 后,通过 setEventThread 方法将 EventThread 设置给 mEventQueue,mEventQueue 为 MessageQueue 类型,方法如下:
void MessageQueue::setEventThread(const sp<EventThread>& eventThread){
mEventThread = eventThread;
mEvents = eventThread->createEventConnection();
mEventTube = mEvents->getDataChannel();
mLooper->addFd(mEventTube->getFd(), 0, Looper::EVENT_INPUT,
MessageQueue::cb_eventReceiver, this);
}
这下就全走通了,SurfaceFlinger 的消息队列监听了 Connection 中的 BitTube,当 vsync 信号在 EventThread 中分发给 Connection 写入 BitTube 后,SurfaceFlinger 的消息队列就能收到通知了。
接着来看 SurfaceFlinger 的 MessageQueue 如何进一步处理 vsync 消息,在 MessageQueue 的 setEventThread 方法中设置接受到 vsync 后回调 cb_eventReceiver 方法:
int MessageQueue::cb_eventReceiver(int fd, int events, void* data) {
MessageQueue* queue = reinterpret_cast<MessageQueue *>(data);
return queue->eventReceiver(fd, events);
}
cb_eventReceiver 进一步调用 eventReceiver 方法:
int MessageQueue::eventReceiver(int /*fd*/, int /*events*/) {
ssize_t n;
DisplayEventReceiver::Event buffer[8];
while ((n = DisplayEventReceiver::getEvents(mEventTube, buffer, 8)) > 0) {
for (int i=0 ; i<n ; i++) {
if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
#if INVALIDATE_ON_VSYNC
mHandler->dispatchInvalidate();
#else
mHandler->dispatchRefresh();
#endif
break;
}
}
}
return 1;
}
可以看到读取出 vsync 信号数据后,只用到了 type 信息,然后通过 Handler 回调出去处理。
至此 vsync -> SurfaceFlinger 的分发流程分析完毕。
5. Connection>>app
尽管 vsync 传递给 SurfaceFlinger 流程发生在同个进程,却使用了支持跨进程的 BitTube。为什么要使用 BitTube 呢?可以想到的一个优点是,可以与 vsync 信号传递给上层 app 进程的方式统一。
我们知道上层 app 会统一将 UI 刷新请求发给 Choreographer,Choreographer 会在下次 vsync 信号到来时真正执行 UI 绘制,下面通过源码来搞懂底层的 vsync 是如何传递至 Choreographer 的。
Choreographer 中实际与 vsync 信号相关的逻辑在 DisplayEventReceiver 中,初始化 Choreographer 时会构造 DisplayEventReceiver:
private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;
mHandler = new FrameHandler(looper);
mDisplayEventReceiver = new FrameDisplayEventReceiver(looper, vsyncSource);
...
}
DisplayEventReceiver 构造函数中会调用到 DisplayEventReceiver.java 的 nativeInit 方法:
private static native long nativeInit(
WeakReference<DisplayEventReceiver> receiver,
MessageQueue messageQueue, int vsyncSource);
底层实现位于 android_view_DisplayEventReceiver.cpp 中:
static jlong nativeInit(JNIEnv* env,
jclass clazz,
jobject receiverWeak, jobject messageQueueObj) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
receiverWeak, messageQueue);
status_t status = receiver->initialize();
...
return reinterpret_cast<jlong>(receiver.get());
}
构造了一个 NativeDisplayEventReceiver,其继承自 DisplayEventReceiver,在后者的构造函数中,创建了关键的 IDisplayEventConnection binder 接口,建立与 SurfaceFlinger 通信的连接:
DisplayEventReceiver::DisplayEventReceiver() {
// ISurfaceComposer binder 接口
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
if (sf != NULL) {
// IDisplayEventConnection binder 接口
mEventConnection = sf->createDisplayEventConnection();
if (mEventConnection != NULL) {
mDataChannel = mEventConnection->getDataChannel();
}
}
}
为什么要将 MessageQueue 传递下来呢?因为要借助 Android Handler 已有的功能,即基于内部 epoll 机制添加对某文件描述符事件的监听,上文传递给 SurfaceFlinger 的原理也是如此。
nativeInit 中构造 NativeDisplayEventReceiver 后调用了其 initialize 方法,内部便通过 Looper 添加了对文件描述符的监听:
status_t DisplayEventDispatcher::initialize() {
int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, this, NULL);
return OK;
}
然后再回到上层的 Choreographer ,其通过 scheduleVsync 请求下一次的 vsync 信号,最终会调用到 DisplayEventReceiver 的 native 方法:
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
private static native void nativeScheduleVsync(long receiverPtr);
底层同样是在 android_view_DisplayEventReceiver.cpp 中:
static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) {
sp<NativeDisplayEventReceiver> receiver =
reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
receiver->scheduleVsync();
}
最终调用到 DisplayEventReceiver 的 requestNextVsync 方法:
status_t DisplayEventReceiver::requestNextVsync() {
mEventConnection->requestNextVsync();
}
上面提到 mEventConnection 是一个 binder 接口,跨进程调用到 BnDisplayEventConnection,而后者的实现类就是 SurfaceFlinger 中的 EventThread:
class EventThread : public Thread, private VSyncSource::Callback {
class Connection : public BnDisplayEventConnection {...}
所以会进一步调用到 EventThread 的 requestNextVsync 方法:
void EventThread::requestNextVsync(
const sp<EventThread::Connection>& connection) {
Mutex::Autolock _l(mLock);
mFlinger.resyncWithRateLimit();
if (connection->count < 0) {
connection->count = 0;
mCondition.broadcast();
}
}
上文对 EventThread 做过详细的分析,这里再贴一遍其工作线程的关键逻辑代码:
Vector< sp<EventThread::Connection> > EventThread::waitForEvent(
DisplayEventReceiver::Event* event){
Vector< sp<EventThread::Connection> > signalConnections;
do {
检测 mVSyncEvent 数组中是否有 vsync 信号
如果有传递给 event 参数,并开始遍历所有注册的 connection
如果 connection.count >= 0,就加到 signalConnections 中
if(waitForVSync){
mCondition.waitRelative(mLock, timeout); //休眠等待
}
} while (signalConnections.isEmpty());
return signalConnections;
}
如上,在 requestNextVsync 中将 connection->count 置为 0 然后唤醒 EventThread,EventThread 被唤醒后会将 connection 加入要通知 vsync 的 signalConnections 中,随后与上文中分析过的逻辑一致,通过 BitTube+looper 进一步分发 vsync 信号。
值得注意的是,在 waitForEvent 中判断如果 connection->count 为 0,会将 count 置为 -1,这意味着 requestNextVsync 方法名副其实,只是一次性请求接受一次 vsync 信号而已,要再调用 requestNextVsync 才能再次接受 vsync 信号。
最后再来看 vsync 信号传递到 java 层的流程,vsync 消息到来通过 dispatchVsync 方法分发:
int DisplayEventDispatcher::handleEvent(int, int events, void*) {
...
if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
mWaitingForVsync = false;
dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
}
return 1;
}
然后又回到了与 java 层最接近的 android_view_DisplayEventReceiver.cpp 中:
void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
if (receiverObj.get()) {
env->CallVoidMethod(receiverObj.get(),
gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, id, count);
}
}
jni 调用了上层 DisplayEventReceiver.java 的 dispatchVsync 方法:
// Called from native code.
@SuppressWarnings("unused")
private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
onVsync(timestampNanos, builtInDisplayId, frame);
}
随后进一步回调到 Choreographer 的 doFrame,执行 ViewRootImpl 传过来的 TraversalRunnable、调用 performTraversals 方法,由顶而下的执行界面绘制逻辑。
最后总结一下:vsync 信号分为 SurfaceFlinger、app 两路分发,这两路分别对应于一个 EventThread。当 vsync 信号到来时,会唤醒 EventThread 线程,然后 EventThread 通过注册到内部的 Connection 分发出去。Connection 进一步通过 BitTube 并使用消息机制 looper 监听 BitTube 的 fd,从而实现 vsync 的分发。
关注我
助你升职加薪
Android 面试官
以上是关于vsync 信号分发源码分析的主要内容,如果未能解决你的问题,请参考以下文章
Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段