理解 VSync

前言

本文讲解 VSync 产生的原因及其作用。内容涉及如下方面:

  • 帧率 vs 屏幕刷新频率;
  • 单缓存,双重缓冲,三重缓存及各自的优缺点;
  • VSync 的工作过程;

帧率 vs 屏幕刷新频率

帧率

即 Frame Rate,单位 fps,是指 gpu 生成帧的速率,如 33 fps,60fps,越高越好。

屏幕刷新频率

即 Refresh Rate 或 Scanning Frequency,单位赫兹/Hz,是指设备刷新屏幕的频率,该值对于特定的设备来说是个常量,如 60hz。

如下图,屏幕的刷新过程是每一行从左到右(行刷新,水平刷新,Horizontal Scanning),从上到下(屏幕刷新,垂直刷新,Vertical Scanning)。当整个屏幕刷新完毕,即一个垂直刷新周期完成,会有短暂的空白期,此时发出 VSync 信号。所以,VSync 中的 V 指的是垂直刷新中的垂直/Vertical。

对于一个特定的设备,帧率和刷新频率没有必然的大小关系。

VSync 是啥

安卓系统中有 2 种 VSync 信号:屏幕产生的硬件 VSync 和由 SurfaceFlinger 将其转成的软件 Vsync 信号。后者经由 Binder 传递给 Choreographer。
硬件 VSync 是一个脉冲信号,起到开关或触发某种操作的作用。
更多信息请自行复习《计算机组成原理》或《数字电路与逻辑设计》等大学教材(就像考研备考的时候研友感慨的:出来混,迟早要还的,当时没学好,现在还得接着学)。

VSync 有啥作用

单缓存

在第一章我们讲到,帧率和刷新频率没有必然的大小关系。先记住这一点。
首先,我们来看下,没有引入 VSync 时,屏幕显示图像的工作流程。

如上图,CPU/GPU 向 Buffer 中生成图像,屏幕从 Buffer 中取图像、刷新后显示。这是一个典型的生产者——消费者模型。 理想的情况是帧率和刷新频率相等,每绘制一帧,屏幕显示一帧。而实际情况是,二者之间没有必然的大小关系,如果没有锁来控制同步,很容易出现问题。例如,当帧率大于刷新频率,当屏幕还没有刷新第 n-1 帧的时候,GPU 已经在生成第 n 帧了,从上往下开始覆盖第 n-1 帧的数据,当屏幕开始刷新第 n-1 帧的时候,Buffer 中的数据上半部分是第 n 帧数据,而下半部分是第 n-1 帧的数据,显示出来的图像就会出现上半部分和下半部分明显偏差的现象,我们称之为 “tearing”,如下图:

甚至更严重的“身首异处”:
在这里插入图片描述

双重缓存(Double Buffer)

注意,此处的“双缓冲”和计算机组成原理中的“二级缓存”是两回事。三重缓存也是如此。
为了解决单缓存的“tearing”问题,双重缓存和 VSync 应运而生。双重缓存模型如下图:

两个缓存区分别为 Back Buffer 和 Frame Buffer。GPU 向 Back Buffer 中写数据,屏幕从 Frame Buffer 中读数据。VSync 信号负责调度从 Back Buffer 到 Frame Buffer 的复制操作,可认为该复制操作在瞬间完成。其实,该复制操作是等价后的效果,实际上双缓冲的实现方式是交换 Back Buffer 和 Frame Buffer 的名字,更具体的说是交换内存地址(有没有联想到那道经典的笔试题目:“有两个整型数,如何用最优的方法交换二者的值?”),通过二位运算“与”即可完成,所以可认为是瞬间完成。

双缓冲的模型下,工作流程这样的:
在某个时间点,一个屏幕刷新周期完成,进入短暂的刷新空白期。此时,VSync 信号产生,先完成复制操作,然后通知 CPU/GPU 绘制下一帧图像。复制操作完成后屏幕开始下一个刷新周期,即将刚复制到 Frame Buffer 的数据显示到屏幕上。

在这种模型下,只有当 VSync 信号产生时,CPU/GPU 才会开始绘制。这样,当帧率大于刷新频率时,帧率就会被迫跟刷新频率保持同步,从而避免“tearing”现象。

注意,当 VSync 信号发出时,如果 GPU/CPU 正在生产帧数据,此时不会发生复制操作。屏幕进入下一个刷新周期时,从 Frame Buffer 中取出的是“老”数据,而非正在产生的帧数据,即两个刷新周期显示的是同一帧数据。这是我们称发生了“掉帧”(Dropped Frame,Skipped Frame,Jank)现象。

三重缓存(Triple Buffer)

双重缓存的缺陷在于:当 CPU/GPU 绘制一帧的时间超过 16 ms 时,会产生 Jank。更要命的是,产生 Jank 的那一帧的显示期间,GPU/CPU 都是在闲置的。
如下图,A、B 和 C 都是 Buffer。蓝色代表 CPU 生成 Display List,绿色代表 GPU 执行 Display List 中的命令从而生成帧,黄色代表生成帧完成。

如果有第三个 Buffer 能让 CPU/GPU 在这个时候继续工作,那就完全可以避免第二个 Jank 的发生了!

于是就有了三缓存:

工作原理同双缓冲类似,只是多了一个 Back Buffer。

需要注意的是,第三个缓存并不是总是存在的,只要当需要的时候才会创建。之所以这样,是因为三缓存会显著增加用户输入到显示的延迟时间。如上图,帧 C 是在第 2 个刷新周期产生的,但却是在第 4 个周期显示的。最坏的情况下,你会同时遇到输入延迟和卡顿现象。

更多文章

写博客的初衷不是分享,而是帮助自己总结整理、深化记忆。
也许本文不值一看,但是下面这些文章则不然:

<think>嗯,用户想了解VSync的典型应用场景以及它在图形处理和显示同步中的作用。首先,我需要回忆一下VSync的基本概念。VSync,也就是垂直同步,主要用来解决屏幕撕裂的问题,当显示器的刷新率和显卡的帧率不同步时,画面会出现撕裂的现象。VSync通过协调两者的刷新来避免这种情况。 接下来,我需要考虑VSync的应用场景。典型的场景包括游戏、视频播放、图形用户界面渲染等。在游戏中,开启VSync可以防止画面撕裂,但可能会引入输入延迟。视频播放中,VSync帮助视频帧与显示器的刷新率同步,确保流畅播放。图形界面比如操作系统中的窗口移动,VSync能减少闪烁和卡顿。 然后,用户还问到它在图形处理和显示同步中的作用。这里需要解释VSync如何作为同步信号,确保GPU在显示器刷新周期开始时传送新帧,避免中间更新导致的撕裂。同时,它可能涉及双缓冲或三缓冲技术,这些技术如何与VSync配合工作。 还要注意引用用户提供的参考资料。引用[1]提到音频同步策略,可能与VSync的同步机制有类似之处,但需要区分音频和视频同步的不同。引用[3]提到的Systrace中的Offset,可能与VSync的时间调整有关,但不确定具体关联,可能需要谨慎引用。 需要确保回答结构清晰,分点列出应用场景和作用,并正确使用LaTeX格式。比如,独立公式如刷新率计算可能需要用$$...$$,而行内术语如垂直同步用$...$。但用户的问题可能不需要复杂公式,所以可能用不到太多数学表达式,但还是要检查是否符合系统级指令的要求。 最后,生成的相关问题要围绕VSync,比如它的优缺点、实现原理、与FreeSync的区别等。确保问题有逻辑性,能帮助用户深入理解。 总结思考步骤:解释VSync的基本原理,列举典型应用场景,详细说明每个场景下的作用,结合同步机制和缓冲技术,适当引用提供的资料,最后生成相关问题。</think>### VSync的典型应用场景及作用分析 #### 一、VSync的核心作用 VSync(垂直同步)的核心是通过**同步GPU渲染帧率与显示器刷新率**,解决画面撕裂问题。其原理是强制GPU在显示器完成当前刷新周期(即垂直消隐期)后才能提交新帧,避免帧缓冲区在屏幕刷新中途被修改[^1]。 #### 二、典型应用场景 1. **游戏渲染** - **问题**:高帧率游戏(如120 FPS)在60Hz显示器上可能导致**帧覆盖不同步** - **VSync作用**:锁定GPU输出帧率为显示器刷新率的整数倍(如60 FPS),确保每帧完整显示 - **代价**:可能引入输入延迟,可通过三重缓冲优化[^2] 2. **视频播放** - **问题**:24/30 FPS视频在60Hz显示器需通过*3:2 Pulldown*等技术匹配 - **VSync作用**:协调视频解码器输出与显示时序,实现帧呈现时间对齐 $$t_{present} = \frac{n}{f_{refresh}} \quad (n \in \mathbb{Z}^+)$$ 3. **图形界面渲染** - **问题**:窗口管理器合成多个应用表面时可能产生**渲染竞争** - **VSync作用**:统一所有合成操作时序,保证界面元素更新原子性[^3] #### 三、关键技术实现 | 技术 | 原理 | VSync关联 | |---------------|-----------------------------|------------------------------| | 双缓冲 | 前台缓冲区显示,后台缓冲区渲染 | 通过VSync信号切换缓冲区 | | 自适应同步 | 动态调整显示器刷新率 | 替代传统VSync的固定频率约束 | | 预测性渲染 | 基于VSync时间戳预计算帧内容 | 减少CPU-GPU管线延迟 | #### 四、性能影响示例 ```cpp // Vulkan显示同步实现片段 VkPresentInfoKHR presentInfo = { .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .waitSemaphoreCount = 1, .pWaitSemaphores = &renderCompleteSemaphore, // 等待渲染完成信号量 .swapchainCount = 1, .pSwapchains = &swapchain, .pImageIndices = &imageIndex, .pResults = nullptr }; vkQueuePresentKHR(presentQueue, &presentInfo); // 提交呈现请求 ``` *此代码展示了现代图形API如何通过信号量机制实现VSync同步[^2]*
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值