SurfaceFlinger原理(二):Vsync事件的處理
SurfaceFlinger內部有兩個EventThread,一個負責app端對Vsync訊號的監聽處理,一個負責SurfaceFlinger對Vsync訊號的監聽處理。SurfaceFlinger內部維持了一個MessageQueue,當SurfaceFlinger端的EventThread監測到Vsync事件,會觸發INVALIDATE操作,即當前的圖層失效了,需要重新計算重新整理。
/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::onFirstRef()
{
mEventQueue.init(this );
}
/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
sfVsyncPhaseOffsetNs, true, "sf");
mSFEventThread = new EventThread(sfVsyncSrc);
mEventQueue.setEventThread(mSFEventThread);
/frameworks/native/services/surfaceflinger/MessageQueue.cpp
void MessageQueue::setEventThread(const sp<EventThread>& eventThread)
{
mEventThread = eventThread;
mEvents = eventThread->createEventConnection();
mEventTube = mEvents->getDataChannel();
mLooper->addFd(mEventTube->getFd(), 0, Looper::EVENT_INPUT,
MessageQueue::cb_eventReceiver , this);
}
每個EventThread內部都維持一個mDisplayEventConnections,記錄了所有跟這個EventThread的Connection物件的集合。app端通過createDisplayEventConnection構建一個Connection,SurfaceFlinger端通過setEventThread–>createEventConnection。app端的Connection可能不止一個,SurfaceFlinger端的Connection只有一個。 Connection的作用是什麼?先看看Connection的建構函式:
/frameworks/native/services/surfaceflinger/EventThread.cpp
EventThread::Connection::Connection(
const sp<EventThread>& eventThread)
: count(-1), mEventThread(eventThread), mChannel(new BitTube())
{
}
mEventThread初始化為連線到的EventThread,即是將儲存到這個EventThread中的mDisplayEventConnections中。count值表示Connection是否有感興趣的事件(通常這個感興趣的事件就是Vsync)。-1表示感興趣的事件已經被處理或者沒有感興趣的事件(因為初始化為-1),0表示一次性的感興趣的事件,這種事件被處理後count值會變成-1,大於等於1表示是連續事件。mChannel是一個BitTube,可以將其理解是用socketpair呼叫建立的兩個互相連線的套接字,Android將其作為管道用,MessageQueue::setEventThread將監聽讀端的套接字的輸入事件,從而回調cb_eventReceiver進行處理。 EventThread的建構函式中,會對mVSyncEvent進行初始化,mVSyncEvent是一個長度為2的DisplayEventReceiver::Event陣列,用來儲存主螢幕(下標為0)和外界螢幕(下標為1)的Vsync事件的資訊,如id,時間戳,count值(這個count值表示接收到的Vsync事件數量)等。然後在onFirstRef中,EventThread會進入threadLoop迴圈。
/frameworks/native/services/surfaceflinger/EventThread.cpp
EventThread::EventThread(const sp<VSyncSource>& src)
: mVSyncSource(src),
mUseSoftwareVSync(false),
mVsyncEnabled(false),
mDebugVsyncEnabled(false),
mVsyncHintSent(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;
}
struct sigevent se;
se.sigev_notify = SIGEV_THREAD;
se.sigev_value.sival_ptr = this;
se.sigev_notify_function = vsyncOffCallback;
se.sigev_notify_attributes = NULL;
timer_create(CLOCK_MONOTONIC, &se, &mTimerId);
}
threadLoop看似很簡短,但其實內部比較複雜。
/frameworks/native/services/surfaceflinger/EventThread.cpp
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;
}
首先看EventThread::waitForEvent,該函式用來等待一個Connection發生感興趣的事件,即Vsync事件。我們從EventThread初始化開始分析,條理會比較清晰。初始化時,timestamp為0,會直接mDisplayEventConnections中尋找發生了Vsync的Connection,條件是遍歷mDisplayEventConnections中的所用Connection,若發現其count值是0或1或者接收到的Vsync事件數量能夠被Connection的count值整除,則認為發生了Vsync,將這個Connection加入到signalConnections中。根據Connection的count值的不同,一次性事件(count為0)發生的Connection會將其count值置為-1,而連續事件發生的Connection不需要重置。waitForVSync表示是否有一個Connection是否對Vsync感興趣,當一個不為null的Connection的count大於等於0時,waitForVSync就會被設為true。 初始化時,timestamp是0,waitForVSync為false,因此執行緒會阻塞在mCondition.wait中。mCondition喚醒的條件之一是由Vsync事件到來。
/frameworks/native/services/surfaceflinger/EventThread.cpp
// 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!
//Connection已經無效
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;
}
Vsync怎麼產生的呢?之前有提到HWComposer的建構函式會註冊一些回撥函式到hwc層:hook_invalidate和hook_vsync。其中hook_vsync就是產生硬體Vsync後進行的回撥函式。產生硬體Vsync的程式碼在廠家的hwc HAL層程式碼中,我們直接看回調函式的程式碼。 HWComposer::vsync帶回了兩個引數:第一個引數是display id,第二個引數是Vsync產生的時間戳。mEventHandler表示的就是SurfaceFlinger。
/frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp
void HWComposer::vsync(int disp, int64_t timestamp) {
if (uint32_t(disp) < HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
{
Mutex::Autolock _l(mLock);
// There have been reports of HWCs that signal several vsync events
// with the same timestamp when turning the display off and on. This
// is a bug in the HWC implementation, but filter the extra events
// out here so they don't cause havoc downstream.
if (timestamp == mLastHwVSync[disp]) {
ALOGW("Ignoring duplicate VSYNC event from HWC (t=%" PRId64 ")",
timestamp);
return;
}
mLastHwVSync[disp] = timestamp;
}
char tag[16];
snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp);
ATRACE_INT(tag, ++mVSyncCounts[disp] & 1);
mEventHandler.onVSyncReceived(disp, timestamp);
}
}
當對應的螢幕是主螢幕且已開啟Vsync時,通過DispSync::addResyncSample的返回值needsHwVsync決定要不要繼續開啟Vsync或者關閉Vsync。
/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
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);
}
}
DispSync::addResyncSample接收以個Vsync的事件戳作為引數,是怎麼計算出是否需要開啟Vsync的呢?mPrimaryDispSync內部維持了一個長度為32的時間戳陣列mResyncSamples,而addResyncSample要做的,就是將這次Vsync事件的事件戳加入到裡面去,重新更新Vsync模型,並根據結果決定是否開啟Vsync。mFirstResyncSample在時間戳總數未達到32時,記錄的是mResyncSamples的首個元素位置,也就是0;在時間戳總數達到32時,記錄的是插入新的時間戳的位置。mNumResyncSamples表示當前的時間戳總數,最大為32。mResyncSamples裡面的時間戳按從舊到新進行排列,總是保留最新的時間戳,如果時間戳數量大於32個的話,則新加入的時間戳會覆蓋掉時間最早的時間戳。updateModelLocked用來更新Vsync模型。 如果kIgnorePresentFences的值為true,則根據DispSyncThread是否存在Vsync事件回撥決定是否開啟Vsync,若存在則開啟;如果kIgnorePresentFences的值為false,週期mPeriod為0或者偏差mError大於閾值kErrorThreshold時,則需要開啟Vsync進行校正。
/frameworks/native/services/surfaceflinger/DispSync.cpp
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 (kIgnorePresentFences) {
// 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 > kErrorThreshold;
}
當定義了巨集RUNNING_WITHOUT_SYNC_FRAMEWORK或者app Vsync相移和SurfaceFlinger vsync相移均為0時,kIgnorePresentFences 為true;否則為false。
/frameworks/native/services/surfaceflinger/DispSync.h
// Ignore present (retire) fences if the device doesn't have support for the
// sync framework, or if all phase offsets are zero. The latter is useful
// because it allows us to avoid resync bursts on devices that don't need
// phase-offset VSYNC events.
#if defined(RUNNING_WITHOUT_SYNC_FRAMEWORK) || \
(VSYNC_EVENT_PHASE_OFFSET_NS == 0 && SF_VSYNC_EVENT_PHASE_OFFSET_NS == 0)
static const bool kIgnorePresentFences = true;
#else
static const bool kIgnorePresentFences = false;
#endif
當樣本時間戳數量大於等於3時,才會更新Vsync模型。更新Vsync模型的方式如下。1.週期mPeriod:從最舊的時間戳起,用第二舊的時間戳減去最舊的時間戳得到期間Vsync間隔時間;然後用第三舊的時間戳減去第二舊的時間戳得到期間Vsync的間隔時間。。。直到得到時間戳總數減1數量(mNumResyncSamples - 1)的Vsync間隔時間總和,求取其平均值即為週期mPeriod 。2.相移mPhase:以mPeriod 為週期,算出其角速度scale=2pi/mPeriod ,對於mNumResyncSamples個時間戳,分別計算出它們的相移角度samplePhase=時間戳%週期*角速度。sampleAvgX為相移角度的cos值 的累加,sampleAvgY為相移角度的sin值的累加值。sampleAvgX和sampleAvgY分別為sampleAvgX和sampleAvgY的平均值。atan2(sampleAvgY, sampleAvgX)計算出平均每個時間戳的偏移角度,除以角速度就是相移mPhase,準確點叫相移時間。為確保mPhase為正數,當計算出的mPhase小於0時,為其加上mPeriod 得到一個新的mPhase值。最後通過updateModel將mPeriod 和mPhase設定到DispSyncThread裡面。
/frameworks/native/services/surfaceflinger/DispSync.cpp
void DispSync::updateModelLocked() {
if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) {
nsecs_t durationSum = 0;
for (size_t i = 1; i < mNumResyncSamples; i++) {
size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES;
durationSum += mResyncSamples[idx] - mResyncSamples[prev];
}
mPeriod = durationSum / (mNumResyncSamples - 1);
double sampleAvgX = 0;
double sampleAvgY = 0;
double scale = 2.0 * M_PI / double(mPeriod);
for (size_t i = 0; i < mNumResyncSamples; i++) {
size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
nsecs_t sample = mResyncSamples[idx];
double samplePhase = double(sample % mPeriod) * scale;
sampleAvgX += cos(samplePhase);
sampleAvgY += sin(samplePhase);
}
sampleAvgX /= double(mNumResyncSamples);
sampleAvgY /= double(mNumResyncSamples);
mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);
if (mPhase < 0) {
mPhase += mPeriod;
}
if (kTraceDetailedInfo) {
ATRACE_INT64("DispSync:Period", mPeriod);
ATRACE_INT64("DispSync:Phase", mPhase);
}
// Artificially inflate the period if requested.
mPeriod += mPeriod * mRefreshSkipCount;
mThread->updateModel(mPeriod, mPhase);
}
}
最初,DispSyncThread在threadLoop過程中會因為mPeriod為0而阻塞,updateModel會將其喚醒。
/frameworks/native/services/surfaceflinger/DispSync.cpp
void updateModel(nsecs_t period, nsecs_t phase) {
Mutex::Autolock lock(mMutex);
mPeriod = period;
mPhase = phase;
mCond.signal();
}
那麼問題來了,mThread->hasAnyEventListeners返回的是false(這裡假定的情況是app和SurfaceFlingfer的Vsync相移為0),導致Vsync會進入關閉狀態,這樣剛產生了一次的Vsync又關掉了,什麼時候才會開啟Vsync呢?實際上,第一個Vsync是用來更新週期和相移時間用的,Vsync在恰當的時候開關,有利於節約電源,減少功耗。 當app或者SurfaceFlinger需要Vsync來協調更新介面時,都需要呼叫到EventThread::requestNextVsync時。表示當前的Connection對Vsync事件感興趣,這時會把Connection的count設為0,這時一直阻塞在條件變數mCondition的SurfaceFlinge端的EventThread會被喚醒。
/frameworks/native/services/surfaceflinger/EventThread.cpp
void EventThread::requestNextVsync(
const sp<EventThread::Connection>& connection) {
Mutex::Autolock _l(mLock);
if (connection->count < 0) {
connection->count = 0;
mCondition.broadcast();
}
}
回到EventThread::waitForEvent函式。由於有Connection的count已經設定成0,waitForVSync變成true,但是timestamp仍為0,於是會呼叫enableVSyncLocked的邏輯使能Vsync。
/frameworks/native/services/surfaceflinger/EventThread.cpp
// 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;
}
mVSyncSource指的是DispSyncSource物件。
/frameworks/native/services/surfaceflinger/EventThread.cpp
void EventThread::enableVSyncLocked() {
if (!mUseSoftwareVSync) {
// never enable h/w VSYNC when screen is off
if (!mVsyncEnabled) {
mVsyncEnabled = true;
mVSyncSource->setCallback(static_cast<VSyncSource::Callback*>(this));
mVSyncSource->setVSyncEnabled(true);
}
}
mDebugVsyncEnabled = true;
sendVsyncHintOnLocked();
}
EventThread繼承自VSyncSource::Callback,這裡將mCallback 設定成EventThread型別的入參。
/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
virtual void setCallback(const sp<VSyncSource::Callback>& callback) {
Mutex::Autolock lock(mMutex);
mCallback = callback;
}
DispSyncSource繼承自DispSync::Callback。
/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
virtual void setVSyncEnabled(bool enable) {
// Do NOT lock the mutex here so as to avoid any mutex ordering issues
// with locking it in the onDispSyncEvent callback.
if (enable) {
status_t err = mDispSync->addEventListener(mPhaseOffset,
static_cast<DispSync::Callback*>(this));
if (err != NO_ERROR) {
ALOGE("error registering vsync callback: %s (%d)",
strerror(-err), err);
}
//ATRACE_INT(mVsyncOnLabel.string(), 1);
} else {
status_t err = mDispSync->removeEventListener(
static_cast<DispSync::Callback*>(this));
if (err != NO_ERROR) {
ALOGE("error unregistering vsync callback: %s (%d)",
strerror(-err), err);
}
//ATRACE_INT(mVsyncOnLabel.string(), 0);
}
}
/frameworks/native/services/surfaceflinger/DispSync.cpp
status_t DispSync::addEventListener(nsecs_t phase,
const sp<Callback>& callback) {
Mutex::Autolock lock(mMutex);
return mThread->addEventListener(phase, callback);
}
addEventListenerd第一個引數是EventThread使用的Vsync偏移,由VSYNC_EVENT_PHASE_OFFSET_NS或者SF_VSYNC_EVENT_PHASE_OFFSET_NS指定;第二個引數是是使用的DispSyncSource,將這兩個引數儲存在一個EventListener 物件中,並將EventListener 的mLastEventTime 設為當前事件減去半個Vsync週期的值,然後加入到mEventListeners的Vector中,然後喚醒DispSyncThread。
/frameworks/native/services/surfaceflinger/DispSync.cpp
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;
}
再看看DispSyncThread的threadLoop函式。第一次因為mPeriod == 0而導致的阻塞現在已經可以通行了。computeNextEventTimeLocked用來計算下一個 Vsync的時間戳。如果當前時間未到下一個 Vsync的時間戳,呼叫waitRelative等待相差的時間直到有人喚醒DispSyncThread。gatherCallbackInvocationsLocked收集所有符合要求的回撥,在fireCallbackInvocations中執行這些回撥。
/frameworks/native/services/surfaceflinger/DispSync.cpp
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 (kTraceDetailedInfo) {
ATRACE_INT64("DispSync:WakeupLat", now - nextEventTime);
ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency);
}
}
callbackInvocations = gatherCallbackInvocationsLocked(now);
}
if (callbackInvocations.size() > 0) {
fireCallbackInvocations(callbackInvocations);
}
}
return false;
}
/frameworks/native/services/surfaceflinger/DispSync.cpp
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;
}
回撥實際上是呼叫了DispSyncSource::onDispSyncEvent。而DispSyncSource::onDispSyncEvent會調到EventThread::onVSyncEvent。
/frameworks/native/services/surfaceflinger/DispSync.cpp
void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) {
for (size_t i = 0; i < callbacks.size(); i++) {
callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
}
}
/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
virtual void onDispSyncEvent(nsecs_t when) {
sp<VSyncSource::Callback> callback;
{
Mutex::Autolock lock(mMutex);
callback = mCallback;
if (mTraceVsync) {
mValue = (mValue + 1) % 2;
ATRACE_INT(mVsyncEventLabel.string(), mValue);
}
}
if (callback != NULL) {
callback->onVSyncEvent(when);
}
}
&esmp; EventThread::onVSyncEvent會填充mVSyncEvent[0],然後喚醒EventThread執行緒。
/frameworks/native/services/surfaceflinger/EventThread.cpp
void EventThread::onVSyncEvent(nsecs_t timestamp) {
Mutex::Autolock _l(mLock);
mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
mVSyncEvent[0].header.id = 0;
mVSyncEvent[0].header.timestamp = timestamp;
mVSyncEvent[0].vsync.count++;
mCondition.broadcast();
}
&esmp; 在非同步呼叫enableVSyncLocked後,EventThread會阻塞在waitRelative中,阻塞時間在關屏狀態下為16ms,亮屏狀態為1s。如果EventThread::onVSyncEvent在超時時間內沒能喚醒EventThread ,則手動填充mVSyncEvent[0],使得流程可以繼續走下去。
// 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);
}
再回到EventThread::waitForEvent開頭,由於mVSyncEvent[0]的時間戳已經更新,所以timestamp的值不為0,然後將mVSyncEvent[0]的時間戳和Connection的count值分別重置為0和-1,將對應的Connection加入到signalConnections中。最終會退出waitForEvent中的do…while迴圈。 離開waitForEvent後就是遍歷signalConnections,然後分別呼叫其Connection::postEvent上報事件,以進行更新圖層或畫一幀的操作。