Android显示vsync信号的虚拟化和处理流程

本文深入探讨Android 4.4后引入的黄油计划,重点关注SurfaceFlinger如何处理vsync信号的虚拟化。从vsync的生成到传递,详细解析了SurfaceFlinger的初始化过程,以及信号如何唤醒和驱动线程。通过分析EventThread、DispSyncThread和DispSyncSource的角色,展示了Android系统中vsync信号从发生到应用层的完整路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

Android系统在4.4之后加入了黄油计划,surfaceflinger对显示的处理也变得复杂起来。由于添加了vsyn虚拟化机制,app将要显示的内容和surfaceflinger对显示内容的合成分成了两个部分,而两者开始的信号都是从vsync发出的。这里就涉及vsync信号的发生和传递,并且考虑到性能原因,信号的发生和传递都是按需进行的。因此,研究vsync信号的虚拟化及其处理,有助于理解线程的休眠唤醒和surfaceflinger的架构。

本文参考三篇文章:

https://blog.youkuaiyun.com/jinzhuojun/article/details/17293325

https://blog.youkuaiyun.com/houliang120/article/details/50908098

https://blog.youkuaiyun.com/kc58236582/article/details/52763534

一:SurfaceFlinger的初始化

init{    
// start the EventThread
    sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
            vsyncPhaseOffsetNs, true);
    mEventThread = new EventThread(vsyncSrc);
    sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
            sfVsyncPhaseOffsetNs, false);
    mSFEventThread = new EventThread(sfVsyncSrc);//先开线程
    mEventQueue.setEventThread(mSFEventThread);//然后在设置connection

    mEventControlThread = new EventControlThread(this);
    mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY);
}

 

Vsync发出后,调用SF的方法:

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);
    nsecs_t next_vsync = mNextFakeVSync;
    nsecs_t sleep = next_vsync - now;
    if (sleep < 0) {
        // we missed, find where the next vsync should be
        sleep = (period - ((now - next_vsync) % period));
        next_vsync = now + sleep;
    }
    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) {
        mHwc.mEventHandler.onVSyncReceived(0, next_vsync);
    }

    return true;
}

进入SF的方法:

void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
    bool needsHwVsync = false;

    { // Scope for the lock
        Mutex::Autolock _l(mHWVsyncLock);
        if (type == 0 && mPrimaryHWVsyncEnabled) {
            needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);
        }
    }

    if (needsHwVsync) {
        enableHardwareVsync();
    } else {
        disableHardwareVsync(false);
    }
}

进入

bool DispSync::addResyncSample(nsecs_t timestamp) {
    Mutex::Autolock lock(mMutex);

    size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
    mResyncSamples[idx] = timestamp;

    if (mNumResyncSamples < MAX_RESYNC_SAMPLES) {
        mNumResyncSamples++;
    } else {
        mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES;
    }

    updateModelLocked();

    if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) {
        resetErrorLocked();
    }

    if (runningWithoutSyncFramework) {
        // If we don't have the sync framework we will never have
        // addPresentFence called.  This means we have no way to know whether
        // or not we're synchronized with the HW vsyncs, so we just request
        // that the HW vsync events be turned on whenever we need to generate
        // SW vsync events.
        return mThread->hasAnyEventListeners();
    }

    return mPeriod == 0 || mError > errorThreshold;
}

进入

void DispSync::updateModelLocked() {
    if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) {
        nsecs_t durationSum = 0;
        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 = durationSum / (mNumResyncSamples - 1);

        double sampleAvgX = 0;
        double sampleAvgY = 0;
        double scale = 2.0 * M_PI / double(mPeriod);
        for (size_t i = 0; 
### vsync信号变化与帧绘制的关系 #### 帧绘制的基本原理 Vsync(垂直同步)信号是一种定时脉冲信号,其核心目的是确保显示器的刷新率与图形渲染速度保持一致[^5]。这种同步可以有效避免图像撕裂现象的发生,提升用户体验。 在 Android 系统中,Vsync 信号不仅用于驱动屏幕刷新,还参与应用层合成层的任务调度。具体来说: - **硬件层面**:由 HWComposer 提供真实的 VSync 信号[^3]。 - **虚拟化层面**:通过 DispSyncThread 将真实 VSync 信号转化为多个虚拟化VSync 信号,分别服务于 App SurfaceFlinger[^1]。 - **任务触发**:当 VSync 信号到来时,Choreographer 在应用层负责协调动画更新、输入事件处理以及视图重绘;SurfaceFlinger 则在原生层完成最终的画面合成并提交给显示屏[^4]。 #### vsync信号状态转换及其意义 Vsync 信号是一个周期性的高低电平切换信号,在一个完整的周期内经历两次重要的状态转变——从低到高(上升沿)从高到低(下降沿)。以下是这两种状态变换的具体含义及它们如何映射至帧绘制过程中的各个阶段: 1. **从0到1 (上升沿)** 上升沿通常标志着一个新的显示周期开始的时间点。对于帧绘制而言,这代表了当前帧计算启动的关键时刻: - 在此瞬间,Android 系统会通知应用程序该准备下一帧的内容了。 - 对于应用层,这意味着 Choreographer 可能会被唤醒以安排新的任务队列执行,比如调用 `doFrame` 方法来处理动画或者重新布局界面元素。 - 同时也意味着 SurfaceFlinger 开始考虑是否需要读取最新的缓冲区数据以便稍后进行画面组合工作[^2]。 2. **从1到0 (下降沿)** 下降沿则往往指示着本周期即将结束,并接近实际物理屏幕上像素更新发生的那一刻之前不久的位置。在这个时间窗口附近会发生如下动作: - 如果一切顺利的话,则此时应该已经完成了所有必要的准备工作,包括但不限于应用端生成的新图像内容已经被写入共享内存区域等待进一步传输。 - 接下来便是 SurfaceFlinger 把这些预处理完毕的数据按照既定顺序排列好之后发送出去让 GPU 或者专用显示控制器去加载进而展示出来形成可见的一帧画面^。 - 随即进入下一个循环继续重复上述步骤直到程序终止运行为止。 值得注意的是,尽管理论上每次遇到边沿都会引发相应行为但是实际情况可能因为各种因素存在差异例如负载过高可能导致丢弃部分帧等情况发生. ```python def frame_drawing_process(vsync_signal): if vsync_signal == 'rising_edge': # From 0 to 1 print("Start preparing next frame.") choreographer_schedule_tasks() # Application layer task scheduling. elif vsync_signal == 'falling_edge': # From 1 to 0 surface_flinger_composite_and_display() print("Display the prepared frame on screen.") def choreographer_schedule_tasks(): pass # Placeholder for application-level tasks like animations or layout updates. def surface_flinger_composite_and_display(): pass # Placeholder for native-layer composition and display submission. ``` ### 结论 综上所述,Vsync 信号的状态改变直接关联到了每帧图像生产的各个环节当中起到了至关重要的节奏把控角色;其中,"0转1"(上升沿)侧重于开启新一轮制作流程而"1变回0"(下降沿)更偏向于是最后呈现成果前的最后一刻确认节点.[^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值