Android에서 VSync 동작 원리 및 초기화 과정(3)

안드로이드/프레임워크 2015. 8. 31. 12:02

 지난 포스팅에 이어 VSync의 동작 과정을 이어서 살펴보도록 하겠습니다. 지난 포스팅에서 VSync의 동작 설정 여부를 살펴보았고 이어서 해당 동작이 현재 실행중인 Application 단계에 전달하는 과정을 이어서 살펴보겠습니다. 지난 포스팅에서 다루었던 SurfaceFlinger::onVSyncReceived() 함수부터 이어가도록 하겠습니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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);
    }
}
cs


/frameworks/native/services/surfaceflinger/SurfaceFlinger.h

1
2
3
4
5
6
....
    // these are thread safe
    mutable MessageQueue mEventQueue;
    FrameTracker mAnimFrameTracker;
    DispSync mPrimaryDispSync;
....
cs

 위의 함수에서 쓰인 변수 mPrimaryDispSync는 DispSync 클래스로서 해당 클래스의 함수 addResyncSample() 함수를 살펴보도록 하겠습니다.


/frameworks/native/services/surfaceflinger/DispSync.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// DispSync maintains a model of the periodic hardware-based vsync events of a
// display and uses that model to execute period callbacks at specific phase
// offsets from the hardware vsync events.  The model is constructed by
// feeding consecutive hardware event timestamps to the DispSync object via
// the addResyncSample method.
//
// The model is validated using timestamps from Fence objects that are passed
// to the DispSync object via the addPresentFence method.  These fence
// timestamps should correspond to a hardware vsync event, but they need not
// be consecutive hardware vsync times.  If this method determines that the
// current model accurately represents the hardware event times it will return
// false to indicate that a resynchronization (via addResyncSample) is not
// needed.
class DispSync {
 
public:
 
    class Callback: public virtual RefBase {
    public:
        virtual ~Callback() {};
        virtual void onDispSyncEvent(nsecs_t when) = 0;
    };
 
    DispSync();
    ~DispSync();
 
    void reset();
 
....
 
    // The beginResync, addResyncSample, and endResync methods are used to re-
    // synchronize the DispSync's model to the hardware vsync events.  The re-
    // synchronization process involves first calling beginResync, then
    // calling addResyncSample with a sequence of consecutive hardware vsync
    // event timestamps, and finally calling endResync when addResyncSample
    // indicates that no more samples are needed by returning false.
    //
    // This resynchronization process should be performed whenever the display
    // is turned on (i.e. once immediately after it's turned on) and whenever
    // addPresentFence returns true indicating that the model has drifted away
    // from the hardware vsync events.
    void beginResync();
    bool addResyncSample(nsecs_t timestamp);
    void endResync();
 
    // The setPreiod method sets the vsync event model's period to a specific
    // value.  This should be used to prime the model when a display is first
    // turned on.  It should NOT be used after that.
    void setPeriod(nsecs_t period);
 
....
 
};
cs


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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 (runningWithoutSyncFramework) {
        // 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 > errorThreshold;
}
cs

addResyncSample()함수는 mThread를 통해 hasAnyEventListeners() 함수를 호출합니다. 여기서 잠시 mThread에 대해 살펴보도록 하겠습니다.


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

1
2
    // mThread is the thread from which all the callbacks are called.
    sp<DispSyncThread> mThread;
cs


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class DispSyncThread: public Thread {
public:
 
    DispSyncThread():
            mStop(false),
            mPeriod(0),
            mPhase(0),
            mWakeupLatency(0) {
    }
 
    virtual ~DispSyncThread() {}
 
    void updateModel(nsecs_t period, nsecs_t phase) {
        Mutex::Autolock lock(mMutex);
        mPeriod = period;
        mPhase = phase;
        mCond.signal();
    }
 
....
 
    // This method is only here to handle the runningWithoutSyncFramework
    // case.
    bool hasAnyEventListeners() {
        Mutex::Autolock lock(mMutex);
        return !mEventListeners.empty();
    }
 
private:
 
    struct EventListener {
        nsecs_t mPhase;
        nsecs_t mLastEventTime;
        sp<DispSync::Callback> mCallback;
    };
 
    struct CallbackInvocation {
        sp<DispSync::Callback> mCallback;
        nsecs_t mEventTime;
    };
 
....
 
    Vector<EventListener> mEventListeners;
 
    Mutex mMutex;
    Condition mCond;
};
cs

 이제 DispSync의 Thread에 대해 살펴보도록 하겠습니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
DispSync::DispSync() {
    mThread = new DispSyncThread();
    mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
 
    reset();
    beginResync();
 
    if (traceDetailedInfo) {
        // If runningWithoutSyncFramework is true then the ZeroPhaseTracer
        // would prevent HW vsync event from ever being turned off.
        // Furthermore the zero-phase tracing is not needed because any time
        // there is an event registered we will turn on the HW vsync events.
        if (!runningWithoutSyncFramework) {
            addEventListener(0new ZeroPhaseTracer());
        }
    }
}
 
DispSync::~DispSync() {}
 
void DispSync::reset() {
    Mutex::Autolock lock(mMutex);
 
    mNumResyncSamples = 0;
    mFirstResyncSample = 0;
    mNumResyncSamplesSincePresent = 0;
    resetErrorLocked();
}
 
....
 
void DispSync::beginResync() {
    Mutex::Autolock lock(mMutex);
    mNumResyncSamples = 0;
}
cs


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

1
2
3
4
5
6
status_t DispSync::addEventListener(nsecs_t phase,
        const sp<Callback>& callback) {
 
    Mutex::Autolock lock(mMutex);
    return mThread->addEventListener(phase, callback);
}
cs


 소스코드 상으로 보았을 때 addEventListener()를 통해 ZeroPhaseTracer가 EventListener로 등록되고 있음을 확인하실 수 있습니다. 여기서 ZeroPhaseTracer에 대해  알아보겠습니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
class ZeroPhaseTracer : public DispSync::Callback {
public:
    ZeroPhaseTracer() : mParity(false) {}
 
    virtual void onDispSyncEvent(nsecs_t when) {
        mParity = !mParity;
        ATRACE_INT("ZERO_PHASE_VSYNC", mParity ? 1 : 0);
    }
 
private:
    bool mParity;
};
cs


 이제 addEventListener() 함수를 살펴보도록 합시다.

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    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;
    }
cs

 위의 과정을 통해 EventListener가 등록되었고 push() 함수를 통해 등록한 후 signal()함수로 thread를 깨웁니다. 이제 thread의 threadLoop()함수를 살펴보도록 하겠습니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
    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 (traceDetailedInfo) {
                        ATRACE_INT64("DispSync:WakeupLat", now - nextEventTime);
                        ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency);
                    }
                }
 
                callbackInvocations = gatherCallbackInvocationsLocked(now);
            }
 
            if (callbackInvocations.size() > 0) {
                fireCallbackInvocations(callbackInvocations);
            }
        }
 
        return false;
    }
cs



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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    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;
    }
 
....
 
    void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) {
        for (size_t i = 0; i < callbacks.size(); i++) {
            callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
        }
    }
cs


300x250