Android SurfaceFlinger——信号同步原理(五十一)
经过前面系列文章的学习,我们的已经理解了 SurfaceFlinger 运行机制以及同步机制,但是SurfaceFlinger 又是以什么方法是把需要刷新的信号发送给 App 进程的。
一、VSync简介
垂直同步(Vertical Synchronization,简称 VSync)是一种用于同步视频信号和显示设备刷新率的技术,以确保视频帧的显示与屏幕的刷新周期相匹配。VSync 对于防止屏幕撕裂(tearing)、保证图像的连贯性和减少输入延迟非常重要。
VSync信号
VSync 信号就是在垂直消隐期产生的一个脉冲信号,用来指示显示器已经完成了当前帧的显示,并且准备好接收下一帧的数据。这个信号对于视频源(如显卡)来说是一个重要的同步信号,它告诉视频源何时可以开始传输下一帧图像数据。
VSync作用
在图形渲染中,VSync的作用是将GPU的帧渲染速率限制在显示器的刷新率之内。如果没有VSync,GPU可能会在任意时刻将帧发送给显示器,这可能导致屏幕撕裂现象——即屏幕上半部分显示的是前一帧的内容,而下半部分显示的是新帧的内容,因为两帧是在同一垂直刷新周期内交错显示的。
屏幕刷新原理
显示器通过逐行扫描的方式来显示图像。每一帧图像由一系列的水平扫描线组成,这些扫描线在垂直方向上依次被点亮。当所有的扫描线完成一次刷新后,显示器会进入一个短暂的垂直消隐期(Vertical Blanking Interval,VBI),在这个期间,显示器不会显示任何图像,而是重新定位电子束,准备下一次的扫描。
VSync工作原理
当启用 VSync 时,GPU 会等待下一个 VSync 信号到达,然后再将渲染完成的帧提交给显示器。这意味着 GPU 的帧渲染速率将被同步到显示器的刷新率,从而避免屏幕撕裂。然而,这也可能引入额外的延迟,因为 GPU 可能需要等待直到下一个 VSync 信号才开始渲染下一帧。
在现代的计算机系统中,VSync 信号的生成和处理通常是由显示驱动程序和硬件(如显卡)协同完成的。例如,在 Android 系统中,VSync 信号是由 DispSync 线程根据硬件控制器(HWC)产生的 VSync 信号进行采样和建模,然后输出软件 VSync 信号(SW_VSYNC),这个信号再根据 SurfaceFlinger 和应用程序的相位偏移进行调整,分别输出给 SurfaceFlinger 和应用程序。
总之,VSync 是一种确保视频输出与显示设备刷新率同步的技术,它通过控制 GPU 的帧提交时间来防止屏幕撕裂并保证视觉效果的平滑性。
二、源码分析
1、SurfaceFlinger.cpp
源码位置:/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
initScheduler
scheduler::ConnectionHandle mAppConnectionHandle;
scheduler::ConnectionHandle mSfConnectionHandle;void SurfaceFlinger::initScheduler(const sp<DisplayDevice>& display) {……const auto configs = mVsyncConfiguration->getCurrentConfigs();const nsecs_t vsyncPeriod = currRefreshRate.getPeriodNsecs();// 创建应用连接mAppConnectionHandle = mScheduler->createConnection("app", mFrameTimeline->getTokenManager(),/*workDuration=*/configs.late.appWorkDuration,/*readyDuration=*/configs.late.sfWorkDuration,impl::EventThread::InterceptVSyncsCallback());// 创建 SurfaceFlinger 连接mSfConnectionHandle = mScheduler->createConnection("appSf", mFrameTimeline->getTokenManager(),/*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),/*readyDuration=*/configs.late.sfWorkDuration, [this](nsecs_t timestamp) {mInterceptor->saveVSyncEvent(timestamp);});// 初始化 VSyncmScheduler->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),configs.late.sfWorkDuration);……
}
该函数主要初始化与调度器 (mScheduler) 的连接,用于处理应用和 SurfaceFlinger 本身的 VSync 事件。以确保应用和 SurfaceFlinger 自身能够正确地处理 VSync 事件,从而实现流畅的画面更新。
2、Scheduler.cpp
源码位置:/frameworks/native/services/surfaceflinger/Scheduler/Scheduler.cpp
createConnection
ConnectionHandle Scheduler::createConnection(const char* connectionName, frametimeline::TokenManager* tokenManager,std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration, impl::EventThread::InterceptVSyncsCallback interceptCallback) {// 创建VSync源auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration);// 创建VSync节流回调,用于控制VSync信号的频率auto throttleVsync = makeThrottleVsyncCallback();// 创建获取VSync周期的函数auto getVsyncPeriod = makeGetVsyncPeriodFunction();// 构建事件线程auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), tokenManager,std::move(interceptCallback), std::move(throttleVsync), std::move(getVsyncPeriod));// 创建连接句柄return createConnection(std::move(eventThread));
}
这里从创建基础的同步机制到构建完整的事件处理线程,最终形成一个可以被外部系统使用的连接句柄。并且可以看到,初始化的 EventThread 持有一个十分核心对象 VSyncSource。
makePrimaryDispSyncSource
std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(const char* name, std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration, bool traceVsync) {return std::make_unique<scheduler::DispSyncSource>(mVsyncSchedule->getDispatch(),mVsyncSchedule->getTracker(), workDuration, readyDuration, traceVsync, name);
}
可以看到,这里主要是创建并返回一个 VSyncSource,用于生成垂直同步(VSync)信号的源头。
createConnection
ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) {// 生成新的连接ID,用于唯一标识创建的连接句柄const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++};ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id);// 内部连接创建auto connection = createConnectionInternal(eventThread.get());std::lock_guard<std::mutex> lock(mConnectionsLock);// 存储连接信息mConnections.emplace(handle, Connection{connection, std::move(eventThread)});return handle;
}
该函数不仅创建了一个唯一的连接句柄,还将其与一个具体的 EventThread 实例关联起来。
createConnectionInternal
sp<EventThreadConnection> Scheduler::createConnectionInternal(EventThread* eventThread, ISurfaceComposer::EventRegistrationFlags eventRegistration) {return eventThread->createEventConnection([&] { resync(); }, eventRegistration);
}
这里利用给定的 EventThread 实例创建一个内部的事件连接(EventThreadConnection)。
3、EventThread.cpp
EventThread 扮演了处理 VSync(垂直同步)信号的核心角色。SurfaceFlinger 是负责窗口和显示合成的主要服务,它确保所有应用程序的帧在屏幕上正确且及时地呈现。
源码位置:/frameworks/native/services/surfaceflinger/Scheduler/EventThread.cpp
createEventConnection
sp<EventThreadConnection> EventThread::createEventConnection(ResyncCallback resyncCallback,ISurfaceComposer::EventRegistrationFlags eventRegistration) const {return new EventThreadConnection(const_cast<EventThread*>(this),IPCThreadState::self()->getCallingUid(), std::move(resyncCallback), eventRegistration);
}
该函数不仅创建了 EventThreadConnection 实例,还初始化了它的关键属性,如事件线程的引用、调用者的 UID、重新同步的回调以及事件注册的标志。这使得 EventThreadConnection 能够作为接口,让外部组件与事件线程进行交互,同时遵循预定的事件处理规则和策略。
EventThread的角色
在 SurfaceFlinger 中,存在两种类型的 EventThread:
- App 端的 EventThread:负责监听应用程序请求的 VSync 信号,通常用于游戏或动画应用,需要精确的时间同步来保证流畅的视觉效果。
- SurfaceFlinger 端的 EventThread:直接与硬件交互,接收来自显示器的 VSync 信号,并将这些信号分发给所有需要同步的应用程序或系统组件。
VSync信号处理
- 硬件 VSync 信号接收:硬件层(HAL)的 EventThread(例如 SDM_EventThread)从显示控制器接收原始的 VSync 信号。
- 信号分发:这些信号被传递给 SurfaceFlinger 的 EventThread,它使用 DispSync 机制来同步这些信号。
- 信号处理:EventThread 通过 DispSyncSource 类来处理 VSync 信号,这涉及到计算和调整 VSync 周期,以适应不同应用的需求。
- 信号分发给客户端:EventThread 将 VSync 信号分发给注册了 VSync 监听器的所有客户端,包括应用程序和其他系统服务。
EventThread 通常会维护一个事件队列,用于存储接收到的 VSync 事件。它会定期检查事件队列,并唤醒等待 VSync 信号的线程。EventThread 可能还会实现一些优化,比如 VSync 信号的节流,以减少不必要的 CPU 唤醒和功耗。
在代码层面,EventThread 的实现可能涉及多个类和接口,如 DispSyncThread、DispSyncSource 和 EventThreadConnection等,它们共同协作来完成 VSync 信号的接收、处理和分发。
总之,EventThread 在 SurfaceFlinger 中是 VSync 信号处理的核心,它确保了系统和应用程序能够根据显示器的实际刷新率同步其渲染输出,从而提供平滑和一致的用户体验。