SurfaceFlinger에서 EventThread의 초기화 과정

 SurfaceFlinger가 수직동기화(VSync)를 수행하기 위해 이를 담당하는 별도의 Thread를 만들어 줄 필요가 있습니다. EventThread 클래스가 바로 그러한 역할을 담당하고 있다고 보면 되겠습니다. 이번 포스팅에서 EventThread의 초기화 과정을 살펴보도록 하겠습니다.


/frameworks/native/services/surfaceflinger/EventThread.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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
class VSyncSource : public virtual RefBase {
public:
    class Callback: public virtual RefBase {
    public:
        virtual ~Callback() {}
        virtual void onVSyncEvent(nsecs_t when) = 0;
    };
 
    virtual ~VSyncSource() {}
    virtual void setVSyncEnabled(bool enable) = 0;
    virtual void setCallback(const sp<Callback>& callback) = 0;
};
 
class EventThread : public Thread, private VSyncSource::Callback {
    class Connection : public BnDisplayEventConnection {
    public:
        Connection(const sp<EventThread>& eventThread);
        status_t postEvent(const DisplayEventReceiver::Event& event);
 
        // count >= 1 : continuous event. count is the vsync rate
        // count == 0 : one-shot event that has not fired
        // count ==-1 : one-shot event that fired this round / disabled
        int32_t count;
 
    private:
        virtual ~Connection();
        virtual void onFirstRef();
        virtual sp<BitTube> getDataChannel() const;
        virtual void setVsyncRate(uint32_t count);
        virtual void requestNextVsync();    // asynchronous
        sp<EventThread> const mEventThread;
        sp<BitTube> const mChannel;
    };
 
public:
 
    EventThread(const sp<VSyncSource>& src);
 
    sp<Connection> createEventConnection() const;
    status_t registerDisplayEventConnection(const sp<Connection>& connection);
 
    void setVsyncRate(uint32_t count, const sp<Connection>& connection);
    void requestNextVsync(const sp<Connection>& connection);
 
    // called before the screen is turned off from main thread
    void onScreenReleased();
 
    // called after the screen is turned on from main thread
    void onScreenAcquired();
 
    // called when receiving a hotplug event
    void onHotplugReceived(int type, bool connected);
 
    Vector< sp<EventThread::Connection> > waitForEvent(
            DisplayEventReceiver::Event* event);
 
    void dump(String8& result) const;
 
private:
    virtual bool        threadLoop();
    virtual void        onFirstRef();
 
    virtual void onVSyncEvent(nsecs_t timestamp);
 
    void removeDisplayEventConnection(const wp<Connection>& connection);
    void enableVSyncLocked();
    void disableVSyncLocked();
 
    // constants
    sp<VSyncSource> mVSyncSource;
    PowerHAL mPowerHAL;
 
    mutable Mutex mLock;
    mutable Condition mCondition;
 
    // protected by mLock
    SortedVector< wp<Connection> > mDisplayEventConnections;
    Vector< DisplayEventReceiver::Event > mPendingEvents;
    DisplayEventReceiver::Event mVSyncEvent[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES];
    bool mUseSoftwareVSync;
    bool mVsyncEnabled;
 
    // for debugging
    bool mDebugVsyncEnabled;
};
cs


/frameworks/native/include/gui/DisplayEventReceiver.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
class DisplayEventReceiver {
public:
    enum {
        DISPLAY_EVENT_VSYNC = 'vsyn',
        DISPLAY_EVENT_HOTPLUG = 'plug'
    };
 
    struct Event {
 
        struct Header {
            uint32_t type;
            uint32_t id;
            nsecs_t timestamp;
        };
 
        struct VSync {
            uint32_t count;
        };
 
        struct Hotplug {
            bool connected;
        };
 
        Header header;
        union {
            VSync vsync;
            Hotplug hotplug;
        };
    };
 
....
 
}
cs


 전체적인 맥락을 파악하기 위해 EventThread의 전체 헤더 소스코드를 보았습니다. 이번에는 EventThread가 초기화 되는 과정을 살펴보도록 하겠습니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void SurfaceFlinger::init() {
 
....
 
    // start the EventThread
    sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
            vsyncPhaseOffsetNs, true);
    mEventThread = new EventThread(vsyncSrc);
    sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
            sfVsyncPhaseOffsetNs, false);
    mSFEventThread = new EventThread(sfVsyncSrc);
    mEventQueue.setEventThread(mSFEventThread);
 
    mEventControlThread = new EventControlThread(this);
    mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY);
 
....
 
}
cs

 SurfaceFlinger에서 EventThread가 초기화 되는 부분입니다. 여기서 mEventThread 클래스 변수가 초기화 되는 과정을 살펴보도록 하겠습니다.


sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,

            vsyncPhaseOffsetNs, true);


 수직동기화의 Source를 설정하는 부분입니다. 해당 부분은 다음과 같이 구성되어 있습니다.


/frameworks/native/services/surfaceflinger/SurfaceFlinger.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
class DispSyncSource : public VSyncSource, private DispSync::Callback {
public:
    DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync) :
            mValue(0),
            mPhaseOffset(phaseOffset),
            mTraceVsync(traceVsync),
            mDispSync(dispSync) {}
 
    virtual ~DispSyncSource() {}
 
....
 
private:
 
....
 
    int mValue;
 
    const nsecs_t mPhaseOffset;
    const bool mTraceVsync;
 
    DispSync* mDispSync;
    sp<VSyncSource::Callback> mCallback;
    Mutex mMutex;
};
cs


mEventThread = new EventThread(vsyncSrc);

 이전 줄에서 설정하였던 수직동기화 정보를 기반으로 EventThread 생성자를 실행합니다.


/frameworks/native/services/surfaceflinger/EventThread.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
EventThread::EventThread(const sp<VSyncSource>& src)
    : mVSyncSource(src),
      mUseSoftwareVSync(false),
      mVsyncEnabled(false),
      mDebugVsyncEnabled(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;
    }
}
 
void EventThread::onFirstRef() {
    run("EventThread", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
}
 
....
 
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;
}
cs


 위의 소스코드에서 waitForEvent() 소스코드를 통해 EventThread의 연결과정이 드러나고 있습니다. 해당 부분을 살펴보면 다음과 같습니다.


/frameworks/native/services/surfaceflinger/EventThread.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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// 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;
}
cs

 위의 waitForEvent() 함수의 실행이 종료되면 이후에 Connection 클래스가 생성됩니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
EventThread::Connection::Connection(
        const sp<EventThread>& eventThread)
    : count(-1), mEventThread(eventThread), mChannel(new BitTube())
{
}
 
EventThread::Connection::~Connection() {
    // do nothing here -- clean-up will happen automatically
    // when the main thread wakes up
}
 
void EventThread::Connection::onFirstRef() {
    // NOTE: mEventThread doesn't hold a strong reference on us
    mEventThread->registerDisplayEventConnection(this);
}
cs

 위의 Connection 클래스가 생성되면서 mChannel을 통해 File Descriptor을 통해 프로세스간 통신을 수행하는 BitTube 클래스가 새로 생성됩니다. BitTube에 대한 자세한 내용은 이전의 포스팅을 참조해주시기 바랍니다.


안드로이드 프레임워크 프로그래밍(24) [BitTube 클래스]

http://elecs.tistory.com/138


 Connection 클래스의 onFirstRef()함수에 의해 EventThread 클래스의 registerDisplayEventConnection()함수가 호출됩니다.


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

1
2
3
4
5
6
7
8
status_t EventThread::registerDisplayEventConnection(
        const sp<EventThread::Connection>& connection) {
    Mutex::Autolock _l(mLock);
    mDisplayEventConnections.add(connection);
    mCondition.broadcast();
    return NO_ERROR;
}
 
cs

 EventThread에 EventThread의 Connection 클래스를 벡터에 등록한 후 멈추어 있던 Thread들을 모두 깨웁니다. 위의 과정을 통해 생성된 Connection 클래스는 이후 postEvent() 함수를 수행하게 됩니다.


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

1
2
3
4
5
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);
}
cs


/frameworks/native/libs/gui/DisplayEventReceiver.cpp

1
2
3
4
5
ssize_t DisplayEventReceiver::sendEvents(const sp<BitTube>& dataChannel,
        Event const* events, size_t count)
{
    return BitTube::sendObjects(dataChannel, events, count);
}
cs


300x250