Vsync与app、surfaceFlinger关系(1)

app要进行更新,必须先要请求一个vsync,等到下个vsync信号来的时候进行界面的更新。
一、首先先看一下app请求vsync。
我们熟知的TextView.setText 或者ImageView.setImageBitmap,最后都要执行invalidate申请重绘,最后会通过ViewParent递归到ViewRootImpl的invalidate。Choreographer.postCallback进行vsync信号的申请。最终胡通过IDisplayEventConnection.cpp 的requestNextVsync,调用到服务端EventThread的requestNextVsync();
流程图如下:

18.png

流程比较简单,最终就是调用了一个 mChoreographer.postCallback

frameworks/base/core/java/android/view/ViewRootImpl.java
      @UnsupportedAppUsage
      void scheduleTraversals() {
          if (!mTraversalScheduled) {
              mTraversalScheduled = true;
              mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); @1
              mChoreographer.postCallback(
                      Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); @2
              notifyRendererOfFramePending();
              pokeDrawLockIfNeeded();
          }
      }

@1 设置同步栅栏,拦截同步消息
@2 来请求请求vnsc垂直同步信号,

二、Choreograhper接下来的流程如下图:

19.png

看下几个重要方法
1、
scheduleFrameLocked可以被调用多次,但是mFrameScheduled保证下一个vsync到来之前,不会有新的请求发出,多余的scheduleFrameLocked调用被无效化

frameworks/base/core/java/android/view/Choreographer.java
private void scheduleFrameLocked(long now) {
    if (!mFrameScheduled) {   
        mFrameScheduled = true;
......
        if (ScrollOptimizer.shouldUseVsync(USE_VSYNC)) {
        ......
            if (isRunningOnLooperThreadLocked()) {
                scheduleVsyncLocked();
            } else {
                // 设置此message为异步消息,因为之前已经设置了同步屏障,所以异步消息会优先执行。
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtFrontOfQueue(msg);
            }
        }  
    }
}

2、
最终要的就是最后一步,在IDisplayEventConnection.cpp中的requestNextVsync方法

framworks/native/libs/gui/IDisplayEventConnection.cpp
      void requestNextVsync() override {
          callRemoteAsync<decltype(&IDisplayEventConnection::requestNextVsync)>(
                  Tag::REQUEST_NEXT_VSYNC);
      }
  };

这块CallRemoteAsync,异步binder调用到服务端,会执行服务端的onTransact方法,这个过程是夸进程的。

 status_t BnDisplayEventConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
                                                uint32_t flags) {
      if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
          return BBinder::onTransact(code, data, reply, flags);
      }
      auto tag = static_cast<Tag>(code);
      switch (tag) {
          case Tag::STEAL_RECEIVE_CHANNEL:
              return callLocal(data, reply, &IDisplayEventConnection::stealReceiveChannel);
          case Tag::SET_VSYNC_RATE:
              return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate);
          case Tag::REQUEST_NEXT_VSYNC: @1
              return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync);
      }
  }

在@1处会调用到 EventThreadConnection的 requestNextVsync。

21.png

很明显,BpDisplayEventConnection 的requestNextVsync 会binder调用到BnDisplayEventConnection的requestNextVsync。也就是夸进程,从app进程到surfaceFlinger进程。

看一下各自的位置
frameworks/native/libs/gui/DisplayEventReceiver.cpp

Client端:

  class BpDisplayEventConnection : public SafeBpInterface<IDisplayEventConnection> {
......
}  在frameworks/native/libs/gui/IDisplayEventConnection.cpp 中

server端:

class EventThreadConnection : public BnDisplayEventConnection {
.....
}  在frameworks/native/services/surfaceflinger/scheduler/eventThread.h 中

class BnDisplayEventConnection : public SafeBnInterface<IDisplayEventConnection> {
......
}  在frameworks/native/include/gui/IDisplayEventConnection.h

三、接下来看下服务端(EventThreadConnection)的requestNextVsync

void EventThreadConnection::requestNextVsync() {
      ATRACE_NAME("requestNextVsync");
      mEventThread->requestNextVsync(this);
  }

会调用EventThread的requestNextVsync

 void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection) {
      if (connection->resyncCallback) {
          connection->resyncCallback();
      }
  
      std::lock_guard<std::mutex> lock(mMutex);  
  
      if (connection->vsyncRequest == VSyncRequest::None) {   
          connection->vsyncRequest = VSyncRequest::Single;
          mCondition.notify_all();   @1
      }
  }

因为 threadMain中的循环在EventThread的构造方法中已经启动,此时@1出的notify_call就会唤醒其 mConditioni.wait(lock),进入下一次循环,这时候就会执行 mVSyncSource->setVsyncEnable(true);

 State nextState;
          if (mVSyncState && vsyncRequested) {
              nextState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync;
          } else {
              ALOGW_IF(!mVSyncState, "Ignoring VSYNC request while display is disconnected");
              nextState = State::Idle;
          }


          if (mState != nextState) {
              if (mState == State::VSync) {
                  mVSyncSource->setVSyncEnabled(false);
              } else if (nextState == State::VSync) {
                  mVSyncSource->setVSyncEnabled(true);    @2
              }
              mState = nextState;
          }

          if (event) {
              continue;
          }
  
          // Wait for event or client registration/request.
          if (mState == State::Idle) {
              mCondition.wait(lock);   @3
          } else {
              // Generate a fake VSYNC after a long timeout in case the driver stalls. When the
              // display is off, keep feeding clients at 60 Hz.
......

@3处就会等待下一次requestVsync信号过来进行唤醒,会进行下一次循环,经过一系列的操作,会执行 mVSyncSource->setVSyncEnabled(true);

frameworks/native/services/surfaceflinger/scheduler/DispSyncSource.cpp
void DispSyncSource::setVsyncEnabled(bool enable){
std::lock_gurad lock(mVsyncMutex);
If(enable){   @4
    status_t err = mDispSync->addEventlistener(mName,mPhaseOffset,static_cast<DispSync::Callback*>(this),mLastCallbackTime); @1
If(err != No_ERROE){
    ALOGE(“error registering vsync callback: %s (%d)”,strerror(-err),err);
}
}else{
   status_t err = mDispSync->removeEventlistener(mName,mPhaseOffset,static_cast<DispSync::Callback*>(this),mLastCallbackTime);
If(err != No_ERROE){
    ALOGE(“error unregistering vsync callback: %s (%d)”,strerror(-err),err);
}
  If(mDolphinCheck){
If(mDolphinCheck(mName)){
  status_t err = mDispSync->addEventlistener(mName,mPhaseOffset,static_cast<DispSync::Callback*>(this),mLastCallbackTime);
If(err != No_ERROE){
    ALOGE(“error registering vsync callback: %s (%d)”,strerror(-err),err);
}

}
}
}
mEnable = enable
}

在@4处 enable 为true的时候,会执行mDispSync->addEventListener注册一个vsync监听

system/native/services/surfaceflinger/scheduler/DispSync.cpp

    status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback,
                              nsecs_t lastCallbackTime) {
        if (mTraceDetailedInfo) ATRACE_CALL();
        Mutex::Autolock lock(mMutex);

        for (size_t i = 0; i < mEventListeners.size(); i++) {
            if (mEventListeners[i].mCallback == callback) {
                return BAD_VALUE;
            }
        }

        EventListener listener;
        listener.mName = name;
        listener.mPhase = phase;
        listener.mCallback = callback;

        // We want to allow the firstmost future event to fire without
        // allowing any past events to fire. To do this extrapolate from
        // mReferenceTime the most recent hardware vsync, and pin the
        // last event time there.
        const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        if (mPeriod != 0) {
            const nsecs_t baseTime = now - mReferenceTime;
            const nsecs_t numPeriodsSinceReference = baseTime / mPeriod;
            const nsecs_t predictedReference = mReferenceTime + numPeriodsSinceReference * mPeriod;
            const nsecs_t phaseCorrection = mPhase + listener.mPhase;
            const nsecs_t predictedLastEventTime = predictedReference + phaseCorrection;
            if (predictedLastEventTime >= now) {
                // Make sure that the last event time does not exceed the current time.
                // If it would, then back the last event time by a period.
                listener.mLastEventTime = predictedLastEventTime - mPeriod;
            } else {
                listener.mLastEventTime = predictedLastEventTime;
            }
        } else {
            listener.mLastEventTime = now + mPhase - mWakeupLatency;
        }

        if (lastCallbackTime <= 0) {
            // If there is no prior callback time, try to infer one based on the
            // logical last event time.
            listener.mLastCallbackTime = listener.mLastEventTime + mWakeupLatency;
        } else {
            listener.mLastCallbackTime = lastCallbackTime;
        }

        mEventListeners.push_back(listener);@1

        mCond.signal();

        return NO_ERROR;
}

传进来的参数name, phase, callback, lastCallbackTime 都会赋值给一个EventListener对象,然后@1处 存入一个vector类型的集合。这样app 的 vsync信号就算注册好了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容