SurfaceFlinger原理(二):Vsync事件的处理

  SurfaceFlinger内部有两个EventThread,一个负责app端对Vsync信号的监听处理,一个负责SurfaceFlinger对Vsync信号的监听处理。SurfaceFlinger内部维持了一个MessageQueue,当SurfaceFlinger端的EventThread监测到Vsync事件,会触发INVALIDATE操作,即当前的图层失效了,需要重新计算刷新。

/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

void SurfaceFlinger::onFirstRef()
{
    mEventQueue.init(this);
}

/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

    sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
            sfVsyncPhaseOffsetNs, true, "sf");
    mSFEventThread = new EventThread(sfVsyncSrc);
    mEventQueue.setEventThread(mSFEventThread);

/frameworks/native/services/surfaceflinger/MessageQueue.cpp

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);
}

  每个EventThread内部都维持一个mDisplayEventConnections,记录了所有跟这个EventThread的Connection对象的集合。app端通过createDisplayEventConnection构建一个Connection,SurfaceFlinger端通过setEventThread–>createEventConnection。app端的Connection可能不止一个,SurfaceFlinger端的Connection只有一个。
  Connection的作用是什么?先看看Connection的构造函数:

/frameworks/native/services/surfaceflinger/EventThread.cpp

EventThread::Connection::Connection(
        const sp<EventThread>& eventThread)
    : count(-1), mEventThread(eventThread), mChannel(new BitTube())
{
}

  mEventThread初始化为连接到的EventThread,即是将保存到这个EventThread中的mDisplayEventConnections中。count值表示Connection是否有感兴趣的事件(通常这个感兴趣的事件就是Vsync)。-1表示感兴趣的事件已经被处理或者没有感兴趣的事件(因为初始化为-1),0表示一次性的感兴趣的事件,这种事件被处理后count值会变成-1,大于等于1表示是连续事件。mChannel是一个BitTube,可以将其理解是用socketpair调用创建的两个互相连接的套接字,Android将其作为管道用,MessageQueue::setEventThread将监听读端的套接字的输入事件,从而回调cb_eventReceiver进行处理。
  EventThread的构造函数中,会对mVSyncEvent进行初始化,mVSyncEvent是一个长度为2的DisplayEventReceiver::Event数组,用来存储主屏幕(下标为0)和外界屏幕(下标为1)的Vsync事件的信息,如id,时间戳,count值(这个count值表示接收到的Vsync事件数量)等。然后在onFirstRef中,EventThread会进入threadLoop循环。

/frameworks/native/services/surfaceflinger/EventThread.cpp

EventThread::EventThread(const sp<VSyncSource>& src)
    : mVSyncSource(src),
      mUseSoftwareVSync(false),
      mVsyncEnabled(false),
      mDebugVsyncEnabled(false),
      mVsyncHintSent(false) {

    for (int32_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
        mVSyncEvent[i].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
        mVSyncEvent[i].header.id = 0;
        mVSyncEvent[i].header.timestamp = 0;
        mVSyncEvent[i].vsync.count =  0;
    }
    struct sigevent se;
    se.sigev_notify = SIGEV_THREAD;
    se.sigev_value.sival_ptr = this;
    se.sigev_notify_function = vsyncOffCallback;
    se.sigev_notify_attributes = NULL;
    timer_create(CLOCK_MONOTONIC, &se, &mTimerId);
}

  threadLoop看似很简短,但其实内部比较复杂。

/frameworks/native/services/surfaceflinger/EventThread.cpp

bool EventThread::threadLoop() {
    DisplayEventReceiver::Event event;
    Vector< sp<EventThread::Connection> > signalConnections;
    signalConnections = waitForEvent(&event);

    // dispatch events to listeners...
    const size_t count = signalConnections.size();
    for (size_t i=0 ; i<count ; i++) {
        const sp<Connection>& conn(signalConnections[i]);
        // now see if we still need to report this event
        status_t err = conn->postEvent(event);
        if (err == -EAGAIN || err == -EWOULDBLOCK) {
            // The destination doesn't accept events anymore, it's probably
            // full. For now, we just drop the events on the floor.
            // FIXME: Note that some events cannot be dropped and would have
            // to be re-sent later.
            // Right-now we don't have the ability to do this.
            ALOGW("EventThread: dropping event (%08x) for connection %p",
                    event.header.type, conn.get());
        } else if (err < 0) {
            // handle any other error on the pipe as fatal. the only
            // reasonable thing to do is to clean-up this connection.
            // The most common error we'll get here is -EPIPE.
            removeDisplayEventConnection(signalConnections[i]);
        }
    }
    return true;
}

  首先看EventThread::waitForEvent,该函数用来等待一个Connection发生感兴趣的事件,即Vsync事件。我们从EventThread初始化开始分析,条理会比较清晰。初始化时,timestamp为0,会直接mDisplayEventConnections中寻找发生了Vsync的Connection,条件是遍历mDisplayEventConnections中的所用Connection,若发现其count值是0或1或者接收到的Vsync事件数量能够被Connection的count值整除,则认为发生了Vsync,将这个Connection加入到signalConnections中。根据Connection的count值的不同,一次性事件(count为0)发生的Connection会将其count值置为-1,而连续事件发生的Connection不需要重置。waitForVSync表示是否有一个Connection是否对Vsync感兴趣,当一个不为null的Connection的count大于等于0时,waitForVSync就会被设为true。
  初始化时,timestamp是0,waitForVSync为false,因此线程会阻塞在mCondition.wait中。mCondition唤醒的条件之一是由Vsync事件到来。

/frameworks/native/services/surfaceflinger/EventThread.cpp

// This will return when (1) a vsync event has been received, and (2) there was
// at least one connection interested in receiving it when we started waiting.
Vector< sp<EventThread::Connection> > EventThread::waitForEvent(
        DisplayEventReceiver::Event* event)
{
    Mutex::Autolock _l(mLock);
    Vector< sp<EventThread::Connection> > signalConnections;

    do {
        bool eventPending = false;
        bool waitForVSync = false;

        size_t vsyncCount = 0;
        nsecs_t timestamp = 0;
        for (int32_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
            timestamp = mVSyncEvent[i].header.timestamp;
            if (timestamp) {
                // we have a vsync event to dispatch
                *event = mVSyncEvent[i];
                mVSyncEvent[i].header.timestamp = 0;
                vsyncCount = mVSyncEvent[i].vsync.count;
                break;
            }
        }

        if (!timestamp) {
            // no vsync event, see if there are some other event
            eventPending = !mPendingEvents.isEmpty();
            if (eventPending) {
                // we have some other event to dispatch
                *event = mPendingEvents[0];
                mPendingEvents.removeAt(0);
            }
        }

        // find out connections waiting for events
        size_t count = mDisplayEventConnections.size();
        for (size_t i=0 ; i<count ; i++) {
            sp<Connection> connection(mDisplayEventConnections[i].promote());
            if (connection != NULL) {
                bool added = false;
                if (connection->count >= 0) {
                    // we need vsync events because at least
                    // one connection is waiting for it
                    waitForVSync = true;
                    if (timestamp) {
                        // we consume the event only if it's time
                        // (ie: we received a vsync event)
                        if (connection->count == 0) {
                            // fired this time around
                            connection->count = -1;
                            signalConnections.add(connection);
                            added = true;
                        } else if (connection->count == 1 ||
                                (vsyncCount % connection->count) == 0) {
                            // continuous event, and time to report it
                            signalConnections.add(connection);
                            added = true;
                        }
                    }
                }

                if (eventPending && !timestamp && !added) {
                    // we don't have a vsync event to process
                    // (timestamp==0), but we have some pending
                    // messages.
                    signalConnections.add(connection);
                }
            } else {
                // we couldn't promote this reference, the connection has
                // died, so clean-up!
                //Connection已经无效
                mDisplayEventConnections.removeAt(i);
                --i; --count;
            }
        }

        // Here we figure out if we need to enable or disable vsyncs
        if (timestamp && !waitForVSync) {
            // we received a VSYNC but we have no clients
            // don't report it, and disable VSYNC events
            disableVSyncLocked();
        } else if (!timestamp && waitForVSync) {
            // we have at least one client, so we want vsync enabled
            // (TODO: this function is called right after we finish
            // notifying clients of a vsync, so this call will be made
            // at the vsync rate, e.g. 60fps.  If we can accurately
            // track the current state we could avoid making this call
            // so often.)
            enableVSyncLocked();
        }

        // note: !timestamp implies signalConnections.isEmpty(), because we
        // don't populate signalConnections if there's no vsync pending
        if (!timestamp && !eventPending) {
            // wait for something to happen
            if (waitForVSync) {
                // This is where we spend most of our time, waiting
                // for vsync events and new client registrations.
                //
                // If the screen is off, we can't use h/w vsync, so we
                // use a 16ms timeout instead.  It doesn't need to be
                // precise, we just need to keep feeding our clients.
                //
                // We don't want to stall if there's a driver bug, so we
                // use a (long) timeout when waiting for h/w vsync, and
                // generate fake events when necessary.
                bool softwareSync = mUseSoftwareVSync;
                nsecs_t timeout = softwareSync ? ms2ns(16) : ms2ns(1000);
                if (mCondition.waitRelative(mLock, timeout) == TIMED_OUT) {
                    if (!softwareSync) {
                        ALOGW("Timed out waiting for hw vsync; faking it");
                    }
                    // FIXME: how do we decide which display id the fake
                    // vsync came from ?
                    mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
                    mVSyncEvent[0].header.id = DisplayDevice::DISPLAY_PRIMARY;
                    mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
                    mVSyncEvent[0].vsync.count++;
                }
            } else {
                // Nobody is interested in vsync, so we just want to sleep.
                // h/w vsync should be disabled, so this will wait until we
                // get a new connection, or an existing connection becomes
                // interested in receiving vsync again.
                //初始化时会阻塞于此
                mCondition.wait(mLock);
            }
        }
    } while (signalConnections.isEmpty());

    // here we're guaranteed to have a timestamp and some connections to signal
    // (The connections might have dropped out of mDisplayEventConnections
    // while we were asleep, but we'll still have strong references to them.)
    return signalConnections;
}

  Vsync怎么产生的呢?之前有提到HWComposer的构造函数会注册一些回调函数到hwc层:hook_invalidate和hook_vsync。其中hook_vsync就是产生硬件Vsync后进行的回调函数。产生硬件Vsync的代码在厂家的hwc HAL层代码中,我们直接看回调函数的代码。
  HWComposer::vsync带回了两个参数:第一个参数是display id,第二个参数是Vsync产生的时间戳。mEventHandler表示的就是SurfaceFlinger。

/frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp

void HWComposer::vsync(int disp, int64_t timestamp) {
    if (uint32_t(disp) < HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
        {
            Mutex::Autolock _l(mLock);

            // There have been reports of HWCs that signal several vsync events
            // with the same timestamp when turning the display off and on. This
            // is a bug in the HWC implementation, but filter the extra events
            // out here so they don't cause havoc downstream.
            if (timestamp == mLastHwVSync[disp]) {
                ALOGW("Ignoring duplicate VSYNC event from HWC (t=%" PRId64 ")",
                        timestamp);
                return;
            }

            mLastHwVSync[disp] = timestamp;
        }

        char tag[16];
        snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp);
        ATRACE_INT(tag, ++mVSyncCounts[disp] & 1);

        mEventHandler.onVSyncReceived(disp, timestamp);
    }
}

  当对应的屏幕是主屏幕且已打开Vsync时,通过DispSync::addResyncSample的返回值needsHwVsync决定要不要继续打开Vsync或者关闭Vsync。

/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
    bool needsHwVsync = false;

    { // Scope for the lock
        Mutex::Autolock _l(mHWVsyncLock);
        if (type == 0 && mPrimaryHWVsyncEnabled) {
            needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);
        }
    }

    if (needsHwVsync) {
        enableHardwareVsync();
    } else {
        disableHardwareVsync(false);
    }
}

  DispSync::addResyncSample接收以个Vsync的事件戳作为参数,是怎么计算出是否需要开启Vsync的呢?mPrimaryDispSync内部维持了一个长度为32的时间戳数组mResyncSamples,而addResyncSample要做的,就是将这次Vsync事件的事件戳加入到里面去,重新更新Vsync模型,并根据结果决定是否开启Vsync。mFirstResyncSample在时间戳总数未达到32时,记录的是mResyncSamples的首个元素位置,也就是0;在时间戳总数达到32时,记录的是插入新的时间戳的位置。mNumResyncSamples表示当前的时间戳总数,最大为32。mResyncSamples里面的时间戳按从旧到新进行排列,总是保留最新的时间戳,如果时间戳数量大于32个的话,则新加入的时间戳会覆盖掉时间最早的时间戳。updateModelLocked用来更新Vsync模型。
  如果kIgnorePresentFences的值为true,则根据DispSyncThread是否存在Vsync事件回调决定是否开启Vsync,若存在则开启;如果kIgnorePresentFences的值为false,周期mPeriod为0或者偏差mError大于阈值kErrorThreshold时,则需要开启Vsync进行校正。

/frameworks/native/services/surfaceflinger/DispSync.cpp

bool DispSync::addResyncSample(nsecs_t timestamp) {
    Mutex::Autolock lock(mMutex);

    size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
    mResyncSamples[idx] = timestamp;

    if (mNumResyncSamples < MAX_RESYNC_SAMPLES) {
        mNumResyncSamples++;
    } else {
        mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES;
    }

    updateModelLocked();

    if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) {
        resetErrorLocked();
    }

    if (kIgnorePresentFences) {
        // If we don't have the sync framework we will never have
        // addPresentFence called.  This means we have no way to know whether
        // or not we're synchronized with the HW vsyncs, so we just request
        // that the HW vsync events be turned on whenever we need to generate
        // SW vsync events.
        return mThread->hasAnyEventListeners();
    }

    return mPeriod == 0 || mError > kErrorThreshold;
}

  当定义了宏RUNNING_WITHOUT_SYNC_FRAMEWORK或者app Vsync相移和SurfaceFlinger vsync相移均为0时,kIgnorePresentFences 为true;否则为false。

/frameworks/native/services/surfaceflinger/DispSync.h

// Ignore present (retire) fences if the device doesn't have support for the
// sync framework, or if all phase offsets are zero.  The latter is useful
// because it allows us to avoid resync bursts on devices that don't need
// phase-offset VSYNC events.
#if defined(RUNNING_WITHOUT_SYNC_FRAMEWORK) || \
        (VSYNC_EVENT_PHASE_OFFSET_NS == 0 && SF_VSYNC_EVENT_PHASE_OFFSET_NS == 0)
static const bool kIgnorePresentFences = true;
#else
static const bool kIgnorePresentFences = false;
#endif

  当样本时间戳数量大于等于3时,才会更新Vsync模型。更新Vsync模型的方式如下。1.周期mPeriod:从最旧的时间戳起,用第二旧的时间戳减去最旧的时间戳得到期间Vsync间隔时间;然后用第三旧的时间戳减去第二旧的时间戳得到期间Vsync的间隔时间。。。直到得到时间戳总数减1数量(mNumResyncSamples - 1)的Vsync间隔时间总和,求取其平均值即为周期mPeriod 。2.相移mPhase:以mPeriod 为周期,算出其角速度scale=2pi/mPeriod ,对于mNumResyncSamples个时间戳,分别计算出它们的相移角度samplePhase=时间戳%周期*角速度。sampleAvgX为相移角度的cos值的累加,sampleAvgY为相移角度的sin值的累加值。sampleAvgX和sampleAvgY分别为sampleAvgX和sampleAvgY的平均值。atan2(sampleAvgY, sampleAvgX)计算出平均每个时间戳的偏移角度,除以角速度就是相移mPhase,准确点叫相移时间。为确保mPhase为正数,当计算出的mPhase小于0时,为其加上mPeriod 得到一个新的mPhase值。最后通过updateModel将mPeriod 和mPhase设置到DispSyncThread里面。

/frameworks/native/services/surfaceflinger/DispSync.cpp

void DispSync::updateModelLocked() {
    if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) {
        nsecs_t durationSum = 0;
        for (size_t i = 1; i < mNumResyncSamples; i++) {
            size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
            size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES;
            durationSum += mResyncSamples[idx] - mResyncSamples[prev];
        }

        mPeriod = durationSum / (mNumResyncSamples - 1);

        double sampleAvgX = 0;
        double sampleAvgY = 0;
        double scale = 2.0 * M_PI / double(mPeriod);
        for (size_t i = 0; i < mNumResyncSamples; i++) {
            size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
            nsecs_t sample = mResyncSamples[idx];
            double samplePhase = double(sample % mPeriod) * scale;
            sampleAvgX += cos(samplePhase);
            sampleAvgY += sin(samplePhase);
        }

        sampleAvgX /= double(mNumResyncSamples);
        sampleAvgY /= double(mNumResyncSamples);

        mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);

        if (mPhase < 0) {
            mPhase += mPeriod;
        }

        if (kTraceDetailedInfo) {
            ATRACE_INT64("DispSync:Period", mPeriod);
            ATRACE_INT64("DispSync:Phase", mPhase);
        }

        // Artificially inflate the period if requested.
        mPeriod += mPeriod * mRefreshSkipCount;

        mThread->updateModel(mPeriod, mPhase);
    }
}

    最初,DispSyncThread在threadLoop过程中会因为mPeriod为0而阻塞,updateModel会将其唤醒。

/frameworks/native/services/surfaceflinger/DispSync.cpp

    void updateModel(nsecs_t period, nsecs_t phase) {
        Mutex::Autolock lock(mMutex);
        mPeriod = period;
        mPhase = phase;
        mCond.signal();
    }

  那么问题来了,mThread->hasAnyEventListeners返回的是false(这里假定的情况是app和SurfaceFlingfer的Vsync相移为0),导致Vsync会进入关闭状态,这样刚产生了一次的Vsync又关掉了,什么时候才会开启Vsync呢?实际上,第一个Vsync是用来更新周期和相移时间用的,Vsync在恰当的时候开关,有利于节约电源,减少功耗。
  当app或者SurfaceFlinger需要Vsync来协调更新界面时,都需要调用到EventThread::requestNextVsync时。表示当前的Connection对Vsync事件感兴趣,这时会把Connection的count设为0,这时一直阻塞在条件变量mCondition的SurfaceFlinge端的EventThread会被唤醒。

/frameworks/native/services/surfaceflinger/EventThread.cpp

void EventThread::requestNextVsync(
        const sp<EventThread::Connection>& connection) {
    Mutex::Autolock _l(mLock);
    if (connection->count < 0) {
        connection->count = 0;
        mCondition.broadcast();
    }
}

  回到EventThread::waitForEvent函数。由于有Connection的count已经设置成0,waitForVSync变成true,但是timestamp仍为0,于是会调用enableVSyncLocked的逻辑使能Vsync。

/frameworks/native/services/surfaceflinger/EventThread.cpp

// This will return when (1) a vsync event has been received, and (2) there was
// at least one connection interested in receiving it when we started waiting.
Vector< sp<EventThread::Connection> > EventThread::waitForEvent(
        DisplayEventReceiver::Event* event)
{
    Mutex::Autolock _l(mLock);
    Vector< sp<EventThread::Connection> > signalConnections;

    do {
        bool eventPending = false;
        bool waitForVSync = false;

        size_t vsyncCount = 0;
        nsecs_t timestamp = 0;
        for (int32_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
            timestamp = mVSyncEvent[i].header.timestamp;
            if (timestamp) {
                // we have a vsync event to dispatch
                *event = mVSyncEvent[i];
                mVSyncEvent[i].header.timestamp = 0;
                vsyncCount = mVSyncEvent[i].vsync.count;
                break;
            }
        }

        if (!timestamp) {
            // no vsync event, see if there are some other event
            eventPending = !mPendingEvents.isEmpty();
            if (eventPending) {
                // we have some other event to dispatch
                *event = mPendingEvents[0];
                mPendingEvents.removeAt(0);
            }
        }

        // find out connections waiting for events
        size_t count = mDisplayEventConnections.size();
        for (size_t i=0 ; i<count ; i++) {
            sp<Connection> connection(mDisplayEventConnections[i].promote());
            if (connection != NULL) {
                bool added = false;
                if (connection->count >= 0) {
                    // we need vsync events because at least
                    // one connection is waiting for it
                    waitForVSync = true;
                    if (timestamp) {
                        // we consume the event only if it's time
                        // (ie: we received a vsync event)
                        if (connection->count == 0) {
                            // fired this time around
                            connection->count = -1;
                            signalConnections.add(connection);
                            added = true;
                        } else if (connection->count == 1 ||
                                (vsyncCount % connection->count) == 0) {
                            // continuous event, and time to report it
                            signalConnections.add(connection);
                            added = true;
                        }
                    }
                }

                if (eventPending && !timestamp && !added) {
                    // we don't have a vsync event to process
                    // (timestamp==0), but we have some pending
                    // messages.
                    signalConnections.add(connection);
                }
            } else {
                // we couldn't promote this reference, the connection has
                // died, so clean-up!
                mDisplayEventConnections.removeAt(i);
                --i; --count;
            }
        }

        // Here we figure out if we need to enable or disable vsyncs
        if (timestamp && !waitForVSync) {
            // we received a VSYNC but we have no clients
            // don't report it, and disable VSYNC events
            disableVSyncLocked();
        } else if (!timestamp && waitForVSync) {
            // we have at least one client, so we want vsync enabled
            // (TODO: this function is called right after we finish
            // notifying clients of a vsync, so this call will be made
            // at the vsync rate, e.g. 60fps.  If we can accurately
            // track the current state we could avoid making this call
            // so often.)
            enableVSyncLocked();
        }

        // note: !timestamp implies signalConnections.isEmpty(), because we
        // don't populate signalConnections if there's no vsync pending
        if (!timestamp && !eventPending) {
            // wait for something to happen
            if (waitForVSync) {
                // This is where we spend most of our time, waiting
                // for vsync events and new client registrations.
                //
                // If the screen is off, we can't use h/w vsync, so we
                // use a 16ms timeout instead.  It doesn't need to be
                // precise, we just need to keep feeding our clients.
                //
                // We don't want to stall if there's a driver bug, so we
                // use a (long) timeout when waiting for h/w vsync, and
                // generate fake events when necessary.
                bool softwareSync = mUseSoftwareVSync;
                nsecs_t timeout = softwareSync ? ms2ns(16) : ms2ns(1000);
                if (mCondition.waitRelative(mLock, timeout) == TIMED_OUT) {
                    if (!softwareSync) {
                        ALOGW("Timed out waiting for hw vsync; faking it");
                    }
                    // FIXME: how do we decide which display id the fake
                    // vsync came from ?
                    mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
                    mVSyncEvent[0].header.id = DisplayDevice::DISPLAY_PRIMARY;
                    mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
                    mVSyncEvent[0].vsync.count++;
                }
            } else {
                // Nobody is interested in vsync, so we just want to sleep.
                // h/w vsync should be disabled, so this will wait until we
                // get a new connection, or an existing connection becomes
                // interested in receiving vsync again.
                mCondition.wait(mLock);
            }
        }
    } while (signalConnections.isEmpty());

    // here we're guaranteed to have a timestamp and some connections to signal
    // (The connections might have dropped out of mDisplayEventConnections
    // while we were asleep, but we'll still have strong references to them.)
    return signalConnections;
}

  mVSyncSource指的是DispSyncSource对象。

/frameworks/native/services/surfaceflinger/EventThread.cpp

void EventThread::enableVSyncLocked() {
    if (!mUseSoftwareVSync) {
        // never enable h/w VSYNC when screen is off
        if (!mVsyncEnabled) {
            mVsyncEnabled = true;
            mVSyncSource->setCallback(static_cast<VSyncSource::Callback*>(this));
            mVSyncSource->setVSyncEnabled(true);
        }
    }
    mDebugVsyncEnabled = true;
    sendVsyncHintOnLocked();
}

  EventThread继承自VSyncSource::Callback,这里将mCallback 设置成EventThread类型的入参。

/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

    virtual void setCallback(const sp<VSyncSource::Callback>& callback) {
        Mutex::Autolock lock(mMutex);
        mCallback = callback;
    }

  DispSyncSource继承自DispSync::Callback。

/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

    virtual void setVSyncEnabled(bool enable) {
        // Do NOT lock the mutex here so as to avoid any mutex ordering issues
        // with locking it in the onDispSyncEvent callback.
        if (enable) {
            status_t err = mDispSync->addEventListener(mPhaseOffset,
                    static_cast<DispSync::Callback*>(this));
            if (err != NO_ERROR) {
                ALOGE("error registering vsync callback: %s (%d)",
                        strerror(-err), err);
            }
            //ATRACE_INT(mVsyncOnLabel.string(), 1);
        } else {
            status_t err = mDispSync->removeEventListener(
                    static_cast<DispSync::Callback*>(this));
            if (err != NO_ERROR) {
                ALOGE("error unregistering vsync callback: %s (%d)",
                        strerror(-err), err);
            }
            //ATRACE_INT(mVsyncOnLabel.string(), 0);
        }
    }

/frameworks/native/services/surfaceflinger/DispSync.cpp

status_t DispSync::addEventListener(nsecs_t phase,
        const sp<Callback>& callback) {

    Mutex::Autolock lock(mMutex);
    return mThread->addEventListener(phase, callback);
}

  addEventListenerd第一个参数是EventThread使用的Vsync偏移,由VSYNC_EVENT_PHASE_OFFSET_NS,SF_VSYNC_EVENT_PHASE_OFFSET_NS指定;第二个参数是是使用的DispSyncSource,将这两个参数保存在一个EventListener 对象中,并将EventListener 的mLastEventTime 设为当前事件减去半个Vsync周期的值,然后加入到mEventListeners的Vector中,然后唤醒DispSyncThread。

/frameworks/native/services/surfaceflinger/DispSync.cpp

    status_t addEventListener(nsecs_t phase, const sp<DispSync::Callback>& callback) {
        Mutex::Autolock lock(mMutex);

        for (size_t i = 0; i < mEventListeners.size(); i++) {
            if (mEventListeners[i].mCallback == callback) {
                return BAD_VALUE;
            }
        }

        EventListener listener;
        listener.mPhase = phase;
        listener.mCallback = callback;

        // We want to allow the firstmost future event to fire without
        // allowing any past events to fire.  Because
        // computeListenerNextEventTimeLocked filters out events within a half
        // a period of the last event time, we need to initialize the last
        // event time to a half a period in the past.
        listener.mLastEventTime = systemTime(SYSTEM_TIME_MONOTONIC) - mPeriod / 2;

        mEventListeners.push(listener);

        mCond.signal();

        return NO_ERROR;
    }

  再看看DispSyncThread的threadLoop函数。第一次因为mPeriod == 0而导致的阻塞现在已经可以通行了。computeNextEventTimeLocked用来计算下一个 Vsync的时间戳。如果当前时间未到下一个 Vsync的时间戳,调用waitRelative等待相差的时间直到有人唤醒DispSyncThread。gatherCallbackInvocationsLocked收集所有符合要求的回调,在fireCallbackInvocations中执行这些回调。

/frameworks/native/services/surfaceflinger/DispSync.cpp

    virtual bool threadLoop() {
        status_t err;
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        nsecs_t nextEventTime = 0;

        while (true) {
            Vector<CallbackInvocation> callbackInvocations;

            nsecs_t targetTime = 0;

            { // Scope for lock
                Mutex::Autolock lock(mMutex);

                if (mStop) {
                    return false;
                }

                if (mPeriod == 0) {
                    err = mCond.wait(mMutex);
                    if (err != NO_ERROR) {
                        ALOGE("error waiting for new events: %s (%d)",
                                strerror(-err), err);
                        return false;
                    }
                    continue;
                }

                nextEventTime = computeNextEventTimeLocked(now);
                targetTime = nextEventTime;

                bool isWakeup = false;

                if (now < targetTime) {
                    err = mCond.waitRelative(mMutex, targetTime - now);

                    if (err == TIMED_OUT) {
                        isWakeup = true;
                    } else if (err != NO_ERROR) {
                        ALOGE("error waiting for next event: %s (%d)",
                                strerror(-err), err);
                        return false;
                    }
                }

                now = systemTime(SYSTEM_TIME_MONOTONIC);

                if (isWakeup) {
                    mWakeupLatency = ((mWakeupLatency * 63) +
                            (now - targetTime)) / 64;
                    if (mWakeupLatency > 500000) {
                        // Don't correct by more than 500 us
                        mWakeupLatency = 500000;
                    }
                    if (kTraceDetailedInfo) {
                        ATRACE_INT64("DispSync:WakeupLat", now - nextEventTime);
                        ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency);
                    }
                }

                callbackInvocations = gatherCallbackInvocationsLocked(now);
            }

            if (callbackInvocations.size() > 0) {
                fireCallbackInvocations(callbackInvocations);
            }
        }

        return false;
    }

/frameworks/native/services/surfaceflinger/DispSync.cpp

    Vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
        Vector<CallbackInvocation> callbackInvocations;
        nsecs_t ref = now - mPeriod;

        for (size_t i = 0; i < mEventListeners.size(); i++) {
            nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i],
                    ref);

            if (t < now) {
                CallbackInvocation ci;
                ci.mCallback = mEventListeners[i].mCallback;
                ci.mEventTime = t;
                callbackInvocations.push(ci);
                mEventListeners.editItemAt(i).mLastEventTime = t;
            }
        }

        return callbackInvocations;
    }

  回调实际上是调用了DispSyncSource::onDispSyncEvent。而DispSyncSource::onDispSyncEvent会调到EventThread::onVSyncEvent。

/frameworks/native/services/surfaceflinger/DispSync.cpp

    void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) {
        for (size_t i = 0; i < callbacks.size(); i++) {
            callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
        }
    }

/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

    virtual void onDispSyncEvent(nsecs_t when) {
        sp<VSyncSource::Callback> callback;
        {
            Mutex::Autolock lock(mMutex);
            callback = mCallback;

            if (mTraceVsync) {
                mValue = (mValue + 1) % 2;
                ATRACE_INT(mVsyncEventLabel.string(), mValue);
            }
        }

        if (callback != NULL) {
            callback->onVSyncEvent(when);
        }
    }

  EventThread::onVSyncEvent会填充mVSyncEvent[0],然后唤醒EventThread线程。

/frameworks/native/services/surfaceflinger/EventThread.cpp

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;
    mVSyncEvent[0].vsync.count++;
    mCondition.broadcast();
}

  在异步调用enableVSyncLocked后,EventThread会阻塞在waitRelative中,阻塞时间在关屏状态下为16ms,亮屏状态为1s。如果EventThread::onVSyncEvent在超时时间内没能唤醒EventThread,则手动填充mVSyncEvent[0],使得流程可以继续走下去。

        // note: !timestamp implies signalConnections.isEmpty(), because we
        // don't populate signalConnections if there's no vsync pending
        if (!timestamp && !eventPending) {
            // wait for something to happen
            if (waitForVSync) {
                // This is where we spend most of our time, waiting
                // for vsync events and new client registrations.
                //
                // If the screen is off, we can't use h/w vsync, so we
                // use a 16ms timeout instead.  It doesn't need to be
                // precise, we just need to keep feeding our clients.
                //
                // We don't want to stall if there's a driver bug, so we
                // use a (long) timeout when waiting for h/w vsync, and
                // generate fake events when necessary.
                bool softwareSync = mUseSoftwareVSync;
                nsecs_t timeout = softwareSync ? ms2ns(16) : ms2ns(1000);
                if (mCondition.waitRelative(mLock, timeout) == TIMED_OUT) {
                    if (!softwareSync) {
                        ALOGW("Timed out waiting for hw vsync; faking it");
                    }
                    // FIXME: how do we decide which display id the fake
                    // vsync came from ?
                    mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
                    mVSyncEvent[0].header.id = DisplayDevice::DISPLAY_PRIMARY;
                    mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
                    mVSyncEvent[0].vsync.count++;
                }
            } else {
                // Nobody is interested in vsync, so we just want to sleep.
                // h/w vsync should be disabled, so this will wait until we
                // get a new connection, or an existing connection becomes
                // interested in receiving vsync again.
                mCondition.wait(mLock);
            }

  再回到EventThread::waitForEvent开头,由于mVSyncEvent[0]的时间戳已经更新,所以timestamp的值不为0,然后将mVSyncEvent[0]的时间戳和Connection的count值分别重置为0和-1,将对应的Connection加入到signalConnections中。最终会退出waitForEvent中的do…while循环。
  离开waitForEvent后就是遍历signalConnections,然后分别调用其Connection::postEvent上报事件,以进行更新图层或画一帧的操作。这种基于Vsync模型的Vsync信号会一直发出来,直到没有Connection对Vsync感兴趣为止,这个从执行disableVSyncLocked的条件可以看出,当Vsync回调使得timestamp不为0时,这是却没有感兴趣的Connection(不需要刷新图层),则调用disableVSyncLocked移除回调,之后自然就没有可用的signalConnections了。
  可以看到,enableVSyncLocked/disableVSyncLocked并不会直接控制硬件Vsync的开关,而是基于EventThread的Vsync虚拟化进行模拟的Vsync输出,只有到模拟的Vsync和物理的Vsync有较大误差时,才会重新开启物理Vsync进行校正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章