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

 이번 포스팅에서는 Java Framework 단계에서 초기화 되는 과정에서 VSync 관련 동작이 Native 단계와 연결되는 과정을 살펴보도록 하겠습니다.


 Java 단계에서 안드로이드 기반 디바이스의 화면을 관리하는 클래스로 Choreographer가 있습니다. 이 클래스는 화면의 변화나 입력등을 통해 그려지는 타이밍을 관리합니다. Choreographer는 디스플레이의 부속시스템으로부터 전달되는 VSync(Vertical Synchronization)와 같은 신호를 수신하고, 다음 디스플레이 프레임을 렌더링 하기 위해 작업을 스케줄링 합니다.


 그렇다면 이제 Choreographer가 초기화 되는 과정을 살펴보도록 하겠습니다.


/frameworks/base/core/java/android/view/Choreographer.java

1
2
3
4
5
6
7
8
9
10
11
12
    // Thread local storage for the choreographer.
    private static final ThreadLocal<Choreographer> sThreadInstance =
            new ThreadLocal<Choreographer>() {
        @Override
        protected Choreographer initialValue() {
            Looper looper = Looper.myLooper();
            if (looper == null) {
                throw new IllegalStateException("The current thread must have a looper!");
            }
            return new Choreographer(looper);
        }
    };
cs

 ThreadLocal을 통하여 Choreographer 값을 thread에 저장합니다. static으로 선언되어 있으므로 sThreadInstance의 값이 초기화 되면서 return 값인 new Choreographer()가 실행됩니다.


/frameworks/base/core/java/android/view/Choreographer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    // Enable/disable vsync for animations and drawing.
    private static final boolean USE_VSYNC = SystemProperties.getBoolean(
            "debug.choreographer.vsync"true);
 
....
 
    private Choreographer(Looper looper) {
        mLooper = looper;
        mHandler = new FrameHandler(looper);
        mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
        mLastFrameTimeNanos = Long.MIN_VALUE;
 
        mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
 
        mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
        for (int i = 0; i <= CALLBACK_LAST; i++) {
            mCallbackQueues[i] = new CallbackQueue();
        }
    }
cs

 Choreographer의 Constructor가 생성되고 있습니다. VSync를 사용하고 있으므로 FrameDisplayEventReceiver()의 Constructor가 새로 생성됨을 확인하실 수 있습니다.


/frameworks/base/core/java/android/view/Choreographer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
    private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;
 
        public FrameDisplayEventReceiver(Looper looper) {
            super(looper);
        }
 
....
 
    }
cs


/frameworks/base/core/java/android/view/DisplayEventReceiver.java

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
/**
 * Provides a low-level mechanism for an application to receive display events
 * such as vertical sync.
 *
 * The display event receive is NOT thread safe.  Moreover, its methods must only
 * be called on the Looper thread to which it is attached.
 *
 * @hide
 */
public abstract class DisplayEventReceiver {
    private static final String TAG = "DisplayEventReceiver";
 
    private final CloseGuard mCloseGuard = CloseGuard.get();
 
    private int mReceiverPtr;
 
    // We keep a reference message queue object here so that it is not
    // GC'd while the native peer of the receiver is using them.
    private MessageQueue mMessageQueue;
 
    private static native int nativeInit(DisplayEventReceiver receiver,
            MessageQueue messageQueue);
    private static native void nativeDispose(int receiverPtr);
    private static native void nativeScheduleVsync(int receiverPtr);
 
    /**
     * Creates a display event receiver.
     *
     * @param looper The looper to use when invoking callbacks.
     */
    public DisplayEventReceiver(Looper looper) {
        if (looper == null) {
            throw new IllegalArgumentException("looper must not be null");
        }
 
        mMessageQueue = looper.getQueue();
        mReceiverPtr = nativeInit(this, mMessageQueue);
 
        mCloseGuard.open("dispose");
    }
 
....
 
}
cs

 위 소스코드를 통해 DisplayEventReceiver 클래스에서 nativeInit를 통해 JNI로 연결되는 것을 보실 수 있습니다.


/frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
        jobject messageQueueObj) {
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }
 
    sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
            receiverObj, messageQueue);
    status_t status = receiver->initialize();
    if (status) {
        String8 message;
        message.appendFormat("Failed to initialize display event receiver.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
        return 0;
    }
 
    receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object
    return reinterpret_cast<jint>(receiver.get());
}
cs

 NativeDisplayEventReceiver클래스의 Constructor가 생성된 후 initialize() 함수가 호출되고 있는 모습입니다.


/frameworks/base/core/jni/android_view_DisplayEventReceiver.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
class NativeDisplayEventReceiver : public LooperCallback {
public:
    NativeDisplayEventReceiver(JNIEnv* env,
            jobject receiverObj, const sp<MessageQueue>& messageQueue);
 
    status_t initialize();
    void dispose();
    status_t scheduleVsync();
 
protected:
    virtual ~NativeDisplayEventReceiver();
 
private:
    jobject mReceiverObjGlobal;
    sp<MessageQueue> mMessageQueue;
    DisplayEventReceiver mReceiver;
    bool mWaitingForVsync;
 
    virtual int handleEvent(int receiveFd, int events, void* data);
    bool processPendingEvents(nsecs_t* outTimestamp, int32_t* id, uint32_t* outCount);
    void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count);
    void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected);
};
 
 
NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env,
        jobject receiverObj, const sp<MessageQueue>& messageQueue) :
        mReceiverObjGlobal(env->NewGlobalRef(receiverObj)),
        mMessageQueue(messageQueue), mWaitingForVsync(false) {
    ALOGV("receiver %p ~ Initializing input event receiver."this);
}
 
NativeDisplayEventReceiver::~NativeDisplayEventReceiver() {
    JNIEnv* env = AndroidRuntime::getJNIEnv();
    env->DeleteGlobalRef(mReceiverObjGlobal);
}
 
status_t NativeDisplayEventReceiver::initialize() {
    status_t result = mReceiver.initCheck();
    if (result) {
        ALOGW("Failed to initialize display event receiver, status=%d", result);
        return result;
    }
 
    int rc = mMessageQueue->getLooper()->addFd(mReceiver.getFd(), 0, ALOOPER_EVENT_INPUT,
            this, NULL);
    if (rc < 0) {
        return UNKNOWN_ERROR;
    }
    return OK;
}
cs


 위의 과정을 보았을 때 MessageQueue 내에 들어있는 Looper를 얻어낸 후 addFd() 함수를 통해 DisplayEventReceiver의 File Description이 등록됩니다. NativeDisplayEventReceiver 내에 있는 mReceiver는 DisplayEventReceiver의 클래스 변스로 해당 클래스의 소스코드를 살펴보도록 하겠습니다.


/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
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
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;
        };
    };
 
public:
    /*
     * DisplayEventReceiver creates and registers an event connection with
     * SurfaceFlinger. VSync events are disabled by default. Call setVSyncRate
     * or requestNextVsync to receive them.
     * Other events start being delivered immediately.
     */
    DisplayEventReceiver();
 
    /*
     * ~DisplayEventReceiver severs the connection with SurfaceFlinger, new events
     * stop being delivered immediately. Note that the queue could have
     * some events pending. These will be delivered.
     */
    ~DisplayEventReceiver();
 
    /*
     * initCheck returns the state of DisplayEventReceiver after construction.
     */
    status_t initCheck() const;
 
    /*
     * getFd returns the file descriptor to use to receive events.
     * OWNERSHIP IS RETAINED by DisplayEventReceiver. DO NOT CLOSE this
     * file-descriptor.
     */
    int getFd() const;
 
    /*
     * getEvents reads events from the queue and returns how many events were
     * read. Returns 0 if there are no more events or a negative error code.
     * If NOT_ENOUGH_DATA is returned, the object has become invalid forever, it
     * should be destroyed and getEvents() shouldn't be called again.
     */
    ssize_t getEvents(Event* events, size_t count);
    static ssize_t getEvents(const sp<BitTube>& dataChannel,
            Event* events, size_t count);
 
    /*
     * sendEvents write events to the queue and returns how many events were
     * written.
     */
    static ssize_t sendEvents(const sp<BitTube>& dataChannel,
            Event const* events, size_t count);
 
    /*
     * setVsyncRate() sets the Event::VSync delivery rate. A value of
     * 1 returns every Event::VSync. A value of 2 returns every other event,
     * etc... a value of 0 returns no event unless  requestNextVsync() has
     * been called.
     */
    status_t setVsyncRate(uint32_t count);
 
    /*
     * requestNextVsync() schedules the next Event::VSync. It has no effect
     * if the vsync rate is > 0.
     */
    status_t requestNextVsync();
 
private:
    sp<IDisplayEventConnection> mEventConnection;
    sp<BitTube> mDataChannel;
};
cs


/frameworks/native/libs/gui/DisplayEventReceiver.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
DisplayEventReceiver::DisplayEventReceiver() {
    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
    if (sf != NULL) {
        mEventConnection = sf->createDisplayEventConnection();
        if (mEventConnection != NULL) {
            mDataChannel = mEventConnection->getDataChannel();
        }
    }
}
 
DisplayEventReceiver::~DisplayEventReceiver() {
}
 
status_t DisplayEventReceiver::initCheck() const {
    if (mDataChannel != NULL)
        return NO_ERROR;
    return NO_INIT;
}
 
int DisplayEventReceiver::getFd() const {
    if (mDataChannel == NULL)
        return NO_INIT;
 
    return mDataChannel->getFd();
}
cs


 DisplayEventReceiver의 소스코드를 하나씩 살펴보도록 하겠습니다.


sp<ISurfaceComposer> sf(ComposerService::getComposerService());

 SurfaceFlinger로부터 ComposerService의 Binder Proxy를 얻어옵니다.


/frameworks/native/libs/gui/SurfaceComposerClient.cpp

1
2
3
4
5
6
7
8
9
10
/*static*/ sp<ISurfaceComposer> ComposerService::getComposerService() {
    ComposerService& instance = ComposerService::getInstance();
    Mutex::Autolock _l(instance.mLock);
    if (instance.mComposerService == NULL) {
        ComposerService::getInstance().connectLocked();
        assert(instance.mComposerService != NULL);
        ALOGD("ComposerService reconnected");
    }
    return instance.mComposerService;
}
cs


/frameworks/native/include/private/gui/ComposerService.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// This holds our connection to the composer service (i.e. SurfaceFlinger).
// If the remote side goes away, we will re-establish the connection.
// Users of this class should not retain the value from
// getComposerService() for an extended period.
//
// (It's not clear that using Singleton is useful here anymore.)
class ComposerService : public Singleton<ComposerService>
{
    sp<ISurfaceComposer> mComposerService;
    sp<IBinder::DeathRecipient> mDeathObserver;
    Mutex mLock;
 
    ComposerService();
    void connectLocked();
    void composerServiceDied();
    friend class Singleton<ComposerService>;
public:
 
    // Get a connection to the Composer Service.  This will block until
    // a connection is established.
    static sp<ISurfaceComposer> getComposerService();
};
cs

mEventConnection = sf->createDisplayEventConnection();

 SurfaceFlinger로 부터 디스플레이와 관련된 이벤트를 수신할 수 있는 EventConnection 클래스를 받습니다.


/frameworks/native/libs/gui/ISurfaceComposer.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
class BpSurfaceComposer : public BpInterface<ISurfaceComposer>
{
public:
    BpSurfaceComposer(const sp<IBinder>& impl)
        : BpInterface<ISurfaceComposer>(impl)
    {
    }
 
....
 
    virtual sp<IDisplayEventConnection> createDisplayEventConnection()
    {
        Parcel data, reply;
        sp<IDisplayEventConnection> result;
        int err = data.writeInterfaceToken(
                ISurfaceComposer::getInterfaceDescriptor());
        if (err != NO_ERROR) {
            return result;
        }
        err = remote()->transact(
                BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION,
                data, &reply);
        if (err != NO_ERROR) {
            ALOGE("ISurfaceComposer::createDisplayEventConnection: error performing "
                    "transaction: %s (%d)", strerror(-err), -err);
            return result;
        }
        result = interface_cast<IDisplayEventConnection>(reply.readStrongBinder());
        return result;
    }
 
....
 
}
 
status_t BnSurfaceComposer::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code) {
 
....
 
        case CREATE_DISPLAY_EVENT_CONNECTION: {
            CHECK_INTERFACE(ISurfaceComposer, data, reply);
            sp<IDisplayEventConnection> connection(createDisplayEventConnection());
            reply->writeStrongBinder(connection->asBinder());
            return NO_ERROR;
        }
 
....
 
}
cs


/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
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);
 
....
 
}
 
....
 
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() {
    return mEventThread->createEventConnection();
}
cs

 EventThread를 통해 createEventConnection() 함수를 실행하게 됩니다. EventThread가 SurfaceFlinger에 적용되는 과정에 대해 알고 싶으신 분은 이전 포스팅의 내용을 참조해 주시기 바랍니다.


SurfaceFlinger에서 EventThread의 초기화 과정

http://elecs.tistory.com/140


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

1
2
3
4
5
6
7
8
9
10
11
sp<EventThread::Connection> EventThread::createEventConnection() const {
    return new Connection(const_cast<EventThread*>(this));
}
 
....
 
EventThread::Connection::Connection(
        const sp<EventThread>& eventThread)
    : count(-1), mEventThread(eventThread), mChannel(new BitTube())
{
}
cs


mDataChannel = mEventConnection->getDataChannel();

 위 Connection 클래스를 생성하면서 만들어진 FD인 BitTube 클래스를 mDataChannel에 저장합니다.


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

1
2
3
sp<BitTube> EventThread::Connection::getDataChannel() const {
    return mChannel;
}
cs


 이후 등록된 FD가 호출될 경우 VSync 관련 이벤트가 발생하게 됩니다.

/system/core/libutils/Looper.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
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
#if DEBUG_CALLBACKS
    ALOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p"this, fd, ident,
            events, callback.get(), data);
#endif
 
    if (!callback.get()) {
        if (! mAllowNonCallbacks) {
            ALOGE("Invalid attempt to set NULL callback but not allowed for this looper.");
            return -1;
        }
 
        if (ident < 0) {
            ALOGE("Invalid attempt to set NULL callback with ident < 0.");
            return -1;
        }
    } else {
        ident = ALOOPER_POLL_CALLBACK;
    }
 
    int epollEvents = 0;
    if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN;
    if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT;
 
    { // acquire lock
        AutoMutex _l(mLock);
 
        Request request;
        request.fd = fd;
        request.ident = ident;
        request.callback = callback;
        request.data = data;
 
        struct epoll_event eventItem;
        memset(& eventItem, 0sizeof(epoll_event)); // zero out unused members of data field union
        eventItem.events = epollEvents;
        eventItem.data.fd = fd;
 
        ssize_t requestIndex = mRequests.indexOfKey(fd);
        if (requestIndex < 0) {
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
            if (epollResult < 0) {
                ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
                return -1;
            }
            mRequests.add(fd, request);
        } else {
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
            if (epollResult < 0) {
                ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno);
                return -1;
            }
            mRequests.replaceValueAt(requestIndex, request);
        }
    } // release lock
    return 1;
}
 
....
 
int Looper::pollInner(int timeoutMillis) {
 
....
 
    // Release lock.
    mLock.unlock();
 
    // Invoke all response callbacks.
    for (size_t i = 0; i < mResponses.size(); i++) {
        Response& response = mResponses.editItemAt(i);
        if (response.request.ident == ALOOPER_POLL_CALLBACK) {
            int fd = response.request.fd;
            int events = response.events;
            void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
            ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",
                    this, response.request.callback.get(), fd, events, data);
#endif
            int callbackResult = response.request.callback->handleEvent(fd, events, data);
            if (callbackResult == 0) {
                removeFd(fd);
            }
            // Clear the callback reference in the response structure promptly because we
            // will not clear the response vector itself until the next poll.
            response.request.callback.clear();
            result = ALOOPER_POLL_CALLBACK;
        }
    }
    return result;
}
cs



300x250