之前有一篇博客已经讲过一点Android应用程序和SurfaceFlinger建立连接,获取一个binder本地代理对象的过程,有了这个本地代理对象,应用程序就可以使用它调用SurfaceFlinger的UI合成、渲染功能了,可参考:Android Application与SurfaceFlinger连接过程分析,讲的很粗糙,但是我们可以看到一个大致过程,就是在WindowManagerService为每个Activity执行添加窗口addView方法时,创建好了WindowState对象,然后会调用win.attch()方法,和SurfaceFlinger取得连接也就是这个时候建立的。如果大家还想了解更细的话,可以参考老罗的博客:Android应用程序与SurfaceFlinger服务的连接过程分析,这里讲的非常详细。
关于UI合成渲染这块的知识,用到的东西太多了,我也是平时看看书,查查百度,想尽量搞清楚一些,但是目前为止,还是只能了解的部分,无法从全面掌握,也是为了怕自己忘记,所以将自己学习到的总结出来,方便随时查阅,如果能给大家带来帮助,那就更好了。
好了,我们进入正题,话说Android在4.1版本之前,UI界面方面存在很大的一个问题,就是经常会出现界面错乱,Google官方叫Jank,我们来看看下面这幅图:
从图上我们就很明显可以看出来问题产生的原因了,就是我们的CPU、GPU、Display三个器件需要进行配合对所有的UI进行合成、渲染、显示,但是之前的版本当中,没有一个统一的命令,导致CPU不知道何时开始执行合成,于是推迟了时间,后边的渲染和显示自然也就耽误了,那么就会出现数字1的画面显示了两帧,还有可能出现,数字1的一部分和数字2的一部分显示在同一界面上了,Google为了解决这个问题,所以加入了Vsync垂直同步信息,我们来看看加入了Vsync垂直同步信息后的情况:
从加入了垂直同步信号后的情况明显可以看出来,情况好了很多,由信号产生模块发出信号,只要信号发出,CPU马上进行合成,完成后交给GPU渲染,最后由Display进行显示,这样的话,就能很好的保证我们的界面显示问题了,当然,任何软件措施都无法保证100%的解决问题,但是从现在实际场景来看效果,确实还是非常满意的。
现在呢,我们就来了解一下Vsync垂直同步信号。从上面的逻辑图当中,应该也很容易看出来,我们需要的是什么呢?就是一个信号产生器就OK了,由它产生信号,指挥CPU、GPU、Display三者对UI进行同步的合成,这样就够了,那么Google在提供这个功能的时候,提供了两种方式:一种是由硬件产生,一种是由软件产生。接下来我们就来看一下Vsync的生产过程,产生的代码在HWComposer.cpp当中,composer的英文意思就是设计者,看着这个名字都让我们非常清晰它的作用,这也是细节之处,高手写出来的代码,处处都是亮点,小到一个变量、一个方法的修饰词,大到一个类、项目框架,里边有很多值得我们学习的地方。下面我们来看一下这块的代码:
HWComposer::HWComposer(
const sp& 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> 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);
// the number of displays we actually have depends on the
// hw composer version
if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
// 1.3 adds support for virtual displays
mNumDisplays = MAX_HWC_DISPLAYS;
} else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
// 1.1 adds support for multiple displays
mNumDisplays = NUM_BUILTIN_DISPLAYS;
} else {
mNumDisplays = 1;
}
}
if (mFbDev) {
ALOG_ASSERT(!(mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)),
"should only have fbdev if no hwc or hwc is 1.0");
DisplayData& disp(mDisplayData[HWC_DISPLAY_PRIMARY]);
disp.connected = true;
disp.width = mFbDev->width;
disp.height = mFbDev->height;
disp.format = mFbDev->format;
disp.xdpi = mFbDev->xdpi;
disp.ydpi = mFbDev->ydpi;
if (disp.refresh == 0) {
disp.refresh = nsecs_t(1e9 / mFbDev->fps);
ALOGW("getting VSYNC period from fb HAL: %lld", disp.refresh);
}
if (disp.refresh == 0) {
disp.refresh = nsecs_t(1e9 / 60.0);
ALOGW("getting VSYNC period from thin air: %lld",
mDisplayData[HWC_DISPLAY_PRIMARY].refresh);
}
} else if (mHwc) {
// here we're guaranteed to have at least HWC 1.1
for (size_t i =0 ; i
首先调用property_get获取系统属性,然后加载硬件模块loadHwcModule,mHwc变量的意思就很明显了,就是当前是否有硬件模块支持,如果有,那么needVSyncThread就赋值为false,表示Vsync信号由硬件产生,不需要软件模拟,否则的话,就需要由软件来产生了。那么如果需要由软件产生的话,就启动一个VSyncThread来执行这个功能。这个方法的意思也非常清晰,就是要确定好Vsync信号的产生源,到底这个事情由硬件来作,还是由软件来作。当然中间还包括一些初始化、设备加载等的逻辑。
下面我们就分硬件产生和软件产生两个方面来分析一下后续流程:
一、Vsync由硬件产生
如果Vsync信号由硬件产生,那么我们在执行初始化的时候,需要注册硬件回调,也就是我们上面代码当中mHwc->registerProcs(mHwc, &mCBContext->procs)这句的作用,mHwc为true,mCBContext是HWComposer的私有类变量,是一个cb_context*指针,它的定义也在HWComposer.cpp当中,代码如下:
struct HWComposer::cb_context {
struct callbacks : public hwc_procs_t {
// these are here to facilitate the transition when adding
// new callbacks (an implementation can check for NULL before
// calling a new callback).
void (*zero[4])(void);
};
callbacks procs;
HWComposer* hwc;
};
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=%lld)",
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,传递什么呢?其实就是一个时间点,我们回头看一下,解决Jank的方案就是要确定好Vsync信号产生的时间点,所以呢,这里由硬件确定好时间点,然后通知各个模块,当然,大家肯定都以系统时间为基点了,这样才能保证统一。我们来看一下mEventHandler是谁创建的。这就联系到SurfaceFlinger的初始化过程了,HWComposer就是在那里创建的,代码如下:
void SurfaceFlinger::init() {
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
status_t err;
Mutex::Autolock _l(mStateLock);
// initialize EGL for the default display
mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(mEGLDisplay, NULL, NULL);
// Initialize the H/W composer object. There may or may not be an
// actual hardware composer underneath.
mHwc = new HWComposer(this,
*static_cast(this));
// get a RenderEngine for the given display / config (can't fail)
mRenderEngine = RenderEngine::create(mEGLDisplay, mHwc->getVisualID());
// retrieve the EGL context that was selected/created
mEGLContext = mRenderEngine->getEGLContext();
LOG_ALWAYS_FATAL_IF(mEGLContext == EGL_NO_CONTEXT,
"couldn't create EGLContext");
// initialize our non-virtual displays
for (size_t i=0 ; iisConnected(i) || type==DisplayDevice::DISPLAY_PRIMARY) {
// All non-virtual displays are currently considered secure.
bool isSecure = true;
createBuiltinDisplayLocked(type);
wp token = mBuiltinDisplays[i];
sp bq = new BufferQueue(new GraphicBufferAlloc());
sp fbs = new FramebufferSurface(*mHwc, i, bq);
int32_t hwcId = allocateHwcDisplayId(type);
sp hw = new DisplayDevice(this,
type, hwcId, mHwc->getFormat(hwcId), isSecure, token,
fbs, bq,
mRenderEngine->getEGLConfig());
if (i > DisplayDevice::DISPLAY_PRIMARY) {
// FIXME: currently we don't get blank/unblank requests
// for displays other than the main display, so we always
// assume a connected display is unblanked.
ALOGD("marking display %d as acquired/unblanked", i);
hw->acquireScreen();
}
mDisplays.add(token, hw);
}
}
// make the GLContext current so that we can create textures when creating Layers
// (which may happens before we render something)
getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
// start the EventThread
sp vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
vsyncPhaseOffsetNs, true);
mEventThread = new EventThread(vsyncSrc);
sp sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
sfVsyncPhaseOffsetNs, false);
mSFEventThread = new EventThread(sfVsyncSrc);
mEventQueue.setEventThread(mSFEventThread);
mEventControlThread = new EventControlThread(this);
mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY);
// set a fake vsync period if there is no HWComposer
if (mHwc->initCheck() != NO_ERROR) {
mPrimaryDispSync.setPeriod(16666667);
}
// initialize our drawing state
mDrawingState = mCurrentState;
// set initial conditions (e.g. unblank default device)
initializeDisplays();
// start boot animation
startBootAnim();
}
class VSyncThread : public Thread {
HWComposer& mHwc;
mutable Mutex mLock;
Condition mCondition;
bool mEnabled;
mutable nsecs_t mNextFakeVSync;
nsecs_t mRefreshPeriod;
virtual void onFirstRef();
virtual bool threadLoop();
public:
VSyncThread(HWComposer& hwc);
void setEnabled(bool enabled);
};
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;
}