Android 13 VSYNC重学习

Android 13 VSYNC重学习

引言

学无止境,一个字干就完事!

源码参考基于Android 13 aosp!


一. Android VSync模块开胃菜

在开始正式的分析之前,我们先简单对Android的Vsync模块简单介绍下,如下图所示,其中:

  • HW_VSync是由屏幕产生的脉冲信号,用于控制屏幕的刷新
  • VSync-app和VSync-sf统称为软件VSync,它们是由SurfaceFlinger通过模拟硬件VSync而产生的VSync信号量,再分发给app和sf用来控制它们的合成节奏

二. Android VSync小结

这里有几点需要补充:

  • VSync-sf是没有对应的EventThread和DispSyncSource

  • VSync-app和VSync-appSf各自都有对应的EventThread和DispSyncSource

  • VSync-sf和VSync-app以及Sync-appSf通过Scheduler的成员mVsyncSchedule指向的VSyncDispatchTimerQueue实例对象关联

Android下VSync设计,牵涉的核心关系图如下:

2.1 VSync信号的分类

VSync信号分为两种:硬件VSync信号HW-VSync和软件VSync信号SW-VSync。SW-VSync信号由SW-VSync模型产生。HW-VSync信号负责对SW-VSync模型进行校准。

2.2 HW-Vsync信号的开启

三种场景下会开启硬件VSync信号HW-VSync会对软件VSync信号SW-VSync进行校准

  • SurfaceFlinger初始化。

  • 连续两次请求VSync-app信号的时间间隔超过750ms。

  • SurfaceFlinger合成后,添加FenceTime到VSyncTracker中导致模型计算误差过大。

2.3 SW-VSync模型与计算

谷歌官方采用一元线性回归分析预测法(最小二乘法),通过采样的HW-VSync信号样本(屏幕刷新率),计算对应的SW-VSync信号周期。最终得到一条y=bx+a的拟合曲线。其中,b称为回归系数,a称为截距。SW-VSync模型就是这这条曲线的回归系数和截距。

2.4 SW-VSync信号的分类

SW-VSync信号也分为两种,VSync-sf信号和Vsync-app信号。这两个信号,各司其职:

  • VSync-sf信号用于控制SurfaceFlinger的Layer合成
    • VSync-app信号用于控制App渲染UI

VSync-sf信号和VSync-app信号是在SW-VSync信号的基础上通过叠加不同的偏移量产生,这些偏移量被称为VSync相位偏移。由于偏移量不同VSync-sf信号和VSync-app信号的回调时机也不同。


三. VSync-sf的申请和分发

VSync-sf用于控制SurfaceFlinger合成和渲染一帧图像。当SurfaceFlinger上帧时(BufferQueue中有新的GraphicBuffer),SurfaceFlinger会触发MessageQueue的scheduleFrame方法。接下来我们看下,VSync-sf是如何完成从申请到分发的流程。

3.1 VSync-sf的申请

c 复制代码
SurfaceFlinger::scheduleCommit(...)//请求上帧
    mScheduler->scheduleFrame()//MessageQueue.cpp
        mVsync.registration->schedule()//这里的registration实现是VSyncCallbackRegistration,定义在Scheduler/VSyncDispatchTimerQueue.cpp
            mDispatch.get().schedule()//这里的mDispatch指向VSyncDispatchTimerQueue对象


/**
 * @brief 
 * 
 * @param token 
 * @param scheduleTiming 
 * @return ScheduleResult 
 * 1)根据CallbackToken找到所有满足要求的VSyncDispatchTimerQueueEntry。VSyncDispatchTimerQueueEntry是VSyncDispatchTimerQueue中对外部VSync信号请求的封装。
 * 2)遍历调用VSyncDispatchTimerQueue的schedule方法,计算下一次VSync信号的发送时间。
 * 3)对发射时间进行定时,等待下一次VSync信号的发送
 */
ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
                                                 ScheduleTiming scheduleTiming) {
              ...
              //根据CallbackToken找到所有满足要求的VSyncDispatchTimerQueueEntry。VSyncDispatchTimerQueueEntry是VSyncDispatchTimerQueue中对外部VSync信号请求的封装。
              auto it = mCallbacks.find(token);
              auto& callback = it->second;
              
              //遍历调用VSyncDispatchTimerQueue的schedule方法,计算下一次VSync信号的发送时间
              result = callback->schedule(scheduleTiming, mTracker, now);
              
              //对发射时间进行定时,等待下一次VSync-sf信号的发送
              rearmTimerSkippingUpdateFor(now, it);
                VSyncDispatchTimerQueue::setTimer()

void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t /*now*/) {
    mIntendedWakeupTime = targetTime;
    mTimeKeeper->alarmAt(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
                         mIntendedWakeupTime);
    mLastTimerSchedule = mTimeKeeper->now();
}     



/**
 * @brief 
 * 1)遍历CallbackMap找到达到唤醒时间的VSyncDispatchTimerQueueEntry,并封装成Invocation,加入Invocation列表。
 * 2)遍历Invocation列表,通过Invocation获取VSyncDispatchTimerQueueEntry,并调用VSyncDispatchTimerQueueEntry的callback方法分发VSync信号。
 */
 //Scheduler/VSyncDispatchTimerQueue.cpp
void VSyncDispatchTimerQueue::timerCallback() {
    struct Invocation {
        std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
        nsecs_t vsyncTimestamp;
        nsecs_t wakeupTimestamp;
        nsecs_t deadlineTimestamp;
    };
    std::vector<Invocation> invocations;
    {
        std::lock_guard lock(mMutex);
        auto const now = mTimeKeeper->now();
        mLastTimerCallback = now;
        for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
            auto& callback = it->second;
            auto const wakeupTime = callback->wakeupTime();
            if (!wakeupTime) {
                continue;
            }

            auto const readyTime = callback->readyTime();

            auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0));
            if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {
                callback->executing();
                invocations.emplace_back(Invocation{callback, *callback->lastExecutedVsyncTarget(),
                                                    *wakeupTime, *readyTime});
            }
        }

        mIntendedWakeupTime = kInvalidTime;
        rearmTimer(mTimeKeeper->now());
    }

    for (auto const& invocation : invocations) {
        invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp,
                                      invocation.deadlineTimestamp);
    }
}

}
            

3.2 VSync-sf的分发

那么VSync-df的callback是怎么注册到VSyncDispatchTimerQueue的呢,这个我们看下:

c 复制代码
SurfaceFlinger::initScheduler(...)
    mScheduler->initVsync(...)//实现在Scheduler/MessageQueue.cpp中
        mVsync.registration = std::make_unique<
            scheduler::VSyncCallbackRegistration>(dispatch,
                                                  std::bind(&MessageQueue::vsyncCallback, this,
                                                            std::placeholders::_1,
                                                            std::placeholders::_2,
                                                            std::placeholders::_3),
                                                  "sf");//这里的dispatch指向VSyncDispatchTimerQueue
 
 //Scheduler/VSyncDispatchTimerQueue.cpp
 VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
                                                     VSyncDispatch::Callback callback,
                                                     std::string callbackName)
      : mDispatch(dispatch),
        mToken(dispatch.registerCallback(std::move(callback), std::move(callbackName))),
        mValidToken(true) {}       
        

VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
        Callback callback, std::string callbackName) {
    std::lock_guard lock(mMutex);
    return CallbackToken{//最终注册到了mCallbacks中
            mCallbacks
                    .emplace(++mCallbackToken,
                             std::make_shared<VSyncDispatchTimerQueueEntry>(std::move(callbackName),
                                                                            std::move(callback),
                                                                            mMinVsyncDistance))
                    .first->first};
}        

所以最后VSync-sf的分发会调用到MessageQueue::vsyncCallback中,我们看下它的实现:

c 复制代码
//Scheduler/MessageQueue.cpp
MessageQueue::vsyncCallback(...)
    mHandler->dispatchFrame(vsyncId, vsyncTime)
        mQueue.mLooper->sendMessage(this, Message())
        
        
//Handle的handleMessage接收前面发过来的消息
void MessageQueue::Handler::handleMessage(const Message&) {
    mFramePending.store(false);

    const nsecs_t frameTime = systemTime();
    auto& compositor = mQueue.mCompositor;//这里的compositor实现类是SurfaceFlinger

    if (!compositor.commit(frameTime, mVsyncId, mExpectedVsyncTime)) {
        return;
    }

    compositor.composite(frameTime, mVsyncId);
    compositor.sample();
}

四. VSync-app的申请和分发

在开始后续的章节编写前,我们先重点申明下:

VSync-app用于控制App的UI渲染

VSync-app用于控制App的UI渲染

VSync-app用于控制App的UI渲染

4.1 VSync-app的申请

当Choreographer通过FrameDisplayEventReceiver调用scheduleVsync方法时,会触发VSync-app信号的申请。在FrameDisplayEventReceiver的scheduleVsync方法中,会调用nativeScheduleVsync方法。

FrameDisplayEventReceiver的nativeScheduleVsync方法对应的native实现为android_view_DisplayEventReceiver的nativeScheduleVsync函数。

在nativeScheduleVsync函数中,主要做了两件事:

  • 获取native层的DisplayEventDispatcher。

  • 调用DisplayEventDispatcher的scheduleVsync方法,请求VSync信号。

在DisplayEventDispatcher的scheduleVsync方法中,会调用DisplayEventReceiver的requestNextVsync方法。

在DisplayEventReceiver的requestNextVsync方法中,会调用IDisplayEventConnection的requestNextVsync方法。

IDisplayEventConnection是一个Binder类,对应bn端的实现类为BnDisplayEventConnection。而EventThreadConnection继承自BnDisplayEventConnection,因此实际调用的是EventThreadConnection的requestNextVsync方法。

在EventThreadConnection的requestNextVsync方法中,会调用EventThread的requestNextVsync方法。

在EventThread的requestNextVsync方法中,主要做了三件事:

  • 开启硬件VSync信号对软件VSync信号进行校准。

  • 标记EventThreadConnection的vsyncRequest,为后续信号分发做准备。

  • 唤起EventThread对应的线程继续执行VSync信号的分发。

c 复制代码
//Scheduler/EventThread.cpp
void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection) {
    if (connection->resyncCallback) {
        /**
         * @brief 
         * 调用到Scheduler::resync
         * 开启硬件Vsync信号对软件Vsync信号进行校准
         */
        connection->resyncCallback();
    }

    std::lock_guard<std::mutex> lock(mMutex);

    if (connection->vsyncRequest == VSyncRequest::None) {
        connection->vsyncRequest = VSyncRequest::Single;
        mCondition.notify_all();//唤起EventThread中的线程
    } else if (connection->vsyncRequest == VSyncRequest::SingleSuppressCallback) {
        connection->vsyncRequest = VSyncRequest::Single;
    }
}

在EventThread的threadMain中,会通过VSyncCallbackRegistration请求或取消VSync信号。

如果是请求VSync信号,会调用VSyncCallbackRegistration的schedule方法。在VSyncCallbackRegistration的schedule方法,会调用VSyncDispatch的schedule方法。

c 复制代码
void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {

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

            mState = nextState;
        }
    
}

之后的流程与VSync-sf信号的申请流程相同。在VSyncDispatchTimerQueue的schedule方法中,会调用scheduleLocked方法。

在VSyncDispatchTimerQueue的scheduleLocked方法中,主要做了三件事:

  • 根据CallbackToken找到所有满足要求的VSyncDispatchTimerQueueEntry。VSyncDispatchTimerQueueEntry是VSyncDispatchTimerQueue中对外部VSync信号请求的封装。

  • 遍历调用VSyncDispatchTimerQueue的schedule方法,计算下一次VSync信号的发送时间。

  • 对发射时间进行定时,等待下一次VSync信号的发送。

4.2 VSync-app的分发

当定时时间到达时,TimerKeeper会回调VSyncDispatchTimerQueue的timerCallback方法。

在VSyncDispatchTimerQueue的timerCallback方法方法中,主要做了两件事:

  • 遍历CallbackMap找到达到唤醒时间的VSyncDispatchTimerQueueEntry,并封装成Invocation,加入Invocation列表。

  • 遍历Invocation列表,通过Invocation获取VSyncDispatchTimerQueueEntry,并调用VSyncDispatchTimerQueueEntry的callback方法分发VSync信号。

在VSyncDispatchTimerQueueEntry的callback方法中,会调用类型为CallbackRepeater::callbackk,然后在该方法中接着调用mCallback(vsyncTime, wakeupTime, readyTime)方法,而这里的mCallback(指向DispSyncSource::onVsyncCallback,最后回调EventThread的onVSyncEvent方法。

对于上述的分发流程是不是还有点懵逼,我们反过来看看VSync-app分发的注册,其核心是DispSyncSource和EventThread以及VSyncDispatchTimerQueue的各种回调callback流程:

c 复制代码
//Scheduler/VSyncDispatchTimerQueue.cpp
VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
        Callback callback, std::string callbackName) {
    std::lock_guard lock(mMutex);
    return CallbackToken{
            mCallbacks
                    .emplace(++mCallbackToken,
                             std::make_shared<VSyncDispatchTimerQueueEntry>(std::move(callbackName),
                                                                            std::move(callback),
                                                                            mMinVsyncDistance))
                    .first->first};
}

//Scheduler/VSyncDispatchTimerQueue.cpp
VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
                                                     VSyncDispatch::Callback callback,
                                                     std::string callbackName)
      : mDispatch(dispatch),
        mToken(dispatch.registerCallback(std::move(callback), std::move(callbackName))),
        mValidToken(true) {}


//Scheduler/DispSyncSource.cpp
class CallbackRepeater {
public:
    CallbackRepeater(VSyncDispatch& dispatch, VSyncDispatch::Callback cb, const char* name,
                     std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
                     std::chrono::nanoseconds notBefore)
          : mName(name),
            mCallback(cb),
            //VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex);
            mRegistration(dispatch,
                          std::bind(&CallbackRepeater::callback, this, std::placeholders::_1,
                                    std::placeholders::_2, std::placeholders::_3),
                          mName),
            mStarted(false),
            mWorkDuration(workDuration),
            mReadyDuration(readyDuration),
            mLastCallTime(notBefore) {}

    ~CallbackRepeater() {
        std::lock_guard lock(mMutex);
        mRegistration.cancel();
    }

    void start(std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) {
        std::lock_guard lock(mMutex);
        mStarted = true;
        mWorkDuration = workDuration;
        mReadyDuration = readyDuration;

        auto const scheduleResult = 
                mRegistration.schedule({.workDuration = mWorkDuration.count(),
                                        .readyDuration = mReadyDuration.count(),
                                        .earliestVsync = mLastCallTime.count()});
        LOG_ALWAYS_FATAL_IF((!scheduleResult.has_value()), "Error scheduling callback");
    }

    void stop() {
        std::lock_guard lock(mMutex);
        LOG_ALWAYS_FATAL_IF(!mStarted, "DispSyncInterface misuse: callback already stopped");
        mStarted = false;
        mRegistration.cancel();
    }

    void dump(std::string& result) const {
        std::lock_guard lock(mMutex);
        const auto relativeLastCallTime =
                mLastCallTime - std::chrono::steady_clock::now().time_since_epoch();
        StringAppendF(&result, "\t%s: ", mName.c_str());
        StringAppendF(&result, "mWorkDuration=%.2f mReadyDuration=%.2f last vsync time ",
                      mWorkDuration.count() / 1e6f, mReadyDuration.count() / 1e6f);
        StringAppendF(&result, "%.2fms relative to now (%s)\n", relativeLastCallTime.count() / 1e6f,
                      mStarted ? "running" : "stopped");
    }

private:
    void callback(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) {
        {
            std::lock_guard lock(mMutex);
            mLastCallTime = std::chrono::nanoseconds(vsyncTime);
        }

        mCallback(vsyncTime, wakeupTime, readyTime);

        {
            std::lock_guard lock(mMutex);
            if (!mStarted) {
                return;
            }
            auto const scheduleResult =
                    mRegistration.schedule({.workDuration = mWorkDuration.count(),
                                            .readyDuration = mReadyDuration.count(),
                                            .earliestVsync = vsyncTime});
            LOG_ALWAYS_FATAL_IF(!scheduleResult.has_value(), "Error rescheduling callback");
        }
    }

    const std::string mName;
    scheduler::VSyncDispatch::Callback mCallback;

    mutable std::mutex mMutex;
    VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex);
    bool mStarted GUARDED_BY(mMutex) = false;
    std::chrono::nanoseconds mWorkDuration GUARDED_BY(mMutex) = 0ns;
    std::chrono::nanoseconds mReadyDuration GUARDED_BY(mMutex) = 0ns;
    std::chrono::nanoseconds mLastCallTime GUARDED_BY(mMutex) = 0ns;
};



mAppConnectionHandle =
            mScheduler->createConnection("app" .....)
            Scheduler::createConnection()
                auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration)
                    return std::make_unique<scheduler::DispSyncSource>(mVsyncSchedule->getDispatch(),
                                                       mVsyncSchedule->getTracker(), workDuration,
                                                       readyDuration, traceVsync, name);
                        //std::unique_ptr<CallbackRepeater> mCallbackRepeater;
                        mCallbackRepeater =
                                std::make_unique<CallbackRepeater>(vSyncDispatch,
                                                                   std::bind(&DispSyncSource::onVsyncCallback, this,
                                                                             std::placeholders::_1,
                                                                             std::placeholders::_2,
                                                                             std::placeholders::_3),
                                                                   name, workDuration, readyDuration,
                                                                   std::chrono::steady_clock::now().time_since_epoch());      
                mVSyncSource->setCallback(this);//为DispVsyncSource设置回调
                     void DispSyncSource::setCallback(VSyncSource::Callback* callback) {
                        std::lock_guard lock(mCallbackMutex);
                        mCallback = callback;
                    }               
                    
 //最终整理出来的Vsync-app分发流程为,各种弯弯绕绕:
 VSyncDispatchTimerQueue::timerCallback()//Scheduler/VSyncDispatchTimerQueue.cpp
               invocation.callback->callback(...)//这里的callback指向VSyncDispatchTimerQueueEntry::callback,Scheduler/VSyncDispatchTimerQueue.cpp
                    mCallback(vsyncTimestamp, wakeupTimestamp, deadlineTimestamp)//这里的 mCallback指向CallbackRepeater::callback,实现在Scheduler/DispSyncSource.cpp 
                        mCallback(vsyncTime, wakeupTime, readyTime)//这里的callback指向DispSyncSource::onVsyncCallback。是现在Scheduler/DispSyncSource.cpp
                            callback = mCallback;
                            callback->onVSyncEvent(targetWakeupTime, {vsyncTime, readyTime})//这里的callback指向EventThread::onVSyncEvent

在EventThread的onVSyncEvent方法中,主要做了三件事:

  • 调用makeVSync函数,创建Event。

  • 将Event加入到vector<DisplayEventReceiver::Event> 中。

  • 唤醒等待线程,执行threadMain方法。

c 复制代码
void EventThread::onVSyncEvent(nsecs_t timestamp, VSyncSource::VSyncData vsyncData) {
    std::lock_guard<std::mutex> lock(mMutex);

    LOG_FATAL_IF(!mVSyncState);
    //包装为DisplayEventReceiver::Event对象,存入mPendingEvents尾部
    mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
                                       vsyncData.expectedPresentationTime,
                                       vsyncData.deadlineTimestamp));
    //唤醒线程
    mCondition.notify_all();
}

我们接下来看EventThread是如何处理分发事件的:

c 复制代码
//Scheduler/EventThread.cpp
void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
    DisplayEventConsumers consumers;

    while (mState != State::Quit) {
        std::optional<DisplayEventReceiver::Event> event;
        // Determine next event to dispatch.
        if (!mPendingEvents.empty()) {
            event = mPendingEvents.front();
            mPendingEvents.pop_front();        
            ...
        }
        
        // Find connections that should consume this event.
        auto it = mDisplayEventConnections.begin();
        while (it != mDisplayEventConnections.end()) {
            if (const auto connection = it->promote()) {
                vsyncRequested |= connection->vsyncRequest != VSyncRequest::None;
                //用来在任务的循环执行中保存当前Vsync信号的消费者
                if (event && shouldConsumeEvent(*event, connection)) {
                    consumers.push_back(connection);//这里的consumers就是待分发的目标
                }

                ++it;
            } else {
                it = mDisplayEventConnections.erase(it);
            }
        }        

        /**
         * @brief 
         * 在该方法中,会循环分发信号,主要做了五件事情
         * 1) 从Vsync信息队列中获取消息
         * 2)收集监听Vsync信号的EventThreadConnection,并加入到consumers中
         * 3) 调用dispatchEvent方法来分发Vsync信号
         * 4)计算当前状态,根据状态请求或取消下一次VSync信号
         * 5)如果没有Vsync信号需要分发,线程进入等待状态
         */
        if (!consumers.empty()) {
            dispatchEvent(*event, consumers);
                consumer->postEvent(copy)
                    DisplayEventReceiver::sendEvents(...)
            consumers.clear();
        }        
        

最终VSync-app分发的事件会被Choreographer模块接收,开始安排应用相关的渲染UI逻辑!


Andoid SurfaceFlinger(二) VSYNC的开始,连续,结束
VSYNC研究-最后的窗户纸
Android 12(S) 图像显示系统 - SurfaceFlinger之VSync-上篇(十六)
Android 12(S) 图像显示系统 - SurfaceFlinger 之 VSync - 中篇(十七)
深度详解 Android S(12.0)屏幕刷新机制之 Choreographer
View绘制流程3-Vsync信号是如何发送和接受的
Android R Vsync相关梳理
显示框架之深入Vsync原理
App/Sf的Vsync部分源码流程结合perfetto/systrace分析
Android-View绘制原理(02)-VSync原理之SurfaceFlinger篇
一文搞定Android VSync来龙机制去脉
VSync信号系统与SurfaceFlinger
SurfaceFlinger-Vsync信号
Android VSync事件分发过程源码分析

相关推荐
每次的天空10 分钟前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭38 分钟前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日2 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安2 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑2 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟6 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡7 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi007 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体
zhangphil9 小时前
Android理解onTrimMemory中ComponentCallbacks2的内存警戒水位线值
android
你过来啊你9 小时前
Android View的绘制原理详解
android