Android graphic: How vsync signal is passed from LCD to app?
Android graphic: How vsync signal is passed from LCD to app?
step 1: from LCD to eventthread.
EventThread and SF are two different threads in one process. it take care of dispatch vsync
signal to App.
0) let's begin from
./frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp
HWComposer() is created by surfaceflinger.
status_t SurfaceFlinger::readyToRun() {
mHwc = new HWComposer(this,
*static_cast<HWComposer::EventHandler *>(this));
}
in HWComposer() construction function
1)
it calls loadHwcModule() which opens Hwc module and assigns the device to mHwc.
LoadHwcModule() does the following:
1.1)
hw_get_module(HWC_HARDWARE_MODULE_ID, &module)
hwc_open_1(module, &mHwc);
1.2) hwc_open_1() actually invokes hwc_device_open()
in file hardware/intel/mfld_cdk/hwc/IntelHWComposerModule.cpp
in this function it will assignes mHwc with IntelHWComposer();
IntelHWComposer *hwc = new IntelHWComposer();
1.3)
if (hwc->initialize() == false)
IntelHWComposer initialize 初始化的过程中,
会创建IntelVsyncEventHandler 和 IntelFakeVsyncEvent
IntelVsyncEventHandler 负责把硬件的vsync signal 上报。
IntelFakeVsyncEvent ,如果硬件不支持vsync, 则用一个thread 伪造。
hardware/intel/mfld_cdk/hwc/IntelHWComposer.cpp
IntelHWComposer::initialize()
{
mVsync = new IntelVsyncEventHandler(this, mDrm->getDrmFd());
mFakeVsync = new IntelFakeVsyncEvent(this);
}
1.4)
IntelVsyncEventHandler inherates from android::Thread.
the thread and SF thread belongs to the same process.
IntelVsyncEventHandler reads the DRM device in a thread loop, if vsync is on, it will calls
bool IntelVsyncEventHandler::threadLoop()
for (i = 0; i <= VSYNC_SRC_HDMI; i++) {
arg.vsync_operation_mask = VSYNC_WAIT;
ret = drmCommandWriteRead(mDrmFd, DRM_PSB_VSYNC_SET, &arg, sizeof(arg));
mComposer->vsync((uint64_t)arg.vsync.timestamp, arg.vsync.pipe);
}
Here, mComposer is IntelHWComposer who creates the IntelVsyncEventHandler .
in HWComposer() construction function
2) register some procedures
mComposer->vsync finally invokes into.
void IntelHWComposer::vsync(int64_t timestamp, int pipe)
{
mProcs->vsync(const_cast<hwc_procs_t*>(mProcs), 0, timestamp);
}
Here we wodner how mProcs is registered?
in
./frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp
mCBContext->hwc = this;
mCBContext->procs.invalidate = &hook_invalidate;
mCBContext->procs.vsync = &hook_vsync;
149 mHwc->registerProcs(mHwc, &mCBContext->procs);
so hook_vsync is finllay invoked from the IntelVsyncEventHandler thread loop.
3)
./frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp
void HWComposer::vsync(int disp, int64_t timestamp) {
mEventHandler.onVSyncReceived(disp, timestamp);
}
the mEventHandler is actally the surfaceflinger itself. when sf creates HWComposer,
it pass itself to mEventHandler.
4)
void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
mEventThread->onVSyncReceived(type, timestamp);
}
5)
void EventThread::onVSyncReceived(int type, nsecs_t timestamp) {
mCondition.broadcast();
}
it just wakes up Eventthread. Eventthread main thread take care of dispatching
vsync to App. (we skip the alogrithm on how to dispatch.)
That's all, vsync signal finally walks from LCD to event thread now!
Step Two: vsync from event thread to app
Application usually create DisplayEventReceiver object.
It registers a connection to eventthread, and recevice vsync signal for app from Eventthread.
DisplayEventReceiver and Eventthread usually are in different process,
they comminucate with each other through binder.
DisplayEventReceiver is created with a looper, the looper is the execution thread.
the looper keeps messagequeue->next(). If no message, it invokes poll() to wait one
some io/FD
DisplayEventReceiver 包含了一个Bittube, which creates socket.
the socket is added to the polling list
frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp
status_t NativeDisplayEventReceiver::initialize() {
int rc = mMessageQueue->getLooper()->addFd(mReceiver.getFd(), 0, ALOOPER_EVENT_INPUT,
once the Eventthread wakes up the socket, the looper will wakeup.
./frameworks/native/services/surfaceflinger/EventThread.cpp
status_t EventThread::Connection::postEvent(
DisplayEventReceiver::sendEvents(mChannel, &event, 1);
BitTube::sendObjects(dataChannel, events, count);
When Eventthread want to notify the DisplayEventReceiver about vsync, it wakes up DisplayEventReceiver looper
the looper calls handleevent()
int callbackResult = response.request.callback->handleEvent(fd, events, data);
int NativeDisplayEventReceiver::handleEvent(int receiveFd, int events, void* data) {
dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
dispatchvsync finally calls
frameworks/base/core/java/android/view/DisplayEventReceiver.java
private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
onVsync(timestampNanos, builtInDisplayId, frame);
}
DisplayEventReceiver::onVsync() , it sends a message into the App looper's queue.
e.g
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / NANOS_PER_MS);
so the app start to draw now!