Analysis of Android GUI's SurfaceFlinger (06) server 1 - handling VSYNC

Keywords: Android Mobile

Link to the general outline of this series of articles: surface flinger series of articles of Android GUI system

Summary & description of key points in this chapter:

The mind map of this chapter is as above. This paper mainly describes the process of surface flinger to deal with Vsync mechanism. Analyze to the section dealing with messages.

1 SurfaceFlinger uses VSync process framework

When the APP submits the generated interface to Buffer, it will call the operation of queueBuffer, and finally notify the SF composition interface. When SF needs to synthesize interface, it sends request Vsync signal to EventThread(SF),EventThread(SF) to DispSyncThread thread. After DispSyncThread receives the request, it delays offset2, sends Vsync SF to EventThread(SF) and wakes up. EventThread(SF) wakes up SF after receiving Vsync SF, and SF starts to synthesize new interface. The overall framework is as follows:

2. Source code analysis of surface flinger using VSync process

2.1 analysis mode

Based on the above framework, the process is analyzed in detail here. When the application has constructed the data, inform the surface flinger. This part has been shared in the previous chapters, so the code here starts from step 2 of the framework diagram.

2.2 SF requests VSync from eventthread SF

This is actually to continue the analysis in Chapter 4 (surface flinger (04) application end analysis of Android GUI system - submit Buffer). Mfringer - > signallayeraupdate(), the code is as follows:

void SurfaceFlinger::signalLayerUpdate() {
    mEventQueue.invalidate();
}

Here is to wake up another thread, eventthread SF, and continue to analyze mEventQueue.invalidate. The code implementation is as follows:

void MessageQueue::invalidate() {
#if INVALIDATE_ON_VSYNC
    mEvents->requestNextVsync();
#else
    mHandler->dispatchInvalidate();
#endif
}

mEvents is of EventThread::Connection type. The corresponding code is as follows:

void EventThread::Connection::requestNextVsync() {
    mEventThread->requestNextVsync(this);
}

Here, we continue to analyze the requestNextVsync method of EventThread. The code implementation is as follows:

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

Here, the condition variable wakes up the current thread EventThread, and the wait in another place is waked up. Here is the mCondition.wait(mLock) in the EventThread::waitForEvent method, and it will continue to execute after wakeup.

2.3 eventthread SF requests VSync from DispSyncThread

First, it analyzes the start of EventThread, that is, the implementation of threadLoop. The code is as follows:

bool EventThread::threadLoop() {
    DisplayEventReceiver::Event event;
    Vector< sp<EventThread::Connection> > signalConnections;
    //Key 1: wait for events
    signalConnections = waitForEvent(&event);
    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
        //Key 2:post event
        status_t err = conn->postEvent(event);
        if (err == -EAGAIN || err == -EWOULDBLOCK) {
            //...
        } else if (err < 0) {
            //...
            removeDisplayEventConnection(signalConnections[i]);
        }
    }
    return true;
}

The code of waitForEvent is as follows:

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) {//Indicates that VSync is needed
                    waitForVSync = true;
                    if (timestamp) {
                        if (connection->count == 0) {
                            connection->count = -1;
                            signalConnections.add(connection);
                            added = true;
                        } else if (connection->count == 1 ||
                                (vsyncCount % connection->count) == 0) {
                            signalConnections.add(connection);
                            added = true;
                        }
                    }
                }

                if (eventPending && !timestamp && !added) {
                    signalConnections.add(connection);
                }
            } else {
                mDisplayEventConnections.removeAt(i);
                --i; --count;
            }
        }

        // Here we figure out if we need to enable or disable vsyncs
        if (timestamp && !waitForVSync) {
            disableVSyncLocked();
        } else if (!timestamp && waitForVSync) {
            //Call this method if Vsync is required
            enableVSyncLocked();
        }

        if (!timestamp && !eventPending) {
            // wait for something to happen
            if (waitForVSync) {
                bool softwareSync = mUseSoftwareVSync;
                nsecs_t timeout = softwareSync ? ms2ns(16) : ms2ns(1000);
                if (mCondition.waitRelative(mLock, timeout) == TIMED_OUT) {
                    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 {
                //Sleep, wait for the next VSync
                mCondition.wait(mLock);
            }
        }
    } while (signalConnections.isEmpty());
    return signalConnections;
}

Once VSync is required, the key method enableVSyncLocked will be called. The code implementation is as follows:

void EventThread::enableVSyncLocked() {
    if (!mUseSoftwareVSync) {
        // never enable h/w VSYNC when screen is off
        if (!mVsyncEnabled) {
            mVsyncEnabled = true;
            //Here mVSyncSource is of type DispSyncSource
            //In fact, it is to set Callback for DispSyncThread thread
            mVSyncSource->setCallback(static_cast<VSyncSource::Callback*>(this));
            mVSyncSource->setVSyncEnabled(true);
        }
    }
    mDebugVsyncEnabled = true;
    sendVsyncHintOnLocked();
}

Continue to analyze. mVSyncSource is an object of DispSyncSource class, so the code implementation of setCallback is as follows:

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

The callback is set here, and then DispSyncThread can call the callback method when receiving VSync signal.

2.4 wake up DispSyncThread

Here we analyze the vsyncthread of HWC (in fact, the final result of software and hardware implementation is the same), that is, the VSync of software implementation. The thread code of software vsyncthread is as follows:

bool HWComposer::VSyncThread::threadLoop() {
    { // scope for lock
        Mutex::Autolock _l(mLock);
        while (!mEnabled) {
            mCondition.wait(mLock);
        }
    }
    const nsecs_t period = mRefreshPeriod;
    const nsecs_t now = systemTime(CLOCK_MONOTONIC);
    nsecs_t next_vsync = mNextFakeVSync;
    nsecs_t sleep = next_vsync - now;
    if (sleep < 0) {
        // we missed, find where the next vsync should be
        sleep = (period - ((now - next_vsync) % period));
        next_vsync = now + sleep;
    }
    mNextFakeVSync = next_vsync + period;
    struct timespec spec;
    spec.tv_sec  = next_vsync / 1000000000;
    spec.tv_nsec = next_vsync % 1000000000;

    int err;
    do {
        err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
    } while (err<0 && errno == EINTR);

    if (err == 0) {
        mHwc.mEventHandler.onVSyncReceived(0, next_vsync);
    }
    return true;
}

After a period of operation, the mHwc.mEventHandler.onVSyncReceived method is called. The value of mHwc.mEventHandler is actually surfaceflinger, which is done in the init function of surfaceflinger. The code is as follows:

mHwc = new HWComposer(this,
            *static_cast<HWComposer::EventHandler *>(this));

Therefore, we will continue to analyze the implementation of surfcefringer's onVSyncReceived method. The code is as follows:

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

Here, we continue to analyze the implementation of addResyncSample of DispSync. The code is as follows:

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) {
        return mThread->hasAnyEventListeners();
    }

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

Here, we continue to analyze the implementation of updatemodelocked of DispSync. The code is as follows:

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

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

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

Continue to analyze the implementation of mthread - > updatemodel. The code is as follows:

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

After a series of analysis, surfcefinger's onVSyncReceived method will eventually wake up the DispSyncThread thread.

2.5 DispSyncThread wakes EventThread

If there is mCond.signal(), there is the corresponding mCond.wait(). The method is in threadloop in the thread execution body. The code of threadloop is as follows:

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);
                //...
                continue;
            }
            /*Calculates the time of the most recent EventThread.
              Eventthread When connection > = 0 is found,
              Register with thread DispSyncThread as callback and listener
            */
            nextEventTime = computeNextEventTimeLocked(now);
            targetTime = nextEventTime;

            bool isWakeup = false;

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

                if (err == TIMED_OUT) {
                    isWakeup = true;
                } else if (err != NO_ERROR) {
                    //...
                }
            }

            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;
                }
            }
            //All the callback s of mobile phones
            callbackInvocations = gatherCallbackInvocationsLocked(now);
        }

        if (callbackInvocations.size() > 0) {
            //Call callback
            fireCallbackInvocations(callbackInvocations);
        }
    }
    return false;
}

Here, the call of fireCallbackInvocations actually calls the onVSyncEvent of eventthread SF. The code is as follows:

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

At this point, eventthread SF is awakened by DispSyncThread.

2.6 eventthread SF wakes up SurfaceFlinger

After being woken up by the DispSyncThread thread, the corresponding mCondition.wait will be executed. The relevant code is in waitforEvent, as follows:

bool EventThread::threadLoop() {
    DisplayEventReceiver::Event event;
    Vector< sp<EventThread::Connection> > signalConnections;
    //Key 1: wait for events
    signalConnections = waitForEvent(&event);
    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
        //Key 2:post event
        status_t err = conn->postEvent(event);
        if (err == -EAGAIN || err == -EWOULDBLOCK) {
            //...
        } else if (err < 0) {
            //...
            removeDisplayEventConnection(signalConnections[i]);
        }
    }
    return true;
}

At this time, after waitforEvent is finished, it will enter this loop, and call the postEvent method of connection to continue the analysis. The code implementation is as follows:

status_t EventThread::Connection::postEvent(
        const DisplayEventReceiver::Event& event) {
    ssize_t size = DisplayEventReceiver::sendEvents(mChannel, &event, 1);
    return size < 0 ? status_t(size) : status_t(NO_ERROR);
}

 

There must be a receiver after sending the message. However, it is not easy to analyze the receiver directly. We need to find it from SF. In the init code of SF, pay attention to this Code:

void SurfaceFlinger::init()
{
    //...
    sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
            vsyncPhaseOffsetNs, true, "app");
    mEventThread = new EventThread(vsyncSrc);
    sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
            sfVsyncPhaseOffsetNs, true, "sf");
    mSFEventThread = new EventThread(sfVsyncSrc);
    mEventQueue.setEventThread(mSFEventThread);//Key points
    //...
}

Specifically analyze the implementation of mEventQueue.setEventThread(mSFEventThread). The code is as follows:

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

This is mainly to monitor whether there is data in EventThread, and focus on the implementation of messagequeue:: CB - eventreceiver. The code is as follows:

int MessageQueue::cb_eventReceiver(int fd, int events, void* data) {
    MessageQueue* queue = reinterpret_cast<MessageQueue *>(data);
    return queue->eventReceiver(fd, events);
}

Here, we will continue to analyze queue - > eventreceiver. The code implementation is as follows:

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

Here, DisplayEventReceiver::getEvents corresponds to the previous sendEvent operation. That is, SF listens for events all the time after init is executed. If there is any, call

Mhandler - > dispatchinvalidate() method.

2.7 processing

void MessageQueue::Handler::dispatchInvalidate() {
    if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) {
        mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
    }
}

The handler Message mechanism is used here, so you can directly see the handling of handleMessage as follows:

void MessageQueue::Handler::handleMessage(const Message& message) {
    switch (message.what) {
        case INVALIDATE:
            android_atomic_and(~eventMaskInvalidate, &mEventMask);
            mQueue.mFlinger->onMessageReceived(message.what);
            break;
        case REFRESH:
            android_atomic_and(~eventMaskRefresh, &mEventMask);
            mQueue.mFlinger->onMessageReceived(message.what);
            break;
        case TRANSACTION:
            android_atomic_and(~eventMaskTransaction, &mEventMask);
            mQueue.mFlinger->onMessageReceived(message.what);
            break;
    }
}

The invalid here corresponds to the execution of the mque.mfringer - > onMessageReceived (message. What) method. The onMessageReceived code is implemented as follows:

void SurfaceFlinger::onMessageReceived(int32_t what) {
    switch (what) {
        case MessageQueue::TRANSACTION: {
            handleMessageTransaction();
            break;
        }
        case MessageQueue::INVALIDATE: {
            //Key sentences
            bool refreshNeeded = handleMessageTransaction();//1 
            refreshNeeded |= handleMessageInvalidate();//2 
            refreshNeeded |= mRepaintEverything;
            if (refreshNeeded) {
                signalRefresh();//3
            }
            break;
        }
        case MessageQueue::REFRESH: {
            handleMessageRefresh();
            break;
        }
    }
}

For the invalid message, the key is to execute several sentences identified in the code. In Section 7, we analyze this part in detail.

 

 

224 original articles published, 30 praised, 20000 visitors+
Private letter follow

Posted by Floodboy on Fri, 13 Mar 2020 22:04:42 -0700