3.3 Vsync
3.3.1 Vsync的作用
Vsync用于画面同步,避免在画面没有完成绘制的时候就进行刷新,而导致出现画面撕裂的情况。
Vsync的作用有两个:
1)解决画面撕裂的情况
2)Cpu时间片分配不合理导致的jank的情况;
出现Jank(重复帧)的首先原因就在于第二帧没有及时的绘制(当然即使第二帧及时绘制,也依然可能出现Jank,这就是同时引入三重缓冲的作用)。
而引入vsync之后,要求一旦VSync出现后,立刻就开始执行下一帧的绘制工作。这样就可以大大降低Jank出现的概率。
3)CPU过度渲染
VSYNC引入后,要求绘制也只能在收到VSYNC消息之后才能进行,因此,也就杜绝了另外一种极端情况的出现—-CPU(GPU)一直不停的进行绘制,帧的生成速度高于屏幕的刷新速度,导致生成的帧不能被显示,只能丢弃,这样就出现了丢帧的情况—-引入VSYNC后,绘制的速度就和屏幕刷新的速度保持一致了。
3.3.2 软件模拟Vsync
Android系统中VSYNC信号分为两种,一种是硬件生成的信号,一种是软件模拟的信号。硬件信号是由HardwareComposer提供的,HWC封装了相关的HAL层,如果硬件厂商提供的HAL层实现能定时产生VSYNC中断,则直接使用硬件的VSYNC中断,否则HardwareComposer内部会通过VSyncThread来模拟产生VSYNC中断。
VsyncThread产生模拟Vsync中断。我们直接来看看threadLoop函数的实现。在threadLoop中是一些很简单的时间计算。然后sleep,等到Vsync信号的时候,就调用mHwc.mEventHandler.onVSyncReceived(0,next_vsync)函数来告知VSYNC的到来。
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); // 下一次Vsync到来的时间 nsecs_t next_vsync = mNextFakeVSync; // 为了等待下一个vsync到来应该休眠的时间 nsecs_t sleep = next_vsync - now; // 错过了VSYNC的时间 if (sleep < 0) { // we missed, find where the next vsync should be //重新计算下应该休息的时间 sleep = (period - ((now - next_vsync) % period)); //更新下次VSYNC的时间 next_vsync = now + sleep; } //更新下下次VSYNC的时间 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) { //休眠之后,到了该发生VSYNC的时间了 mHwc.mEventHandler.onVSyncReceived(0, next_vsync); }
return true; } |
那么mHwc.mEventHandler.onVSyncReceived(0,next_vsync);执行的是哪个方法呢?我们注意到mEventHandler实际上是HWC创建时被传入的。我们来看下HWC创建时的代码:
// 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));
@HWComposer.cpp HWComposer::HWComposer( const sp<SurfaceFlinger>& flinger, EventHandler& handler) : mFlinger(flinger), mFbDev(0), mHwc(0), mNumDisplays(1), mCBContext(new cb_context), mEventHandler(handler), mDebugForceFakeVSync(false) { for (size_t i =0 ; i<MAX_HWC_DISPLAYS ; i++) { mLists[i] = 0; } |
这个mEventHandler就是指向SurfaceFlinger。因此可以知道当VSYNC到来的时候,Surfaceflinger的OnVSyncReceived函数处理这个消息。
3.3.3 硬件实现vsync
从HWComposer的构造函数来看,如果存在硬件合成模块的话,就会进行procs操作指针的赋值:
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); } |
同vsync相关的函数处理指针为hook_vsync。当HWC有vsync信号生成时,硬件模块会通过procs.vsync来通知软件部分,因此也就是调用了hook_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); }
void HWComposer::vsync(int disp, int64_t timestamp) { //只有真实的硬件设备才会产生VSYNC 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。 mEventHandler.onVSyncReceived(disp, timestamp); } } |
因此,可以看到,不管是软件模拟VSYNC还是硬件模块产生的VSYNC,当VSYNC信号到来的时候,都是执行SurfaceFlinger的onVSyncReceived。
3.3.4 硬件VSYNC时procs.vsync回调时机
在3.1节的HWComposer构建函数的代码中,有一段注册回调函数的的代码:
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); } |
中代码中3.2节的知识点,我们可以快递定位到mHwc->registerProcs(mHwc,&mCBContext->procs);实际上面就是调用hwc_registerProcs函数。
/* * Save callback functions registered to HWC */ static void hwc_registerProcs(struct hwc_composer_device_1* dev, hwc_procs_t const* procs) { ALOGI("%s", __FUNCTION__); hwc_context_t* ctx = (hwc_context_t*)(dev); if(!ctx) { ALOGE("%s: Invalid context", __FUNCTION__); return; } ctx->proc = procs;
// Now that we have the functions needed, kick off // the uevent & vsync threads、 // 初始化uevent线程和vsync线程 init_uevent_thread(ctx); init_vsync_thread(ctx); }
void init_vsync_thread(hwc_context_t* ctx) { int ret; pthread_t vsync_thread; ALOGI("Initializing VSYNC Thread"); // 处理函数为vysnc_loop ret = pthread_create(&vsync_thread, NULL, vsync_loop, (void*) ctx); if (ret) { ALOGE("%s: failed to create %s: %s", __FUNCTION__, HWC_VSYNC_THREAD_NAME, strerror(ret)); } }
|
我们来看看vsync_loop函数的具体实现:
static void *vsync_loop(void *param) { hwc_context_t * ctx = reinterpret_cast<hwc_context_t *>(param);
char thread_name[64] = HWC_VSYNC_THREAD_NAME; prctl(PR_SET_NAME, (unsigned long) &thread_name, 0, 0, 0); setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY + android::PRIORITY_MORE_FAVORABLE);
const int MAX_DATA = 64; char vdata[MAX_DATA]; bool logvsync = false;
struct pollfd pfd[2]; int fb_fd[2]; uint64_t timestamp[2]; int num_displays;
// 如果设置了"debug.hwc.fakevsync"属性,使用假的vsync char property[PROPERTY_VALUE_MAX]; if(property_get("debug.hwc.fakevsync", property, NULL) > 0) { if(atoi(property) == 1) ctx->vstate.fakevsync = true; }
if(property_get("debug.hwc.logvsync", property, 0) > 0) { if(atoi(property) == 1) logvsync = true; }
if (ctx->mExtDisplay->getHDMIIndex() > 0) num_displays = 2; else num_displays = 1;
char vsync_node_path[MAX_SYSFS_FILE_PATH]; for (int dpy = HWC_DISPLAY_PRIMARY; dpy < num_displays; dpy++) { snprintf(vsync_node_path, sizeof(vsync_node_path), "/sys/class/graphics/fb%d/vsync_event", dpy == HWC_DISPLAY_PRIMARY ? 0 : ctx->mExtDisplay->getHDMIIndex()); ALOGI("%s: Reading vsync for dpy=%d from %s", __FUNCTION__, dpy, vsync_node_path); fb_fd[dpy] = open(vsync_node_path, O_RDONLY); // 如果打开"/sys/class/graphics/fb%d/vsync_event"失败,也使用假的vsync if (fb_fd[dpy] < 0) { // Make sure fb device is opened before starting this thread so this // never happens. ALOGE ("%s:not able to open vsync node for dpy=%d, %s", __FUNCTION__, dpy, strerror(errno)); if (dpy == HWC_DISPLAY_PRIMARY) { ctx->vstate.fakevsync = true; break; } } // Read once from the fds to clear the first notify pread(fb_fd[dpy], vdata , MAX_DATA, 0);
pfd[dpy].fd = fb_fd[dpy]; if (pfd[dpy].fd >= 0) pfd[dpy].events = POLLPRI | POLLERR; }
if (LIKELY(!ctx->vstate.fakevsync)) { // 默认fakevsync=false do { int err = poll(pfd, num_displays, -1); if(err > 0) { for (int dpy = HWC_DISPLAY_PRIMARY; dpy < num_displays; dpy++) { if (pfd[dpy].revents & POLLPRI) { int len = pread(pfd[dpy].fd, vdata, MAX_DATA, 0); if (UNLIKELY(len < 0)) { // If the read was just interrupted - it is not a // fatal error. Just continue in this case ALOGE ("%s: Unable to read vsync for dpy=%d : %s", __FUNCTION__, dpy, strerror(errno)); continue; } // extract timestamp if (!strncmp(vdata, "VSYNC=", strlen("VSYNC="))) { timestamp[dpy] = strtoull(vdata + strlen("VSYNC="), NULL, 0); } // send timestamp to SurfaceFlinger ALOGD_IF (logvsync, "%s: timestamp %llu sent to SF for dpy=%d", __FUNCTION__, timestamp[dpy], dpy); //从"/sys/class/graphics/fb%d/vsync_event"找到VSYNC时间戳,回调函数。 ctx->proc->vsync(ctx->proc, dpy, timestamp[dpy]); } }
} else { ALOGE("%s: vsync poll failed errno: %s", __FUNCTION__, strerror(errno)); continue; } } while (true);
} else { // 使用假的时间戳,强制60HZ,回调函数。 //Fake vsync is used only when set explicitly through a property or when //the vsync timestamp node cannot be opened at bootup. There is no //fallback to fake vsync from the true vsync loop, ever, as the //condition can easily escape detection. //Also, fake vsync is delivered only for the primary display. do { usleep(16666); timestamp[HWC_DISPLAY_PRIMARY] = systemTime(); ctx->proc->vsync(ctx->proc, HWC_DISPLAY_PRIMARY, timestamp[HWC_DISPLAY_PRIMARY]);
} while (true); }
for (int dpy = HWC_DISPLAY_PRIMARY; dpy <= HWC_DISPLAY_EXTERNAL; dpy++ ) { if(fb_fd[dpy] >= 0) close (fb_fd[dpy]); }
return NULL; } |
版权所有,转载请注明出处:http://www.junsion.icoc.bz/ by 小丑