Android研究_Hardware Composer_3 Vsync

3 篇文章 0 订阅

3.3 Vsync

3.3.1 Vsync的作用

Vsync用于画面同步,避免在画面没有完成绘制的时候就进行刷新,而导致出现画面撕裂的情况。

Vsync的作用有两个:

1)解决画面撕裂的情况

2)Cpu时间片分配不合理导致的jank的情况;

出现Jank(重复帧)的首先原因就在于第二帧没有及时的绘制(当然即使第二帧及时绘制,也依然可能出现Jank,这就是同时引入三重缓冲的作用)。

而引入vsync之后,要求一旦VSync出现后,立刻就开始执行下一帧的绘制工作。这样就可以大大降低Jank出现的概率。

3)CPU过度渲染

VSYNC引入后,要求绘制也只能在收到VSYNC消息之后才能进行,因此,也就杜绝了另外一种极端情况的出现—-CPU(GPU)一直不停的进行绘制,帧的生成速度高于屏幕的刷新速度,导致生成的帧不能被显示,只能丢弃,这样就出现了丢帧的情况—-引入VSYNC后,绘制的速度就和屏幕刷新的速度保持一致了。

3.3.2 软件模拟Vsync

Android系统中VSYNC信号分为两种,一种是硬件生成的信号,一种是软件模拟的信号。硬件信号是由HardwareComposer提供的,HWC封装了相关的HAL层,如果硬件厂商提供的HAL层实现能定时产生VSYNC中断,则直接使用硬件的VSYNC中断,否则HardwareComposer内部会通过VSyncThread来模拟产生VSYNC中断。

VsyncThread产生模拟Vsync中断。我们直接来看看threadLoop函数的实现。在threadLoop中是一些很简单的时间计算。然后sleep,等到Vsync信号的时候,就调用mHwc.mEventHandler.onVSyncReceived(0,next_vsync)函数来告知VSYNC的到来。

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);

// 下一次Vsync到来的时间

nsecs_t next_vsync = mNextFakeVSync;

// 为了等待下一个vsync到来应该休眠的时间

nsecs_t sleep = next_vsync - now;

// 错过了VSYNC的时间

    if (sleep < 0) {

        // we missed, find where the next vsync should be

        //重新计算下应该休息的时间

        sleep = (period - ((now - next_vsync) % period));

        //更新下次VSYNC的时间

        next_vsync = now + sleep;

}

//更新下下次VSYNC的时间

    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) {

//休眠之后,到了该发生VSYNC的时间了

        mHwc.mEventHandler.onVSyncReceived(0, next_vsync);

    }

 

    return true;

}

那么mHwc.mEventHandler.onVSyncReceived(0,next_vsync);执行的是哪个方法呢?我们注意到mEventHandler实际上是HWC创建时被传入的。我们来看下HWC创建时的代码:

init@surfaceflinger.cpp

    // Initialize the H/W composer object.  There may or may not be an

    // actual hardware composer underneath.

    mHwc = new HWComposer(this,

            *static_cast<HWComposer::EventHandler *>(this));

 

@HWComposer.cpp

HWComposer::HWComposer(

        const sp<SurfaceFlinger>& 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<MAX_HWC_DISPLAYS ; i++) {

        mLists[i] = 0;

    }

这个mEventHandler就是指向SurfaceFlinger。因此可以知道当VSYNC到来的时候,Surfaceflinger的OnVSyncReceived函数处理这个消息。

3.3.3 硬件实现vsync

从HWComposer的构造函数来看,如果存在硬件合成模块的话,就会进行procs操作指针的赋值:

    if (mHwc) {

        ALOGI("Using %s version %u.%u", HWC_HARDWARE_COMPOSER,

              (hwcApiVersion(mHwc) >> 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);

        }

同vsync相关的函数处理指针为hook_vsync。当HWC有vsync信号生成时,硬件模块会通过procs.vsync来通知软件部分,因此也就是调用了hook_vsync函数。

void HWComposer::hook_vsync(const struct hwc_procs* procs, int disp,

        int64_t timestamp) {

    cb_context* ctx = reinterpret_cast<cb_context*>(

            const_cast<hwc_procs_t*>(procs));

    ctx->hwc->vsync(disp, timestamp);

}

 

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

//只有真实的硬件设备才会产生VSYNC

    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=%" PRId64 ")",

                        timestamp);

                return;

            }

 

            mLastHwVSync[disp] = timestamp;

        }

 

        char tag[16];

        snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp);

        ATRACE_INT(tag, ++mVSyncCounts[disp] & 1);

// 依旧是调用mEventHandler.onVSyncReceived

        mEventHandler.onVSyncReceived(disp, timestamp);

    }

}

因此,可以看到,不管是软件模拟VSYNC还是硬件模块产生的VSYNC,当VSYNC信号到来的时候,都是执行SurfaceFlinger的onVSyncReceived。

3.3.4 硬件VSYNC时procs.vsync回调时机

在3.1节的HWComposer构建函数的代码中,有一段注册回调函数的的代码:

        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);

        }

 

中代码中3.2节的知识点,我们可以快递定位到mHwc->registerProcs(mHwc,&mCBContext->procs);实际上面就是调用hwc_registerProcs函数。

/*

 * Save callback functions registered to HWC

 */

static void hwc_registerProcs(struct hwc_composer_device_1* dev,

                              hwc_procs_t const* procs)

{

    ALOGI("%s", __FUNCTION__);

    hwc_context_t* ctx = (hwc_context_t*)(dev);

    if(!ctx) {

        ALOGE("%s: Invalid context", __FUNCTION__);

        return;

    }

    ctx->proc = procs;

 

    // Now that we have the functions needed, kick off

// the uevent & vsync threads、

// 初始化uevent线程和vsync线程

    init_uevent_thread(ctx);

    init_vsync_thread(ctx);

}

 

void init_vsync_thread(hwc_context_t* ctx)

{

    int ret;

    pthread_t vsync_thread;

ALOGI("Initializing VSYNC Thread");

// 处理函数为vysnc_loop

    ret = pthread_create(&vsync_thread, NULL, vsync_loop, (void*) ctx);

    if (ret) {

        ALOGE("%s: failed to create %s: %s", __FUNCTION__,

              HWC_VSYNC_THREAD_NAME, strerror(ret));

    }

}

 

我们来看看vsync_loop函数的具体实现:

static void *vsync_loop(void *param)

{

    hwc_context_t * ctx = reinterpret_cast<hwc_context_t *>(param);

 

    char thread_name[64] = HWC_VSYNC_THREAD_NAME;

    prctl(PR_SET_NAME, (unsigned long) &thread_name, 0, 0, 0);

    setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY +

                android::PRIORITY_MORE_FAVORABLE);

 

    const int MAX_DATA = 64;

    char vdata[MAX_DATA];

    bool logvsync = false;

 

    struct pollfd pfd[2];

    int fb_fd[2];

    uint64_t timestamp[2];

int num_displays;

 

// 如果设置了"debug.hwc.fakevsync"属性,使用假的vsync

    char property[PROPERTY_VALUE_MAX];

    if(property_get("debug.hwc.fakevsync", property, NULL) > 0) {

        if(atoi(property) == 1)

            ctx->vstate.fakevsync = true;

    }

 

    if(property_get("debug.hwc.logvsync", property, 0) > 0) {

        if(atoi(property) == 1)

            logvsync = true;

    }

 

    if (ctx->mExtDisplay->getHDMIIndex() > 0)

        num_displays = 2;

    else

        num_displays = 1;

 

    char vsync_node_path[MAX_SYSFS_FILE_PATH];

    for (int dpy = HWC_DISPLAY_PRIMARY; dpy < num_displays; dpy++) {

        snprintf(vsync_node_path, sizeof(vsync_node_path),

                "/sys/class/graphics/fb%d/vsync_event",

                dpy == HWC_DISPLAY_PRIMARY ? 0 :

                ctx->mExtDisplay->getHDMIIndex());

        ALOGI("%s: Reading vsync for dpy=%d from %s", __FUNCTION__, dpy,

                vsync_node_path);

        fb_fd[dpy] = open(vsync_node_path, O_RDONLY);

// 如果打开"/sys/class/graphics/fb%d/vsync_event"失败,也使用假的vsync

        if (fb_fd[dpy] < 0) {

            // Make sure fb device is opened before starting this thread so this

            // never happens.

            ALOGE ("%s:not able to open vsync node for dpy=%d, %s",

                    __FUNCTION__, dpy, strerror(errno));

            if (dpy == HWC_DISPLAY_PRIMARY) {

                ctx->vstate.fakevsync = true;

                break;

            }

        }

        // Read once from the fds to clear the first notify

        pread(fb_fd[dpy], vdata , MAX_DATA, 0);

 

        pfd[dpy].fd = fb_fd[dpy];

        if (pfd[dpy].fd >= 0)

            pfd[dpy].events = POLLPRI | POLLERR;

    }

 

    if (LIKELY(!ctx->vstate.fakevsync)) { // 默认fakevsync=false

        do {

            int err = poll(pfd, num_displays, -1);

            if(err > 0) {

                for (int dpy = HWC_DISPLAY_PRIMARY; dpy < num_displays; dpy++) {

                    if (pfd[dpy].revents & POLLPRI) {

                        int len = pread(pfd[dpy].fd, vdata, MAX_DATA, 0);

                        if (UNLIKELY(len < 0)) {

                            // If the read was just interrupted - it is not a

                            // fatal error. Just continue in this case

                            ALOGE ("%s: Unable to read vsync for dpy=%d : %s",

                                    __FUNCTION__, dpy, strerror(errno));

                            continue;

                        }

                        // extract timestamp

                        if (!strncmp(vdata, "VSYNC=", strlen("VSYNC="))) {

                            timestamp[dpy] = strtoull(vdata + strlen("VSYNC="),

                                    NULL, 0);

                        }

                        // send timestamp to SurfaceFlinger

                        ALOGD_IF (logvsync,

                                "%s: timestamp %llu sent to SF for dpy=%d",

                                __FUNCTION__, timestamp[dpy], dpy);

//"/sys/class/graphics/fb%d/vsync_event"找到VSYNC时间戳,回调函数。

                        ctx->proc->vsync(ctx->proc, dpy, timestamp[dpy]);

                    }

                }

 

            } else {

                ALOGE("%s: vsync poll failed errno: %s", __FUNCTION__,

                        strerror(errno));

                continue;

            }

        } while (true);

 

    } else {

         // 使用假的时间戳,强制60HZ,回调函数。

        //Fake vsync is used only when set explicitly through a property or when

        //the vsync timestamp node cannot be opened at bootup. There is no

        //fallback to fake vsync from the true vsync loop, ever, as the

        //condition can easily escape detection.

        //Also, fake vsync is delivered only for the primary display.

        do {

            usleep(16666);

            timestamp[HWC_DISPLAY_PRIMARY] = systemTime();

            ctx->proc->vsync(ctx->proc, HWC_DISPLAY_PRIMARY,

                    timestamp[HWC_DISPLAY_PRIMARY]);

 

        } while (true);

    }

 

    for (int dpy = HWC_DISPLAY_PRIMARY; dpy <= HWC_DISPLAY_EXTERNAL; dpy++ ) {

        if(fb_fd[dpy] >= 0)

            close (fb_fd[dpy]);

    }

 

    return NULL;

}


版权所有,转载请注明出处:http://www.junsion.icoc.bz/ by 小丑

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值