1)vsync信号的产生、分发的组成有哪些:

  • HWComposer:
    • 产生hardware vsync,post fb
  • VSyncThread :
    • 硬件不支持的状态下,只有通过软件方式来模拟hw vsync
    • DispSync,DispSyncThread: 接受HWComposer的hw vsync信号作为校准,同时也开始执行模拟产生vsync信号+偏移,此时会不时地进行校准,如postComposition后。
    • EventControlThread:SurfaceFlinger中的一个线程,它的作用仅仅是用来控制hw vsync开关
    • EventThread:负责分发vsync到SurfaceFlinger或app
  • DispSyncSource:
    • EventThread和DispSyncThread的信息传递者, 把vsync信号从DispSyncThread传递到EventThread;同时可以用来设置相位偏移参数。

      2)vsync的现状:

      vsync信号不再完全由硬件产生,其中有 HWComposer 产生的 hw vsync 信号主要用来做时间的校准,vsync信号发生者是DispSyncThread, 当DispSyncThread产生的信号得到校准后,此时 HWCComposer 的 hw vsync会被关闭。

3)vsync生成围绕的问题:

核心问题:

1.硬件vsync如何开关?
2.DispSyncThread 信号的是如何产生?
3. 两个EventThread 线程如何把vsync分发给 SurfaceFlinger 和app?

问题的解决方法:

硬件vsync如何开关?

  • 首先,HWComposer在hwc_composer_device_1中注册回调:
    • mCBContext->procs.vsync = &hook_vsync;
    • mHwc->registerProcs(mHwc, &mCBContext->procs);
  • 其次,vsync过来:HWComposer::hook_vsync => HWComposer::vsync
  • 然后,HWComposer::vsync=>mEventHandler.onVSyncReceived(disp, timestamp); 其中mEventHandler即surfaceflinger
  • 最后,SurfaceFlinger::onVSyncReceived
    1. //加入样本,校准mPrimaryDispSync(DispSync),返回是否还需要hwsync
    2. needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);
    3. if (needsHwVsync) {
    4. enableHardwareVsync(); //未同步,接续接受hw vsync
    5. } else {
    6. disableHardwareVsync(false); //已同步,关闭hw vsync
    7. }
    总结:
    硬件vsync的开启和关闭:SurfaceFlinger 专门启动了一个线程EventControlThread来开关硬件vsync
    SurfaceFlinger::enableHardwareVsync() => mEventControlThread->setVsyncEnabled(true);

DispSyncThread 信号的是如何产生?

DispSync像是一个PLL, 它的 input 信息是硬件的VSYNC的时间, 它的 feedback 是Present Fence的时间, 它的输出就是SW_VSYNC, 以及SW_VSYNC再根据SF與APP的phase offset做调整, 分別输出VSYNC-sf与VSYNC-app.
总之:

  1. app和SurfaceFlinger真正使用的vsync属于sw vsync,这两个vsync是根据HWC vsync推算出来,这两者有一定相位偏移,原因应该是防止app和SurfaceFlinger同时被唤醒进行绘制和显示,造成争夺CPU,这叫做Vsync虚拟化。app 的vsync会先得到响应。
  2. 由于有CPU延时响应可能造成sw vsync超出某个阈值,导致需要HWC vsync校正,此时才会再次打开HWC Vsync。


1, 在sf类中声明一个成员变量:

  1. class SurfaceFlinger
  2. {
  3. DispSync mPrimaryDispSync;
  4. }

(2) 构造函数里启动DispSyncThread线程:

  1. DispSync::DispSync() {
  2. mThread = new DispSyncThread();
  3. mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
  4. reset();
  5. beginResync();
  6. ...
  7. }

(3) 进入DispSyncThread::threadLoop(): 计算vsync周期:

  • 调用所有存在mEventListeners的Listener的callback,fireCallbackInvocations()方法中调用callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime); mCallback的类型为DispSync::Callback
  • 也就是说,SurfaceFlinger调用DispSync的updateModel函数,唤醒DispSyncThread。DispSyncThread通过fireCallbackInvocations函数,告诉DispSyncSource有VSYNC了。
  • DispSyncSource作为DispSyncThread与EventThread的桥梁,会唤醒EventThread存下vsync。EventThread的threadLoop唤醒后,通过postEvent,向所有注册过的connection发送VSYNC消息。

(4) mEventListeners的注册通过调用DispSync::addEventListener()
(5) 谁注册成为了vsync事件的监听者? 是DispSyncSource
DispSyncSource继承自DispSync::Callback, 在它setVSysncEnabled(true)中调用:

  1. status_t err = mDispSync->addEventListener(mPhaseOffset,
  2. static_cast<DispSync::Callback*>(this)); //DispSyncSource把自己设为了DispSyncThread vsync信号的监听者
  3. 所以vsync消息传到了DispSyncSource中的onDispSyncEvent(nsecs_t when)中:
  4. virtual void onDispSyncEvent(nsecs_t when) {
  5. sp<VSyncSource::Callback> callback;
  6. {
  7.   Mutex::Autolock lock(mMutex);
  8.   callback = mCallback;
  9.   if (mTraceVsync) {
  10.   mValue = (mValue + 1) % 2;
  11.   ATRACE_INT("VSYNC", mValue);
  12.   }
  13. }
  14. if (callback != NULL) {
  15.   callback->onVSyncEvent(when); //又把vsync事件传递到了它自己的mCallback里, 这个mCallback 在EventThread::enableVSyncLocked()中设置, 就是EventThread, 这样vsync传递到了EventThread里
  16. }
  17. }

(6)继续看谁又调用了DispSyncSource::setVSyncEnabled()?
是EventThread::enableVSyncLocked():

  1. void EventThread::enableVSyncLocked() {
  2. if (!mUseSoftwareVSync) {
  3. // never enable h/w VSYNC when screen is off
  4.   if (!mVsyncEnabled) {
  5.   mVsyncEnabled = true;
  6.   mVSyncSource->setCallback(static_cast<VSyncSource::Callback*>(this)); //EventThread设为DispSyncSource的callback
  7.   mVSyncSource->setVSyncEnabled(true);
  8.   mPowerHAL.vsyncHint(true);
  9. }
  10. }
  11. mDebugVsyncEnabled = true;
  12. }

(7)EventThread::enableVSyncLocked()又被EventThread::waitForEvent()调用:

  1. Vector< sp<EventThread::Connection> > EventThread::waitForEvent(
  2. DisplayEventReceiver::Event* event)
  3. {
  4. ...
  5. // Here we figure out if we need to enable or disable vsyncs
  6. if (timestamp && !waitForVSync) {
  7. // we received a VSYNC but we have no clients
  8. // don't report it, and disable VSYNC events
  9. disableVSyncLocked();
  10. } else if (!timestamp && waitForVSync) {
  11. // we have at least one client, so we want vsync enabled
  12. // (TODO: this function is called right after we finish
  13. // notifying clients of a vsync, so this call will be made
  14. // at the vsync rate, e.g. 60fps. If we can accurately
  15. // track the current state we could avoid making this call
  16. // so often.)
  17. enableVSyncLocked();
  18. }
  19. ...
  20. }

waitForEvent()的逻辑是:
接到vsync信号,但是当前EventThread中没有请求 vsync 的connection, EventThread向下不再监听vsync
EventThread中有请求 vsync 的connection, EventThread继续监听vsync

总结: vsync传递路径 DispSyncThread => DispSyncSource => EventThread
DispSyncSource: 这个类比较简单,其实就是vsync传递者, 同时负责传递相位偏移phase offset到dispsyncThread。
EventThread: 负责接收vsync, 分发给sf或者app

所以在Sf init中的代码就比较好理解了

  1. // start the EventThread
  2. sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
  3. vsyncPhaseOffsetNs, true);
  4. mEventThread = new EventThread(vsyncSrc);
  5. sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
  6. sfVsyncPhaseOffsetNs, false);
  7. mSFEventThread = new EventThread(sfVsyncSrc);
  8. //上面代码注册了两个vsync信号的监听者, 当vsync产生时,分发给EventThread,这里有两个,一个处理sf,一个处理app
  9. mEventQueue.setEventThread(mSFEventThread); //
  10. mEventControlThread = new EventControlThread(this);
  11. mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY);

两个EventThread 线程如何把vsync分发给 SurfaceFlinger 和app?

1,vsync的分发:

1,app与EventThread
2,sf与EventThread

2,app与EventThread:
EventThread里有一个 class Connection : public BnDisplayEventConnection,

BnDisplayEventConnection 继承自 IDisplayEventConnection
这个接口实现了:
getDataChannel()
setVsyncRate()
requestNextVsync()
App端请求vsync:
app端通过DisplayEventReceiver 中的sp mEventConnection 来requestNextVsync
mEventConnection实际上是BpDisplayEventConnection, 所以requestNextVsync是通过Binder通信从app端传递到EventThread并由它来处理。
同时通过mEventConnection->getDataChannel(); 保存一个BitTube类型的mDataChannel。
在EventThread端:
app调用createEventConnection创建DisplayEventConnection时,EventThread将这个connection注册到它的mDisplayEventConnections集合里
当vsync过来时,通知所有connection
App端接受vsync是通过建立连接时保存的那个BitTube:

  1. //file:android_view_DisplayEventReceiver.cpp
  2. status_t NativeDisplayEventReceiver::initialize() {
  3.   status_t result = mReceiver.initCheck();
  4.   f (result) {
  5.     ALOGW("Failed to initialize display event receiver, status=%d", result);
  6.     return result;
  7.   }
  8.   int rc = mMessageQueue->getLooper()->addFd(mReceiver.getFd(), 0, ALOOPER_EVENT_INPUT, // 监听BitTube的fd,有数据时,调用handler
  9.   this, NULL);
  10.   if (rc < 0) {
  11.     return UNKNOWN_ERROR;
  12.   }
  13.   return OK;
  14. }

所以app接受vsync信号是通过监听BitTube的fd, BitTube底层是通过socket实现。

每个app创建时都会new一个RenderThread,用于管理UI的画图。如同上面讲的4个线程, 这个RenderThread也是继承Thread类,当我们调用它的成员函数run的时候,就会创建一个新的线程。这个新的线程的入口点函数为RenderThread类的成员函数threadLoop。
RenderThread类有一个成员变量mQueue,描述的是一个Task Queue。这个Queue用于保存所有需要执行的Task,每一个Task都是用一个RenderTask对象来描述。同时,RenderTask类有一个成员变量mRunAt,用来表明Task的执行时间。这样,保存在Task Queue的Task就可以按照执行时间从先到后的顺序排序,这个mRunAt最终会存VSYNC。于是,RenderThread类的成员函数nextTask通过判断排在队列头的Task的执行时间是否小于等于当前时间,就可以知道当前是否有Task需要执行。如果有Task需要执行的话,就将它返回给调用者。我们可以把Task当做是画图任务。
RenderThread 初始化的时候,会new DisplayEventReceiver。
DisplayEventReceiver就是APP接收Vsync的接口。 DisplayEventReceiver在构造函数里会调用surfaceflinger去注册一个connection;APP与SurfaceFlingger是两个进程,这里DisplayEventReceiver调用surfaceflinger注册Connection的过程是一个远程调用,由Binder实现。 Connection类是Binder服务端的实现,继承了BnDisplayEventConnection;在APP里面的Binder引用对象实现了IDisplayEventConnection接口。
Connection类里面有一个重要的成员mChannel,它是BitTube类。BitTube是啥? 字面意思就是字节管道。如果看它的构造函数,就能发现,其实它就是包了socket,用于进程间通信。
DisplayEventReceiver构造函数,就首先会调用surfaceflinger去注册一个connection,connection又会创建BitTube。客户端便可以通过getDataChannel()获得这个BitTube。
那么进程通信就可以开始了。
surfaceflinger进程的EventThread把VSYNC不断的往BitTube写(想象成往socket写),而app进程将会创建DisplayEventReceiver,该app进程又可以不断的读BitTube(想象成读socket)。这样,VSYNC就可以传输了

3, sf与EventThread
SurfaceFlinger::init()中调用mEventQueue.setEventThread(mSFEventThread);

  1. void MessageQueue::setEventThread(const sp<EventThread>& eventThread)
  2. {
  3.   mEventThread = eventThread;
  4.   mEvents = eventThread->createEventConnection();
  5.   mEventTube = mEvents->getDataChannel();
  6.   mLooper->addFd(mEventTube->getFd(), 0, ALOOPER_EVENT_INPUT,
  7.   MessageQueue::cb_eventReceiver, this);
  8. }
  • SurfaceFlinger 自己创建了一个Connection类,这个类就是传递Vsync信息的媒介。 其实就注册了一个事件的监听者,得到了发送VSYNC事件通知的BitTube,然后监控这个BitTube中的套接字,并且指定了收到通知后的回调函数。
  • SurfaceFlinger接收处理Vsync的过程大致为: HWComposer HAL通过callback函数,把VSYNC信号传给DispSyncThread,DispSyncThread传给EventThread。在上面的flow中,SurfaceFlinger通过onVsyncReceived拿到HWC Vsync之后,会利用DiscSync::addResyncSample函数判断是否还需要HWC Vsync作为输入,不需要的话就会使用EventControlThread关掉HWC Vsync。
  • DiscSync::addResyncSample就是对Vsync的处理过程的开始,具体的过程是在DiscSync::updateModelLocked里面实现。因此DiscSync这个类的作用就是 SurfaceFlinger 用来处理Vsync的。

    4)vsynce与SurfaceFlinger相关的4个线程:

  • 1个EventControlThread,用于开始/关闭 HWComposer HAL的VSYNC。

  • 1个是DispSyncThread,用于接收HWComposer HAL的VSYNC信号,并分发给EventThread。
  • 2个EventThread,EventThread类主要是接收DispSyncThread发来的VSYNC,并分发给真正使用的Client。

5) VSync的过程:

  • 开启Vysnc:

public static void startVsync( Activity act ) {
act.runOnUiThread( new Thread()
{
@Override
public void run()
{
// Look this up now, so the callback (which will be on the same thread)
// doesn’t have to.
choreographerInstance = Choreographer.getInstance();

  1. // Make sure we never get multiple callbacks going.<br /> _choreographerInstance_.removeFrameCallback(_handler_);
  2. // Start up our vsync callbacks.<br /> _choreographerInstance_.postFrameCallback(_handler_);<br /> }<br /> });<br /> <br />}
  • 停止Vsync

// It is important to stop the callbacks when the app is paused,
// because they consume several percent of a cpu core!
public static void stopVsync( Activity act ) {
// This may not need to be run on the UI thread, but it doesn’t hurt.
act.runOnUiThread( new Thread()
{
@Override
public void run()
{
choreographerInstance.removeFrameCallback(handler);
}
});
}

  • 执行Frame

��public void doFrame(long frameTimeNanos) {
nativeVsync(frameTimeNanos);
choreographerInstance.postFrameCallback(this);
}

/*
This callback imitates the actual rendering loop in Android. Since the 2D Activities are effectively
invisible, we post for frame callbacks within the rendering loop so we can do the drawing manually
in conjunction with our own rendering
*/
_protected Choreographer.FrameCallback frameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos)
{
if( projectedView != null )
{
if( _anyDirtyViews
( (ViewGroup) projectedView ) )
{
refreshActivityDialog( projectedView );
}

Choreographer.getInstance().postFrameCallbackDelayed( this, 1000 / 60 );
}
}};

public void startFlatActivityForResult(Intent intent, int requestCode)
{
String classId = intent.getComponent().getClassName();
Window win = getLocalActivityManager().startActivity(
classId, intent);
projectedView = win.getDecorView();

if( projectedView != null )
{
Activity activity = getLocalActivityManager().getActivity( classId );
createActivityDialog( projectedView, activity );
Choreographer.getInstance().postFrameCallback( frameCallback );
}
}

@Override
protected void onPause() {
Log.d(TAG, this + “ onPause()”);

// Shutdown 2D UI rendering when we’re backgrounded
Choreographer.getInstance().removeFrameCallback( frameCallback );

if( getApplication() instanceof VrApplication )
{
VrApplication vrApp = (VrApplication)getApplication();
vrApp.setHostActivity( null );
}

super.onPause();
nativePause(appPtr);
}

��@Override
protected void onResume() {
Log.d(TAG, this + “ onResume()”);

// Resume 2D UI rendering when we come back to the foreground
if( projectedView != null )
Choreographer.getInstance().postFrameCallback( frameCallback );

if( getApplication() instanceof VrApplication )
{
VrApplication vrApp = (VrApplication)getApplication();
vrApp.setHostActivity( this );
}

super.onResume();
nativeResume(appPtr);
}