Android爲了增強界面流暢度引入了VSync機制。VSync信號一般由硬件產生,爲了兼容一些老的機器,也可以軟件產生。因些,就可以分爲硬件VSync和軟件VSync。VSync的生成邏輯主要在HWComposer裏面。相關文件如下:
- frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp
- frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.h
HWComposer在SurfaceFlinger的init函數裏創建:
void SurfaceFlinger::init() {
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
......
// Initialize the H/W composer object. There may or may not be an
// actual hardware composer underneath.
mHwc = new HWComposer(this,
*static_cast<HWComposer::EventHandler *>(this));
......
}
- SurfaceFlinger成員變量保存了HWComposer對象指針
- 創建HWComposer時,使用SurfaceFlinger作爲參數傳入
HWComposer::HWComposer(
const sp<SurfaceFlinger>& flinger,
EventHandler& handler)
: mFlinger(flinger),
mFbDev(0), mHwc(0), mNumDisplays(1),
mCBContext(new cb_context),
mEventHandler(handler),
mDebugForceFakeVSync(false)
{
......
bool needVSyncThread = true;
// Note: some devices may insist that the FB HAL be opened before HWC.
int fberr = loadFbHalModule();
loadHwcModule();
.......
if (mHwc) {
ALOGI("Using %s version %u.%u", HWC_HARDWARE_COMPOSER,
(hwcApiVersion(mHwc) >> 24) & 0xff,
(hwcApiVersion(mHwc) >> 16) & 0xff);
if (mHwc->registerProcs) {
mCBContext->hwc = this;
mCBContext->procs.invalidate = &hook_invalidate;
mCBContext->procs.vsync = &hook_vsync;
if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
mCBContext->procs.hotplug = &hook_hotplug;
else
mCBContext->procs.hotplug = NULL;
memset(mCBContext->procs.zero, 0, sizeof(mCBContext->procs.zero));
mHwc->registerProcs(mHwc, &mCBContext->procs);
}
// don't need a vsync thread if we have a hardware composer
needVSyncThread = false;
// always turn vsync off when we start
eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0);
......
}
......
if (needVSyncThread) {
// we don't have VSYNC support, we need to fake it
mVSyncThread = new VSyncThread(*this);
}
}
- 在構造函數裏把SurfaceFlinger對象指針賦值給成員變量mFlinger和mEventHandler
- 首先嚐試加載HWC模塊,如果加載成功就使用硬件vsync,加載失敗就使用軟件模擬vsync
在加載HWC硬件模塊時,會註冊vsync回調函數HWComposer::hook_vsync。當硬件vsync到來時會調用些函數。代碼如下:
void HWComposer::hook_vsync(const struct hwc_procs* procs, int disp,
int64_t timestamp) {
cb_context* ctx = reinterpret_cast<cb_context*>(
const_cast<hwc_procs_t*>(procs));
ctx->hwc->vsync(disp, timestamp);
}
- 調用hwc對象的vsync函數
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);
}
}
- 最終調用mEventHandler的onVSyncReceived函數,也既SurfaceFlinger的onVSyncReceived
軟件vsync在VSyncThread線程中實現,看看線程循環函數:
bool HWComposer::VSyncThread::threadLoop() {
{ // scope for lock
Mutex::Autolock _l(mLock);
while (!mEnabled) {
mCondition.wait(mLock);
}
}
const nsecs_t period = mRefreshPeriod;
const nsecs_t now = systemTime(CLOCK_MONOTONIC);
nsecs_t next_vsync = mNextFakeVSync;
nsecs_t sleep = next_vsync - now;
if (sleep < 0) {
// we missed, find where the next vsync should be
sleep = (period - ((now - next_vsync) % period));
next_vsync = now + sleep;
}
mNextFakeVSync = next_vsync + period;
struct timespec spec;
spec.tv_sec = next_vsync / 1000000000;
spec.tv_nsec = next_vsync % 1000000000;
int err;
do {
err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
} while (err<0 && errno == EINTR);
if (err == 0) {
mHwc.mEventHandler.onVSyncReceived(0, next_vsync);
}
return true;
}
- 以固定的時間間隔發送vsync信號
- 調用mEventHandler的onVSyncReceived函數,最終調用SurfaceFlinger的onVSyncReceived函數。
- 這樣不管是硬件vsync還是軟件vsync都達成了統一。當VSync信號產生時調用SurfaceFlinger的回調函數onVSyncReceived。