精华内容
下载资源
问答
  • vsync_provider VsyncProvider为AnimationController提供vsync。 这适用于。 例子
  • SurfaceFlinger 视频教程 显示系统,内部机制,Vsync机制,HDMI系统
  • Vsync同步机制 一

    2021-06-06 12:57:42
    什么是Vsync同步机制?Vsync(垂直同步信号量),用来同步渲染,让AppUI和SurfaceFlinger可以按硬件产生的VSync节奏进行工作。Vsync要解决的问题:Vsync要解决的问题为什么会产生这样的问题?CPU负责对UI进行更新,GPU...

    什么是Vsync同步机制?

    Vsync(垂直同步信号量),用来同步渲染,让AppUI和SurfaceFlinger可以按硬件产生的VSync节奏进行工作。

    Vsync要解决的问题:

    bb61daded32000099e98f5bc7c6ce4db.png

    Vsync要解决的问题

    为什么会产生这样的问题?

    CPU负责对UI进行更新,GPU负责对UI进行渲染,两者的频率不一致,会导致CPU还未更新完成,就被GPU渲染到了屏幕上。所以会出现图片上的问题。

    如何解决这个问题?

    解决这个问题的方法就是让CPU和GPU以相同的节奏进行工作。如下图所示

    446ec71b23a46ba6f47e5bdb15cb33fe.png

    Vsync

    让CPU和GPU以相同的频率进行工作,这就是Vsync要做的工作。Vsync以固定的频率发出信号,每当收到CPU先对UI进行更新,然后GPU再进行绘制,这样就可以解决上面的问题了。

    那Android的Vsync机制是如何进行的呢?

    Android的Vsync整体框架图

    ad0c6129e116c9caa9c563b67c31401b.png

    disp_sync_arch.png

    这张图可以很明显的看出Vsync事件的传递过程。

    Vsync信号并不是有硬件直接产生,而是由DispSync线程产生的。DispSync会根据HWC产生的VSync进行采样,创建模型,然后输出了SW_VSYNC信号,SW_VSYNC再根据SF和APP的phase offset做调整,分别输出给Vsync-sf和Vsync-app。

    再看下几个类之间的关系图

    HWC产生硬件Vsync信号

    在分析HWComposer的时候,HWComposer中会注册硬件Vsync时间回调,在硬件Vsync时间到来的时候,回调HWComposer的vsync函数。

    void HWComposer::vsync(int disp, int64_t timestamp) {

    if (uint32_t(disp) < HWC_NUM_PHYSICAL_DISPLAY_TYPES) {

    {

    Mutex::Autolock _l(mLock);

    //记录对应硬件设备Vsync信号产生的时间

    mLastHwVSync[disp] = timestamp;

    }

    //然后直接通知SurfaceFlinger的onVsyncReceived函数

    mEventHandler.onVSyncReceived(disp, timestamp);

    }

    }

    void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {

    bool needsHwVsync = false;

    { // Scope for the lock

    Mutex::Autolock _l(mHWVsyncLock);

    //如果是主显示设备的Vsync信号,且当前硬件Vsync事件是打开的,则调用DispSync的addResyncSample函数

    //将硬件VSync事件添加到DispSYnc的样本中,用于创建软件Vsync时间模型

    if (type == 0 && mPrimaryHWVsyncEnabled) {

    needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);

    }

    }

    //更加样本采样结果,决定是否要关掉硬件Vsync事件。

    if (needsHwVsync) {

    enableHardwareVsync();

    } else {

    disableHardwareVsync(false);

    }

    }

    HWComposer接收到硬件的Vsync事件并没有直接传递给系统使用,而是通过SurfaceFlinger将Vsync事件,添加到了DispSync的Vsync事件样本,当DispSync采样完成后,则会停止硬件Vsync事件,由软件Vsync根据样本的计算结果产生Vsync事件。

    mPrimaryHWVsyncEnabled变量控制DispSync是否需要采集样本,当模型Vsync周期有误差时需要重新打开硬件Vsync,再次采集硬件Vsync样本。

    bool DispSync::addResyncSample(nsecs_t timestamp) {

    Mutex::Autolock lock(mMutex);

    //此处相当于做了一个大小为32环形Buffer,每当有新的样本过来之后就添加到数组Buffer中,如果Buffer已满,则替换掉最老的样本

    size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;

    mResyncSamples[idx] = timestamp;

    if (mNumResyncSamples < MAX_RESYNC_SAMPLES) {

    mNumResyncSamples++;

    } else {

    mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES;

    }

    //当样本更新后,根据32个硬件Vsync样本计算软件Vsync模型.

    updateModelLocked();

    if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) {

    resetErrorLocked();

    }

    //当Vsync周期mPeriod = 0或者误差超过一定的阀值,需要重新采样,否则,则停止采样,关掉硬件Vsync,知道有误差的时候在打开,再次采样

    return mPeriod == 0 || mError > kErrorThreshold;

    }

    addResyncSample方法主要作用是添加采样样本到Buffer中,DispSync中维护了一个环形的Buffer,大小为32个,每当有新样本过来时候,则将样本添加到Buffer中,如果Buffer已经满了,则替换掉最老的样本。

    样本更新后调用updateModelLocked来计算更新DispSync模型。

    void DispSync::updateModelLocked() {

    //只有样本数量大于3个的时候才会计算,太少则没有意义

    if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) {

    nsecs_t durationSum = 0;

    //第一步 :从最老的时间样本开始计算Vsync间隔,然后再计算平均的时间间隔

    for (size_t i = 1; i < mNumResyncSamples; i++) {

    size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;

    size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES;

    durationSum += mResyncSamples[idx] - mResyncSamples[prev];

    }

    //mPeriod就是计算产生的平均Vsync间隔的时间

    mPeriod = durationSum / (mNumResyncSamples - 1);

    //第二部:开始计算平均的偏差时间,因为不可能每个间隔都非常准时,要求平均偏差

    double sampleAvgX = 0;

    double sampleAvgY = 0;

    double scale = 2.0 * M_PI / double(mPeriod);

    for (size_t i = 0; i < mNumResyncSamples; i++) {

    //遍历并计算每个样本相对于平均周期取余的偏差值,(如果完全准时的样本,应该可以整除,余数为0),然后将偏差值转换成弧度。

    size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;

    nsecs_t sample = mResyncSamples[idx];

    double samplePhase = double(sample % mPeriod) * scale;

    //计算弧度的X和Y

    sampleAvgX += cos(samplePhase);

    sampleAvgY += sin(samplePhase);

    }

    //求平均弧度X,Y

    sampleAvgX /= double(mNumResyncSamples);

    sampleAvgY /= double(mNumResyncSamples);

    //将平均弧度转换成平均的偏差值

    mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);

    if (mPhase < 0) {

    mPhase += mPeriod;

    }

    if (kTraceDetailedInfo) {

    ATRACE_INT64("DispSync:Period", mPeriod);

    ATRACE_INT64("DispSync:Phase", mPhase);

    }

    // 人为减少Vsync刷新频率

    mPeriod += mPeriod * mRefreshSkipCount;

    //更新周期和偏差模型

    mThread->updateModel(mPeriod, mPhase);

    }

    }

    计算平均周期模型和平均偏差模型。

    如何计算平均周期?

    将所有样本的间隔时间相加,然后除以间隔数,求出平均间隔时间.

    如何计算平均偏差?

    如果完全准时的样本,应该可以整除平均,余数为0,有偏差的则余数不为0,所以对所有的样本除以mPeriod平均周期时间,计算余数,将余数又转换成弧度. 计算X和Y, 求X和Y的平均值,然后再由平均X,Y求出弧度,再转换成偏差值。这样就计算出平均偏差了.

    void updateModel(nsecs_t period, nsecs_t phase) {

    Mutex::Autolock lock(mMutex);

    mPeriod = period;

    mPhase = phase;

    mCond.signal();

    }

    调用VsyncThread的mCond.signal()通知VsyncThread模型更新完成。

    DispSync 软件Vsync模型

    SurfaceFlinger的Vsync并不是直接使用硬件产生Vsync,而是由软件根据硬件Vsync创建了一个数据模型来模拟产生Vsync信号。负责产生软件Vsync信号的就是DispSync。

    DispSync是SurfaceFlinger中的一个变量。

    DispSync mPrimaryDispSync;

    mPrimaryDispSync就是Vsync的信号源,我们先看下Vsync信号是如何产生的?

    DispSync::DispSync() :

    mRefreshSkipCount(0),

    mThread(new DispSyncThread()) {

    //启动VsyncThread

    mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);

    }

    DispSync VsyncThread 第一部分

    DispSync对象在创建的时候会启动一个VsyncThread线程,该线程用于模拟Vsync信号。然后我们看下VsyncThread线程的threadloop方法

    virtual bool threadLoop() {

    status_t err;

    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);

    nsecs_t nextEventTime = 0;

    //执行循环

    while (true) {

    Vector callbackInvocations;

    nsecs_t targetTime = 0;

    { // Scope for lock

    Mutex::Autolock lock(mMutex);

    //如果需要停止Vsync线程,则直接返回

    if (mStop) {

    return false;

    }

    //如果mPeriod为0, 则等待

    //在VsyncThread创建的时候,mPeriod默认为0,所以会阻塞在这边。

    //在VsyncThread updateModel更新模型的时候,会设置mPeriod,然后继续执行,线程刚开始的时候Vsync模型还没有创建好,所以无法产生SW Vsync信号

    if (mPeriod == 0) {

    err = mCond.wait(mMutex);

    if (err != NO_ERROR) {

    ALOGE("error waiting for new events: %s (%d)",

    strerror(-err), err);

    return false;

    }

    continue;

    }

    ......

    }

    VsyncThread刚开始由于mPeriod=0,也就是说当前线程还不知道按照什么样的频率产生Vsync信号,所以会等待mPeriod周期模型的更新。

    什么时候设置更新mPeriod的时间呢?

    就是上面提到的,应用模型样本计算完成后会设置mPeriod和mPhase,这样VsyncThread就会继续执行

    DispSync VsyncThread 第二部分

    virtual bool threadLoop() {

    ......

    //计算下次Vsync要产生的时间

    nextEventTime = computeNextEventTimeLocked(now);

    targetTime = nextEventTime;

    bool isWakeup = false;

    //如果还没有到时间,就等待相应的时间后在处理

    if (now < targetTime) {

    err = mCond.waitRelative(mMutex, targetTime - now);

    if (err == TIMED_OUT) {

    isWakeup = true;

    } else if (err != NO_ERROR) {

    ALOGE("error waiting for next event: %s (%d)",

    strerror(-err), err);

    return false;

    }

    }

    now = systemTime(SYSTEM_TIME_MONOTONIC);

    //到达执行Vsync时间后,回调监听对象.

    if (isWakeup) {

    mWakeupLatency = ((mWakeupLatency * 63) +

    (now - targetTime)) / 64;

    if (mWakeupLatency > 500000) {

    // Don't correct by more than 500 us

    mWakeupLatency = 500000;

    }

    if (kTraceDetailedInfo) {

    ATRACE_INT64("DispSync:WakeupLat", now - nextEventTime);

    ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency);

    }

    }

    callbackInvocations = gatherCallbackInvocationsLocked(now);

    }

    if (callbackInvocations.size() > 0) {

    fireCallbackInvocations(callbackInvocations);

    }

    }

    return false;

    }

    当有Vsync的周期时间和偏差时间后,VsyncThread就可以模拟产生软件Vsync信号了。产生Vsync信号就会通过回调接口通知监听者.

    DispVsync有两个监听者,就是我们上面提到的SurfaceFlinger和APP,SurfaceFlinger和App所需要的Vsync时间不是完全一致。App一般先接到Vsync信号,还是绘制UI,然后SurfaceFlinger再接收到Vsync信号完成UI合成。

    VsyncThread做了什么事情呢?

    首先计算下次Vsync产生的时间,计算下次产生时间会根据两个监听者上次接收到Vsync时间,然后加上周期时间,偏差时间,和APP或者SF自己的偏差时间。得到下次Vsync时间。

    下次Vsync时间 = 上次Vsync时间 + mPeriod(平均周期) + mPhase(平均偏差) + offset(自定义偏差)

    得到最近的下次执行时间,Vsync只要Wait相应的时间差就可了,等到执行时间后回调SF或者APP的Vsync信号。这样Vsync信号就产生了.

    DisplaySyncSource

    上面讲DispSync提到,DispSync有两个监听者,这两个监听这就是两个DisplaySyncSource对象。两个对象是在SurfaceFlinger的init进程创建的.

    sp vsyncSrc = new DispSyncSource(&mPrimaryDispSync,

    vsyncPhaseOffsetNs, true, "app");

    sp sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,

    sfVsyncPhaseOffsetNs, true, "sf");

    init中创建了两个DispSyncSource对象, 一个是SF的sfVsyncSrc,一个是SurfaceFlinger的sfVsyncSrc对象. DiplaySyncSource构造方法中会保存一个syncOffet值,表示自己收到Vsync偏差时间.

    DispSync计算Vsync产生时间的时候,会根据这个偏差进行计算.也就是上面公式中的自定义偏差offset。

    DispSync有一些重要的函数,具体如下:

    //开始/关闭监听Vsync事件

    //开始监听Vsync事件就是将自己添加到DispSync的回调监听中

    //关闭监听Vsync事件就是将自己从DispSync监听回调中移除

    virtual void setVSyncEnabled(bool enable) {

    Mutex::Autolock lock(mVsyncMutex);

    if (enable) {

    status_t err = mDispSync->addEventListener(mPhaseOffset,

    static_cast<:callback>(this));

    if (err != NO_ERROR) {

    ALOGE("error registering vsync callback: %s (%d)",

    strerror(-err), err);

    }

    //ATRACE_INT(mVsyncOnLabel.string(), 1);

    } else {

    status_t err = mDispSync->removeEventListener(

    static_cast<:callback>(this));

    if (err != NO_ERROR) {

    ALOGE("error unregistering vsync callback: %s (%d)",

    strerror(-err), err);

    }

    //ATRACE_INT(mVsyncOnLabel.string(), 0);

    }

    mEnabled = enable;

    }

    //设置自己的回调,即收到Vsync事件后,回调给谁

    virtual void setCallback(const sp<:callback>& callback) {

    Mutex::Autolock lock(mCallbackMutex);

    mCallback = callback;

    }

    //DispSync的回调接口, 当收到DispSync事件后会回到该接口,然后该接口将事件又回调给自己的callBack

    //也就是后边讲的EventThread,EventThread会监听DisplaySyncSource。

    virtual void onDispSyncEvent(nsecs_t when) {

    sp<:callback> callback;

    {

    Mutex::Autolock lock(mCallbackMutex);

    callback = mCallback;

    if (callback != NULL) {

    callback->onVSyncEvent(when);

    }

    }

    这样,我们就大概理解了Vsync信号的产生过程。

    1:HWC硬件产生Vsync信号,给DispSync添加样本

    2:DispSync根据样本计算Vsync周期,然后产生软件Vsync信号。计算Vsync事件会根据不同的Listenr计算不同的时间.

    3:DisplaySyncSource是DispSync的监听者,有两个DisplaySyncSource对象,分别代表SF和APP的DisplaySyncSource,两个回调的Vsync时间不同.

    4:DisplaySyncSource收到Vsync事件后,会发给他自己的监听者EventThread

    void EventThread::onVSyncEvent(nsecs_t timestamp) {

    Mutex::Autolock _l(mLock);

    mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;

    mVSyncEvent[0].header.id = 0;

    mVSyncEvent[0].header.timestamp = timestamp;

    mVSyncEvent[0].vsync.count++;

    mCondition.broadcast();

    }

    EventThread收到Vsync时间后会存放到mVSyncEvent[0]中,通知EventThread线程进行处理。

    为什么要有两个DisplaySyncSource且时间不一样呢?

    b30b72acd207c0b9dc38a92b5291c6bb.png

    Vsync

    具体如上图所示

    App绘制UI, 绘制完成后交给SurfaceFlinger进行合成, 所以App绘制时间优先于SF合成时间,将两者时间错开,避免资源竞争,增加效率。如何APP和SF的偏差时间设置好的话,APP + SF在一个Vsync周期内就可以绘制合成,在下一个Vsync周期到来的时候就可以显示到屏幕上了。

    展开全文
  • 适用于Cinder的Vsync / FPS测试应用程序 (提交之日为HEAD) 也可以在获得openFrameworks版本
  • Android vsync

    2021-06-06 12:56:30
    VSYNC 的概念VSYNC(Vertical Synchronization)是一个相当古老的概念,对于游戏玩家,它有一个更加大名鼎鼎的中文名字—-垂直同步。“垂直同步(vsync)”指的是显卡的输出帧数和屏幕的垂直刷新率相同,这完全是一个CRT...

    VSYNC 的概念

    VSYNC(Vertical Synchronization)是一个相当古老的概念,对于游戏玩家,它有一个更加大名鼎鼎的中文名字—-垂直同步。“垂直同步(vsync)”指的是显卡的输出帧数和屏幕的垂直刷新率相同,这完全是一个CRT显示器上的概念。其实无论是VSYNC还是垂直同步这个名字,因为LCD根本就没有垂直扫描的这种东西,因此这个名字本身已经没有意义。但是基于历史的原因,这个名称在图形图像领域被沿袭下来。在当下,垂直同步的含义我们可以理解为,使得显卡生成帧的速度和屏幕刷新的速度的保持一致。举例来说,如果屏幕的刷新率为60Hz,那么生成帧的速度就应该被固定在1/60 s。

    Android中的VSYNC — 黄油计划

    从Android 4.1开始,谷歌致力于解决Android系统中最饱受诟病的一个问题,滑动不如iOS流畅。因谷歌在4.1版本引入了一个重大的改进—Project Butter,也即是黄油计划。Project Butter对Android Display系统进行了重构,引入了三个核心元素,即VSYNC、Triple Buffer和Choreographer。关于后面两个概念我们会在后面开专题讲解,这里我们重点讲解VSYNC的作用。玩过大型PC游戏的玩家都知道,VSYNC最重要的作用是防止出现画面撕裂(screentearing)。所谓画面撕裂,就是指一个画面上出现了两帧画面的内容,如下图。

    1267e6a6e841

    画面撕裂

    为什么会出现这种情况呢?这种情况一般是因为显卡输出帧的速度高于显示器的刷新速度,导致显示器并不能及时处理输出的帧,而最终出现了多个帧的画面都留在了显示器上的问题。这也就是我们所说的画面撕裂。

    提到垂直同步这里就多提一句,其实我认为对于PC上的大型游戏来说,只有配置足够高,高到显卡输出帧率可以稳定的高于显示器的刷新频率,才有开启垂直同步的必要。因为只有这个时候,画面撕裂才会真正成为一个问题。而对于很多情况下主机性能不足导致游戏输出帧率低于显示器的刷新频率的情况下,尤其是帧率稳定在40~60之间时,开启垂直同步可能会导致帧率倍数级的下降(具体原因我们在Graphic架构一文中提到过,当帧生成速度不及VSync速度时,帧率的下降不是平缓的,而且很可能是倍数级的。当然这在android系统上并非严重问题,因为android上很少有高速的复杂场景的频繁切换。事实上,在Android的普通应用场景下,VSync的使用不仅不会降低帧率,还可以有效解决卡顿问题)。

    回到正文中来,那么VSync除了可以解决画面的撕裂的问题,还可以解决别的什么问题吗?我们来看下图:

    1267e6a6e841

    Jank

    这个图中有三个元素,Display是显示屏幕,GPU和CPU负责渲染帧数据,每个帧以方框表示,并以数字进行编号,如0、1、2等等。VSync用于指导双缓冲区的交换。以时间的顺序来看下将会发生的异常:

    Step1. Display显示第0帧数据,此时CPU和GPU渲染第1帧画面,而且赶在Display显示下一帧前完成。

    Step2. 因为渲染及时,Display在第0帧显示完成后,也就是第1个VSync后,正常显示第1帧。

    Step3. 由于某些原因,比如CPU资源被占用,系统没有及时地开始处理第2帧,直到第2个VSync快来前才开始处理

    Step4. 第2个VSync来时,由于第2帧数据还没有准备就绪,显示的还是第1帧。这种情况被Android开发组命名为“Jank”。

    Step5. 当第2帧数据准备完成后,它并不会马上被显示,而是要等待下一个VSync。所以总的来说,就是屏幕平白无故地多显示了一次第1帧。原因大家应该都看到了,就是CPU没有及时地开始着手处理第2帧的渲染工作,以致“延误军机”。

    其实总结上面的这个情况之所以发生,首先的原因就在于第二帧没有及时的绘制(当然即使第二帧及时绘制,也依然可能出现Jank,这就是同时引入三重缓冲的作用。我们将在三重缓冲一节中再讲解这种情况)。那么如何使得第二帧及时被绘制呢?这就是我们在Graphic系统中引入VSYNC的原因,考虑下面这张图:

    1267e6a6e841

    Drawing with Vsync

    如上图所示,一旦VSync出现后,立刻就开始执行下一帧的绘制工作。这样就可以大大降低Jank出现的概率。另外,VSYNC引入后,要求绘制也只能在收到VSYNC消息之后才能进行,因此,也就杜绝了另外一种极端情况的出现—-CPU(GPU)一直不停的进行绘制,帧的生成速度高于屏幕的刷新速度,导致生成的帧不能被显示,只能丢弃,这样就出现了丢帧的情况—-引入VSYNC后,绘制的速度就和屏幕刷新的速度保持一致了。

    展开全文
  • VSYNC

    2022-01-10 14:47:54
    VSYNC 信号可同步显示流水线。显示流水线由应用渲染、SurfaceFlinger 合成以及用于在屏幕上显示图像的硬件混合渲染器 (HWC) 组成。VSYNC 可同步应用唤醒以开始渲染的时间、SurfaceFlinger 唤醒以合成屏幕的时间以及...

    VSYNC 信号可同步显示流水线。显示流水线由应用渲染、SurfaceFlinger 合成以及用于在屏幕上显示图像的硬件混合渲染器 (HWC) 组成。VSYNC 可同步应用唤醒以开始渲染的时间、SurfaceFlinger 唤醒以合成屏幕的时间以及屏幕刷新周期。这种同步可以消除卡顿,并提升图形的视觉表现。

    HWC 可生成 VSYNC 事件并通过回调将事件发送到 SurfaceFlinger:


    typedef void (*HWC2_PFN_VSYNC)(hwc2_callback_data_t callbackData,
            hwc2_display_t display, int64_t timestamp);
    注意:HWC 从 HAL_PRIORITY_URGENT_DISPLAY 的线程触发 hwc2_callback_data_t,延迟时间尽可能短,通常小于 0.5 毫秒。
    SurfaceFlinger 通过调用 setVsyncEnabled 来控制 HWC 是否生成 VSYNC 事件。SurfaceFlinger 使 setVsyncEnabled 能够生成 VSYNC 事件,因此它可以与屏幕的刷新周期同步。当 SurfaceFlinger 同步到屏幕刷新周期时,SurfaceFlinger 会停用 setVsyncEnabled 以阻止 HWC 生成 VSYNC 事件。如果 SurfaceFlinger 检测到实际 VSYNC 与它先前建立的 VSYNC 之间存在差异,则 SurfaceFlinger 会重新启动 VSYNC 事件生成过程。

    VSYNC 偏移
    同步应用和 SurfaceFlinger 渲染循环应同步到硬件 VSYNC。在 VSYNC 事件中,屏幕开始显示帧 N,而 SurfaceFlinger 开始为帧 N+1 合成窗口。应用处理等待的输入并生成帧 N+2。

    与 VSYNC 同步会实现一致的延迟时间。它可以减少应用和 SurfaceFlinger 中的错误,并最大限度减小相位内外屏幕之间的偏移。这要假定应用和 SurfaceFlinger 的每帧时间没有很大变化。延迟至少为两帧。

    为了解决此问题,您可以通过使应用和合成信号与硬件 VSYNC 相关,从而利用 VSYNC 偏移减少输入设备到屏幕的延迟。这是有可能的,因为应用加合成通常需要不到 33 毫秒的时间。

    VSYNC 偏移的结果是具有相同周期和偏移相位的三个信号:

    HW_VSYNC_0 - 屏幕开始显示下一帧。
    VSYNC - 应用读取输入内容并生成下一帧。
    SF_VSYNC - SurfaceFlinger 开始为下一帧进行合成。
    通过 VSYNC 偏移,SurfaceFlinger 接收缓冲区并合成帧,而应用同时处理输入内容并渲染帧。

    注意:VSYNC 偏移会缩短可用于应用和合成的时间,因此增加了出错几率。


    DispSync
    DispSync 维护屏幕基于硬件的周期性 VSYNC 事件的模型,并使用该模型在硬件 VSYNC 事件的特定相位偏移处执行回调。

    DispSync 是一个软件锁相回路 (PLL),它可以生成由 Choreographer 和 SurfaceFlinger 使用的 VSYNC 和 SF_VSYNC 信号,即使没有来自硬件 VSYNC 的偏移也是如此。

    DispSync 流程


    图 1. DispSync 流程

     

    DispSync 具有以下特点:

    参考 - HW_VSYNC_0。
    输出 - VSYNC 和 SF_VSYNC。
    反馈 - 自硬件混合渲染器的退出栅栏有信号状态时间戳。


    VSYNC/退出偏移
    退出栅栏的有信号状态时间戳必须与 HW VSYNC 相符,即使在不使用偏移相位的设备上也是如此。否则,造成的错误会更加严重。智能面板通常有一个增量:退出栅栏是对显示内存进行直接内存访问 (DMA) 的终点,但是实际的显示切换和 HW VSYNC 会晚一段时间。

    PRESENT_TIME_OFFSET_FROM_VSYNC_NS 在设备的 BoardConfig.mk makefile 中设置。它取决于屏幕控制器和面板特性。从退出栅栏时间戳到 HW VSYNC 信号的时间以纳秒为单位进行测量。

    VSYNC 和 SF_VSYNC 偏移
    VSYNC_EVENT_PHASE_OFFSET_NS 和 SF_VSYNC_EVENT_PHASE_OFFSET_NS 根据高负载使用情形进行了保守设置,例如在窗口过渡期间进行部分 GPU 合成或 Chrome 滚动显示包含动画的网页。这些偏移允许较长的应用渲染时间和较长的 GPU 合成时间。

    超过一两毫秒的延迟时间是非常明显的。为了最大限度地缩短延迟时间而不显著增加错误计数,请集成彻底的自动化错误测试。

    注意:VSYNC 和 SF_VSYNC 偏移同样在设备的 BoardConfig.mk 文件中配置。两个设置都是 HW_VSYNC_0 之后以纳秒为单位的偏移,默认值为零(如未设置的话),也可以为负值。

    展开全文
  • 前面系列我们依赖 Android ...这个问题在网上的答案基本都说 VSYNC,但是少有人说这个 VSYNC 是怎么被关联起来的,本文就针对这个问题进行一个 Platform 到 Engine 到 Dart Framework 分析,源码依赖 Flutter 2.2.3。

    Flutter 系列文章连载~

    背景

    前面系列我们依赖 Android 平台实现分析了端侧很多机制,但是有一个知识点一直比较迷糊,那就是 Flutter 是怎么被触发绘制的?这个问题在网上的答案基本都说 VSYNC,但是少有人说这个 VSYNC 是怎么被关联起来的,本文就针对这个问题进行一个 Platform 到 Engine 到 Dart Framework 分析,源码依赖 Flutter 2.2.3。

    Android 平台 Java 层

    还记得我们前面系列文章分析过的io.flutter.embedding.engine.FlutterJNI吗,FlutterJNI 的作用就是架起 Android 端 Java 与 Flutter Engine C/C++ 端的一座接口桥梁。记不记得当时我们分析 FlutterEngine 时(《Flutter Android 端 FlutterEngine Java 相关流程源码分析》)在他的实例化过程中有这么一段调用逻辑:

    -> 调用 FlutterEngine 构造方法
    -> 调用 FlutterLoader 的 startInitialization(context.getApplicationContext()) 方法
    -> 调用 VsyncWaiter.getInstance((WindowManager) appContext.getSystemService(Context.WINDOW_SERVICE)).init() 方法
    

    基于上面流程,我们把重点转向 Java 端的 VsyncWaiter 类及其 init 方法,如下:

    //进程单例实例类
    public class VsyncWaiter {
      //......
      //一个来自engine触发的回调
      private final FlutterJNI.AsyncWaitForVsyncDelegate asyncWaitForVsyncDelegate =
          new FlutterJNI.AsyncWaitForVsyncDelegate() {
            @Override
            public void asyncWaitForVsync(long cookie) {
              //每逢回调回来就向Choreographer post一个绘制VSYNC请求。
              Choreographer.getInstance()
                  .postFrameCallback(
                      new Choreographer.FrameCallback() {
                        @Override
                        public void doFrame(long frameTimeNanos) {
                          float fps = windowManager.getDefaultDisplay().getRefreshRate();
                          long refreshPeriodNanos = (long) (1000000000.0 / fps);
                          //调用FlutterJNI的nativeOnVsync逻辑通知VSYNC
                          FlutterJNI.nativeOnVsync(
                              frameTimeNanos, frameTimeNanos + refreshPeriodNanos, cookie);
                        }
                      });
            }
          };
      //......
      //唯一被调用的方法
      public void init() {
        //设置委托实例回调引用
        FlutterJNI.setAsyncWaitForVsyncDelegate(asyncWaitForVsyncDelegate);
    
        //传输fps值给engine
        float fps = windowManager.getDefaultDisplay().getRefreshRate();
        FlutterJNI.setRefreshRateFPS(fps);
      }
    }
    

    我们简单看下 FlutterJNI 里面是怎么做的,如下:

    @Keep
    public class FlutterJNI {
      //......
      //VsyncWaiter.init方法设置的委托回调实现就是赋值给了他
      private static AsyncWaitForVsyncDelegate asyncWaitForVsyncDelegate;
      //......
      //VsyncWaiter.init方法中调用
      public static void setAsyncWaitForVsyncDelegate(@Nullable AsyncWaitForVsyncDelegate delegate) {
        asyncWaitForVsyncDelegate = delegate;
      }
    
      //这个方法的注释明确说了被 netive engine 调用,也就是 JNI 的 C/C++ 端调用
      private static void asyncWaitForVsync(final long cookie) {
        if (asyncWaitForVsyncDelegate != null) {
          asyncWaitForVsyncDelegate.asyncWaitForVsync(cookie);
        } else {
          throw new IllegalStateException("An AsyncWaitForVsyncDelegate must be registered with FlutterJNI before asyncWaitForVsync() is invoked.");
        }
      }
    
      //java端调用native实现
      public static native void nativeOnVsync(
          long frameTimeNanos, long frameTargetTimeNanos, long cookie);
    
      public interface AsyncWaitForVsyncDelegate {
        void asyncWaitForVsync(final long cookie);
      }
    }
    

    对于安卓仔来说,上面代码中熟悉的系统 API 比较多,所以我们先回到纯 Android 平台。老司机都知道,现代 Android 系统至少都是基于 VSYNC 的 Double Buffer(双缓冲)机制实现绘制,而双缓冲机制背后的核心思想是让绘制和显示拥有各自的图像缓冲区,也就是说 GPU 始终将完成的一帧图像数据写入到 Back Buffer,而显示器使用 Frame Buffer 数据进行显示,这样双缓冲 Frame Buffer 中的数据一定不会存在撕裂(类似并发不安全的写),VSYNC 信号负责调度从 Back Buffer 到 Frame Buffer 的交换操作,这里并不是真正的数据 copy,实际是交换各自的内存地址,可以认为该操作是瞬间完成。

    在这里插入图片描述
    看过我 Android 源码分析系列文章或者其他网文的小伙伴一定都知道,Android中有一个 ViewRootImpl,他的 mView 成员是 DecorView(本质 FrameLayout),而 DecorView 是一个 Activity 的根 View。整个界面的重绘入口都是 ViewRootImpl 类的 scheduleTraversals 方法(不懂就去看历史文章),我们自己调用 View 的 invalidate 方法也是类似,如下:

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            //......
            mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            //......
        }
    }
    

    上面的 mTraversalRunnable 就是调用了 Activity 中 View 树的 measure、layout、draw 进行绘制。而 mChoreographer 就是 Choreographer,在安卓平台上,Choreographer 通过 postXXX 调用 FrameDisplayEventReceiver(继承自 DisplayEventReceiver) 的 nativeScheduleVsync 方法进行 VSYNC 请求;同时 Choreographer 也通过 FrameDisplayEventReceiver 的 onVsync 方法监听了系统 VSYNC 脉冲信号,该监听方法中会触发 Choreographer 的 doFrame 方法,该方法会把我们 post 进去的 callback 队列拿出来执行,然后将已经执行过的 callback 进行移除。整个过程如下图:
    在这里插入图片描述
    简单总结下结论,安卓应用程序如果有绘制(包括动画)需求的话,必须向系统框架发起 VSYNC 请求,请求在下一次 VSYNC 信号到来时绘制应用界面。

    看到上面这个结论其实如果你有一定悟性应该能猜到 Flutter 的 VSYNC 是怎么工作的了,他其实也实现了类似标准安卓绘制触发的流程,即发送 VSYNC 请求,等待下一个 VSYNC 信号到来执行 callback 回调。我们在继续分析前可以基于上面 VsyncWaiter 和 FlutterJNI 相关接口进行一个猜想如下:
    在这里插入图片描述

    Flutter Framework Dart 层

    Android 平台 Java 层面的问题我们都分析完毕了,通过上面 Flutter VSYNC 猜想时序图我们知道重点都在 Flutter Engine 里面。也就是说 Flutter Engine 调用 FlutterJNI 的 asyncWaitForVsync 方法通过安卓平台的 Choreographer 发送 VSYNC 请求,请求在安卓平台下一次 VSYNC 信号到来时通过 FlutterJNI 的 nativeOnVsync 方法向 Flutter Engine 传递绘制信号,整个过程像极了安卓 View 统管的 ViewRootImpl 实现。

    我们知道,Flutter Engine 是 Flutter Dart Framework 与 Android 平台之间的一个桥梁,抽象如下:
    在这里插入图片描述
    所以我们基于前面 Flutter 系列分析及上面 Android 绘制机制大胆猜测可以知道,VSYNC 请求来自 Flutter Dart Framework,下一次 VSYNC 信号到来触发绘制也调用到了 Flutter Dart Framework,Flutter Engine 只是一个桥梁处理过程。

    发起绘制 VSYNC 请求

    前面我们分析 Flutter App Dart main 方法时有提到 scheduleWarmUpFrame 方法最终调用了 SchedulerBinding 的 scheduleFrame 方法,进而调用window.scheduleFrame(),再调用 PlatformDispatcher 的scheduleFrame(),代码如下:

    class PlatformDispatcher {
      /// 发起VSYNC请求,等待下一帧调用onBeginFrame和onDrawFrame回调。
      void scheduleFrame() native 'PlatformConfiguration_scheduleFrame';
    }
    

    PlatformDispatcher 的 scheduleFrame 方法实现其实是 Dart 调用 C/C++ native 代码,对应的也是 PlatformConfiguration_scheduleFrame,我们可以在 engine 的 C/C++ 中搜其注册入口。

    上面方法就是 Flutter 层真正发起 VSYNC 请求的地方,然后等系统下一个 VSYNC 信号到来进行绘制操作(即来自 FlutterJni 的 nativeOnVsync 方法触发),也就是最终调用到 Dart 层的 onBeginFrame 和 onDrawFrame。

    其实我们日常中调用 Flutter Dart StatefulWidget 的 setState 方法也是调用了上面 scheduleFrame 方法,也就是说绘制的发起都来自 Widget 的变更主动调用触发,包括动画效果等也是同样道理。

    收到下一帧 VSYNC 绘制信号

    当上面 VSYNC 请求发出且等到下一个 VSYNC 信号到来时会通过 Java 到 C/C++ 再到 Dart Framework 层,对应到 Dart 层入口在hooks.dart文件(调用详见下面 Flutter Engine C/C++ 层分析),如下:

    @pragma('vm:entry-point')
    // ignore: unused_element
    void _beginFrame(int microseconds) {
      PlatformDispatcher.instance._beginFrame(microseconds);
    }
    
    @pragma('vm:entry-point')
    // ignore: unused_element
    void _drawFrame() {
      PlatformDispatcher.instance._drawFrame();
    }
    

    本质在 PlatformDispatcher 中调用对应方法,即如下:

    class PlatformDispatcher {
      FrameCallback? get onBeginFrame => _onBeginFrame;
      FrameCallback? _onBeginFrame;
      Zone _onBeginFrameZone = Zone.root;
      set onBeginFrame(FrameCallback? callback) {
        _onBeginFrame = callback;
        _onBeginFrameZone = Zone.current;
      }
    
      VoidCallback? get onDrawFrame => _onDrawFrame;
      VoidCallback? _onDrawFrame;
      Zone _onDrawFrameZone = Zone.root;
      set onDrawFrame(VoidCallback? callback) {
        _onDrawFrame = callback;
        _onDrawFrameZone = Zone.current;
      }
    }
    

    也就是调用了 PlatformDispatcher 通过 onBeginFrame、onDrawFrame 设置的对应回调,我们反过来推看谁设置了这个回调赋值,首先看到的调用赋值位于 SingletonFlutterWindow:

    class SingletonFlutterWindow extends FlutterWindow {
      FrameCallback? get onBeginFrame => platformDispatcher.onBeginFrame;
      set onBeginFrame(FrameCallback? callback) {
        platformDispatcher.onBeginFrame = callback;
      }
    }
    

    接着看 SingletonFlutterWindow 的 onBeginFrame 属性是谁赋值的,发现赋值调用如下:

    mixin SchedulerBinding on BindingBase {
      void ensureFrameCallbacksRegistered() {
        //回调实现位于SchedulerBinding中
        window.onBeginFrame ??= _handleBeginFrame;
        window.onDrawFrame ??= _handleDrawFrame;
      }
    
      void scheduleFrame() {
        //......
        ensureFrameCallbacksRegistered();
        window.scheduleFrame();
        //......
      }
    }
    

    可以看到本质回到了 SchedulerBinding 的 scheduleFrame 方法,也就是说第一次 Dart 发起 VSYNC 请求前先设置了回调,当下一个系统 VSYNC 信号到来时就调用了 onBeginFrame、onDrawFrame 的回调赋值。也就是说真正的绘制到 Dart 层入口在 SchedulerBinding 的void handleBeginFrame(Duration? rawTimeStamp)handleDrawFrame()中,关于他们的具体内容不在本文分析范围,本文关注 VSYNC 动机过程。

    Dart 层大致流程如下:
    在这里插入图片描述

    Flutter Engine C/C++ 层

    有了上面 Dart 层及 Java 层的分析,我们其实分析 Engine 层的 C/C++ 时就大致知道关键入口是什么了,所以下面依然基发起 VSYNC 请求和下一帧回调 VSYNC 信号流程进行分析。

    发起绘制 VSYNC 请求

    通过 Dart 分析得知 VSYNC 信号的发起的实现是通过 Dart 调用了 engine C/C++ 的PlatformConfiguration_scheduleFrame native 方法,所以我们搜索可以看到对应 C/C++ 只有一处注册且位于lib/ui/window/platform_configuration.cc文件:

    void PlatformConfiguration::RegisterNatives(
        tonic::DartLibraryNatives* natives) {
      natives->Register({
          //......
          {"PlatformConfiguration_scheduleFrame", ScheduleFrame, 1, true},
          //......
      });
    }
    
    void ScheduleFrame(Dart_NativeArguments args) {
      UIDartState::ThrowIfUIOperationsProhibited();
      //client()本质PlatformConfigurationClient,也就是RuntimeController
      UIDartState::Current()->platform_configuration()->client()->ScheduleFrame();
    }
    

    通过上面代码可以看到,Dart 调用 engine C/C++ 的PlatformConfiguration_scheduleFrame native 方法走进了lib/ui/window/platform_configuration.cc文件的void ScheduleFrame(Dart_NativeArguments args)方法,通过platform_configuration.h文件中可以知道,client 是 PlatformConfigurationClient 类型,而 RuntimeController 类是他的实现,即runtime/runtime_controller.h中如下:

    class RuntimeController : public PlatformConfigurationClient {
        //......
    }
    

    因此我们把目光转向 RuntimeController 类,即runtime/runtime_controller.h中如下:

    void RuntimeController::ScheduleFrame() {
      //client_ 类型为 RuntimeDelegate,也就是 engine instance
      client_.ScheduleFrame();
    }
    

    通过runtime/runtime_controller.h中 client 的注释可以知道,client_ 其实是 Engine 实例,即shell/common/engine.h中如下:

    class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {
        //......
    }
    

    对应实现shell/common/engine.cc如下:

    void Engine::ScheduleFrame(bool regenerate_layer_tree) {
      animator_->RequestFrame(regenerate_layer_tree);
    }
    

    类似同上分析模式查看对应 h 和 cpp 文件可以知道 animator_ 位于shell/common/animator.cc,如下:

    void Animator::RequestFrame(bool regenerate_layer_tree) {
      //......
      task_runners_.GetUITaskRunner()->PostTask(//......
           frame_request_number = frame_request_number_]() {
            //......
            self->AwaitVSync();
          });
    }
    

    在引擎的 UITaskRunner 中执行shell/common/animator.cc文件的 AwaitVSync 方法,如下:

    void Animator::AwaitVSync() {
      waiter_->AsyncWaitForVsync(
          [self = weak_factory_.GetWeakPtr()](
              std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {
            if (self) {
              if (self->CanReuseLastLayerTree()) {
                self->DrawLastLayerTree(std::move(frame_timings_recorder));
              } else {
                self->BeginFrame(std::move(frame_timings_recorder));
              }
            }
          });
    }
    

    类似同上分析模式查看对应 h 和 cpp 文件可以知道 waiter_ 位于shell/common/vsync_waiter.cc,在 Android 平台的实现类是 VsyncWaiterAndroid,位于shell/platform/android/vsync_waiter_android.cc如下:

    //父类VsyncWaiter的AsyncWaitForVsync调用子类VsyncWaiterAndroid的AwaitVSync方法
    void VsyncWaiter::AsyncWaitForVsync(const Callback& callback) {
      //......
      callback_ = std::move(callback);
      //......
      //对应VsyncWaiterAndroid::AwaitVSync()
      AwaitVSync();
    }
    
    void VsyncWaiterAndroid::AwaitVSync() {
      //......
      task_runners_.GetPlatformTaskRunner()->PostTask([java_baton]() {
        JNIEnv* env = fml::jni::AttachCurrentThread();
        env->CallStaticVoidMethod(
            //io/flutter/embedding/engine/FlutterJNI
            g_vsync_waiter_class->obj(),  
            //asyncWaitForVsync方法   
            g_async_wait_for_vsync_method_,
            //参数
            java_baton
        );
      });
    }
    

    真相大白,最后通过 Engine 的 PlatformTaskRunner 调用了 JNI 方法 asyncWaitForVsync,也就是我们上面分析 Android 平台 Java 层小节提到的 FlutterJNI java 类的 asyncWaitForVsync 静态方法。哈哈,Flutter 发起 VSYNC 请求的流程就这样从 Java 到 C++ 到 Dart,再从 Dart 到 C++ 到 Java 全串起来了。

    收到下一帧 VSYNC 绘制信号

    刚刚发起绘制 VSYNC 请求最终走进了 java 层的Choreographer.getInstance().postFrameCallback(callback)方法,上面分析 Java 部分代码时也提到了,等下一帧 VSYNC 信号到来会触发 java 层 FlutterJNI 类的 nativeOnVsync 方法。经过 C/C++ 搜索分析可知,上面 FlutterJNI 中的 nativeOnVsync 方法调用点位于 engine 的shell/platform/android/vsync_waiter_android.cc中,如下:

    void VsyncWaiterAndroid::OnNativeVsync(JNIEnv* env, jclass jcaller, jlong frameTimeNanos,
                                           jlong frameTargetTimeNanos, jlong java_baton) {
      //......
      ConsumePendingCallback(java_baton, frame_time, target_time);
    }
    
    void VsyncWaiterAndroid::ConsumePendingCallback( jlong java_baton,
        fml::TimePoint frame_start_time, fml::TimePoint frame_target_time) {
      //......
      shared_this->FireCallback(frame_start_time, frame_target_time);
    }
    
    void VsyncWaiter::FireCallback(fml::TimePoint frame_start_time,
                                   fml::TimePoint frame_target_time,
                                   bool pause_secondary_tasks) {
        //......
        PauseDartMicroTasks();
        //......
        task_runners_.GetUITaskRunner()->PostTaskForTime(
            [ui_task_queue_id, callback, flow_identifier, frame_start_time,
             frame_target_time, pause_secondary_tasks]() {
              //......
              callback(std::move(frame_timings_recorder));
              //......
              ResumeDartMicroTasks(ui_task_queue_id);
              //......
            }, frame_start_time);
      }
      //......
    }
    

    其实上面绕一圈最终就是在引擎的 UITaskRunner 中执行了上面 dart 发起 VSYNC 请求小节分析的 callback 参数。即,这里的callback(std::move(frame_timings_recorder))等价于Animator::AwaitVSync()方法中调用waiter_->AsyncWaitForVsync方法传递的参数 callback,callback 赋值代码位于shell/common/animator.cc文件的 AwaitVSync 方法,如下:

    void Animator::AwaitVSync() {
      //AsyncWaitForVsync函数参数就是callback
      waiter_->AsyncWaitForVsync(
          [self = weak_factory_.GetWeakPtr()](
              std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {
            if (self) {
              if (self->CanReuseLastLayerTree()) {
                self->DrawLastLayerTree(std::move(frame_timings_recorder));
              } else {
                self->BeginFrame(std::move(frame_timings_recorder));
              }
            }
          });
    }
    

    真相大白,callback 被回调(即 VSYNC 绘制信号过来)时调用了 Animator 的 DrawLastLayerTree 或者 BeginFrame 方法,具体取决于是否需要重新生成 LayerTree 树进行绘制。

    由于本文我们主要关心绘制动机流程,所以上面 DrawLastLayerTree 就先不分析了,我们看看 Animator 的 BeginFrame 方法,可以发现其调用了delegate_.OnAnimatorBeginFrame(frame_target_time, frame_number);,也就是 Shell 的 OnAnimatorBeginFrame 方法,本质就是 Engine 的 BeginFrame 方法,如下shell/common/engine.cc

    void Engine::BeginFrame(fml::TimePoint frame_time, uint64_t frame_number) {
      TRACE_EVENT0("flutter", "Engine::BeginFrame");
      runtime_controller_->BeginFrame(frame_time, frame_number);
    }
    

    RuntimeController 的 BeginFrame 方法调用了 PlatformConfiguration 的 BeginFrame 方法,如下lib/ui/window/platform_configuration.cc

    void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime,
                                           uint64_t frame_number) {
      //......
      tonic::LogIfError(
          tonic::DartInvoke(begin_frame_.Get(), {
              Dart_NewInteger(microseconds),
              Dart_NewInteger(frame_number),
          }));
      UIDartState::Current()->FlushMicrotasksNow();
      tonic::LogIfError(tonic::DartInvokeVoid(draw_frame_.Get()));
    }
    
    void PlatformConfiguration::DidCreateIsolate() {
      Dart_Handle library = Dart_LookupLibrary(tonic::ToDart("dart:ui"));
      //......
                       Dart_GetField(library, tonic::ToDart("_beginFrame")));
      draw_frame_.Set(tonic::DartState::Current(),
                      Dart_GetField(library, tonic::ToDart("_drawFrame")));
      //......
    }
    

    哈哈,这就呼应了上面 Flutter Framework Dart 层小节收到下一帧 VSYNC 绘制信号部分被调用的入库,即下一个 VSYNC 绘制信号过来最终引擎 engine 调用了 Dart 层入口在hooks.dart文件的_beginFrame_drawFrame等方法触发 dart 层进行绘制操作。

    C++ 层流程大致总结如下:
    在这里插入图片描述

    总结

    到此我想你应该就能大概看懂 Flutter 官网贴的这张经典绘制流程图了:
    在这里插入图片描述
    关于上图中的每一步细节不在本文分析范围之内,但是关于上图从发起 Flutter VSYNC 请求到收到系统下一个 VSYNC 绘制信号进行绘制操作的全流程我们算是彻底搞明白了,也从一定程度上理解了 Flutter 架构分层图的整个架构流转机制。

    其实搞懂本文 VSYNC 信号从 Dart 到 C++ 到 Java,再从 Java 到 C++ 到 Dart,可以不夸张的说你已经掌握了 Flutter 架构的精髓,缺少的只是这条链条上的各个细节节点而已。

    展开全文
  • Android 显示 一、 Vsync

    2021-04-17 19:49:58
    Android 显示系统:Vsync机制 典型的显示系统中,一般包括CPU、GPU、Display三个部分, CPU负责计算帧数据, GPU对计算好的图形数据进行渲染,渲染好后放在buffer(图像缓冲区)里, Display负责把buffer里的数据...
  • 学习流程: 先看文章,再看代码,再看文章,再画图 很多文章写的太长,看了后面忘了...如果CPU和GPU不按照Vsync的节奏执行,比如第二帧vsync马上要到了,CPu和GPU才去执行,就出现Jank, 改进是,vsync来的时...
  • Android SurfaceFlinger SW Vsync模型

    千次阅读 2019-02-27 15:59:52
    Android SurfaceFlinger Vsync这块比较复杂,最初在看这块的时候,迟迟不知道从何入手,网上看了各种SurfaceFlinger Vsync相关的博客,个人感觉紧紧是把代码流程给讲了一遍,当涉及到更具体一些知识,比如...
  • VSync 信号处理

    2020-07-29 15:05:15
    Android 系统每隔 16ms 发出 VSYNC 信号,触发对 UI 进行渲染,VSync 是 Vertical Synchronization(垂直同步)的缩写,是一种在PC上很早就广泛使用的技术,可以简单的把它认为是一种定时中断。而在 Android 4.1(JB)...
  • vsync是节奏的带动者,从硬件到软件,从底层到上层,所有显示模块都按照vsync的节奏运行和等待。 2. TE信号 如此重要的vsync是如何产生的呢? vsync的产生是一个从底向上的过程,需要从下位机到上位机的逐层处理。 ...
  • 文章目录 一、背景和疑问 二、显示系统基础知识 2.1 基础概念 2.2 双缓存 2.2.1 画面撕裂 原因 2.2.2 双缓存 2.2.3 VSync 三、Android屏幕刷新机制 3.1 Android4.1之前的问题 3.2 drawing with VSync 3.3 三缓存 四...
  • SurfaceFlinger模块下的HWComposer,其中一个功能就是产生硬件的vsync。SurfaceFlinger_hwc1.cpp初始化HWComposer对象,同时传入参数SurfaceFlinger本身,作为回调事件的接收方。void SurfaceFlinger::init() { ...
  • 谷歌官方发布视频
  • 1 mtk采用的是:硬件VSYNC线程2.Mtk通过surfaceflinger注册了3个eventthread2.1DispSyncThread如何区分和执行vssyncsrc和sfVsyncSrc的vs?2.2 vssyncsrc和sfVsyncSrc的事件接受者什么创建2.3EventControl线程///////...
  • Android Systrace 基础知识(7) - Vsync 解读

    千次阅读 2020-06-02 08:58:33
    本文是 Systrace 系列文章的第七篇,主要是是介绍 Android 中的 Vsync 机制。文章会从 Systrace 的角度来看 Android 系统如何基于 Vsync 每一帧的展示。Vsync 是 Systrace 中一个非常关键的机制,虽然我们在操作手机...
  • Android 显示刷新机制、VSYNC和三重缓存机制 为了理解 APP 是如何进行渲染的,我们就必须了解手机硬件是如何工作的,也必须理解什么是 VSYNC。 首先,我们需要了解2个相关概念: 刷新率(Refresh Rate):代表了...
  • 其实就是向底层请求Vsync,等到下一个Vsync到了之后就开始绘制,同样这里的invalidate函数也是类似的功能,也会请求Vsync,收到Vsync之后开始合成,这里的Vsync是SurfaceFlinger进程的,顺便提一句,一个Vsync会被...
  • 文章目录UI 卡顿定义卡顿原因及常见解决方式VSync定义作用工作原理Buffer 缓存机制单缓存双缓存三缓存Choreographer使用源码分析 UI 卡顿定义 用户角度:APP 操作比较缓慢,响应不及时,列表滑动卡顿,动画刷新不...
  • 一、Vsync简介:屏幕的刷新过程是每一行从左到右(行刷新,水平刷新,Horizontal Scanning),从上到下(屏幕刷新,垂直刷新,Vertical Scanning)。当整个屏幕刷新完毕,即一个垂直刷新周期完成,会有短暂的空白期,...
  • hw vsync

    2021-07-28 23:16:46
    HW vsync的间隔是在resyncToHardwareVsync中得到的, const nsecs_t period =getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
  • vsync信号

    千次阅读 2018-03-28 20:46:03
    上个小节说明了HWC模块的加载等操作,并且也说明了HWC模块主要用于产生vsync信号,现在的问题是, vsync信号是如何产生的呢? 又是如何传输到SurfaceFlinger 中,处理的呢?在上面的论述中,HWComposer 的构造方法在加载完...
  • 随时等待mSendFd的写入 这篇文章就来分析什么时候mSendFd会写入数据,以及如何传递到应用层,核心函数nativeScheduleVsync,这个函数用于应用层向native层注册监听下一次Vsync信号,在UI需要刷新时调用 ...
  • Android vsync 信号生成源码分析

    千次阅读 2021-01-07 09:10:00
    vsync 信号在 SurfaceFlinger 中生成,并且有硬件、软件两种生成方式❞仅通过这种高度总结的描述,我们能得到多少信息呢?具体是如何生成?是优先选硬件生成还是软件生成还是...
  • 文章目录1 什么是VSYNC?1.1 帧率和刷新频率1.2 VSYNC2 1 什么是VSYNC? 1.1 帧率和刷新频率 在讲解 VSYNC 之前,需要了解两个相关的概念: Refresh Rate:屏幕在1秒内刷新屏幕的次数,这取决于硬件的固定参数,...
  • VSync信号

    千次阅读 2017-06-28 08:57:47
     在我们详细分析SurfaceFlinger之前要了解一下VSync信号,为下一节分析Vsync工作原理打下基础。 VSync信号相关  首先我们要了解以下几个概念: 屏幕刷新率  即
  • 快速理解 VSync

    千次阅读 2019-02-19 15:13:30
    前言 ... VSync 是啥  VSync 有啥作用  单缓存  双重缓存Double Buffer  三重缓存Triple Buffer  更多文章 前言 本文讲解 VSync 产生的原因及其作用。内容涉及如下方面:  帧率 vs 屏幕...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 19,594
精华内容 7,837
关键字:

VSYNC