검색결과 리스트
글
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,
수직동기화의 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 클래스]
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 |
'안드로이드 > 프레임워크' 카테고리의 다른 글
SurfaceFlinger에서 DispSyncSource의 초기화 과정 (0) | 2015.09.12 |
---|---|
SurfaceFlinger의 초기화 과정 흐름도 (0) | 2015.09.09 |
안드로이드 프레임워크 프로그래밍(24) [BitTube 클래스] (2) | 2015.09.06 |
안드로이드 프레임워크 프로그래밍(23) [onFirstRef() 함수 호출시기] (0) | 2015.09.03 |
Android에서 VSync 동작 원리 및 초기화 과정(4) (5) | 2015.09.02 |