Derek Wilson:三重缓冲,为什么我们爱它[通俗易懂]

Derek Wilson:三重缓冲,为什么我们爱它[通俗易懂]什么是双重缓冲,垂直同步和三重缓冲?当电脑在显示器上显示东西时,它按照它的想法画一幅需要显示的图像(我们称之为缓冲区Buffer)并传输给显示器。在过去,只有一个缓冲区并不断的被电脑绘制和发送给显示器。这种做法有一些优势,但也有非常大的缺点。最值得注意的是,当物体在屏幕上进行了更新,他们往往会导致闪烁。计算机在绘制的同时发送内容。所有插图感谢劳拉.威尔逊提供。为了解决同一缓冲区

大家好,又见面了,我是你们的朋友全栈君。

什么是双重缓冲,垂直同步和三重缓冲?

当电脑在显示器上显示东西时,它按照它的想法画一幅需要显示的图像(我们称之为缓冲区Buffer)并传输给显示器。在过去,只有一个缓冲区并不断的被电脑绘制和发送给显示器。这种做法有一些优势,但也有非常大的缺点。最值得注意的是,当物体在屏幕上进行了更新,他们往往会导致闪烁。

Derek Wilson:三重缓冲,为什么我们爱它[通俗易懂]

计算机在绘制的同时发送内容。
所有插图感谢劳拉.威尔逊提供。

为了解决同一缓冲区内绘制的同时又读取的问题,使用双重缓冲,就成了最经济的方式。双重缓冲背后的原理是:计算机只绘制一个缓冲区(所谓的“后部”缓 冲),同时发送另一个缓冲(所谓的“前部”缓冲)到屏幕上。当计算机完成绘制后部缓冲时,图像绘制程序会执行缓冲区“交换”。这种交换不会移动任何东西: 唯一的改变是交换两个缓冲器的名称,前部缓冲区成为后部缓冲区的同时后部缓冲区成为前部缓冲区。

Derek Wilson:三重缓冲,为什么我们爱它[通俗易懂]

计算机绘制后部缓冲,前部缓冲发送给显示器。

经过缓冲交换,程序可以开始起在新的后部缓冲区绘制同时电脑传送新的前部缓冲区给显示器,直至下一次缓冲区交换发生。从此一切都和谐了,恩,大多数情况下如此。

这种形式的双重缓冲,“交换”随时可能发生。这意味着,在电脑发送数据给显示器的同时,就可能发生“交换”。在这种情况下,(同一帧下)其余的画面根据新 的前部缓冲器进行显示。如果新前部缓冲内容与旧的前部缓冲相差很大,就会观察到一种视觉上的“撕裂”感。这种类型的问题,常常出现在高帧率的FPS游戏 中,特别是转角处快速运动时看到。由于快速运动,每帧有很大的不同,当“交换”发生在前后差异很大的场景绘制途中,就会被察觉到同时干扰人的视觉感受。

最常见的做法是等待显示器准备充分时“交换”缓冲器。显示器准备充分意味着在完成绘制完上一幅图像后刚好赶上下个垂直刷新周期将会开始。所以同步缓冲区交换与垂直刷新被称作垂直同步 。

开启垂直同步确实能修复图像“撕裂”,它同时会设置游戏的内部帧率的最大值与显示器的的刷新率一致(大多数液晶面板通常是60Hz)。这可能会牺牲的性 能:如果游戏不能保持在每秒60帧的运行时,(图像绘制程序)会人为的增加延迟到图像帧输出之间来达到同步效果。哪怕是每一帧所需的绘制时间超过 16.67毫秒一点点(1/60秒)都可能导致性能可减少将近一半 。在这种情况下,帧速率将下降到每秒30帧,尽管游戏的运行速度极其接近60帧 。消除画面撕裂和保持帧数一致,确实有助于增加游戏流畅度,这是单纯的双重缓冲模式无法提供的。

同时开启垂直同步后的输入延迟将会成为更加严重的问题。这是因为人为加入的延迟增加了实际发生的事情(当帧被绘制时)和最终显示在屏幕上的画面的区别。输入延迟始终存在(我们不可能在绘制的瞬间实时的反映变化),但诀窍是,尽量减少它。

我们目前的选择一个是只进行双重缓冲但是会导致解决画面撕裂问题,二是进行垂直同步的双重缓存但是会影响性能和输入延迟。但是不要担心,有一种选择,结合了两者的优点,既不牺牲画面质量也不会影响真实的性能。这种选择就是三重缓冲。

Derek Wilson:三重缓冲,为什么我们爱它[通俗易懂]

计算机在发送前部缓冲时可以有两个后部缓冲区供使用。

光看这个名字就已经很明显了:三重缓冲使用三个缓冲区。这种额外的缓冲区使计算机有足够的空间来锁定一个正被送往显示器的缓冲区(以避免撕裂),同时也不限制图像的绘制速度(就算是有一个缓冲区被锁定,也能来回绘制剩下的两个缓冲区)。软件来回绘制两个后部缓冲器,当每一次刷新时,前部缓冲区就与最近完成的那个后部缓冲区交换。这确实需要一些额外的图形卡存储空间(大约15到25M之间),但与现代的图形卡动辄512MB显存来说这不构成什么问题。

换言之,有了三重缓冲我们获得了不开启垂直同步时高效的性能和相似的低输入延迟,同时还实现了开启垂直同步时的图像质量。

现在,重要的一点是,当你看开启了三重缓冲游戏的“帧数”时,你将不会看到实际的“性能”。这是因为帧计数器像FRAPS这种只能计算前部缓冲(就是目前 正在被显示器绘制的那个缓冲)换出的次数(即是我们通常所说的广义上的“帧数”)。在双重缓冲时,数据显示在每个帧绘制并被显示器显示的时候(也就是说, 如果另一帧在下一次刷新前绘制好了,它可能不会显示出来)。而三重缓冲,前部缓冲在每个刷新周期最多只“交换”一次 。

FRAPS软件仍在幕后的两个后部缓冲区上绘制时间。这意味着一旦发生前部缓冲区交换,与开启垂直同步的双重缓冲不同,我们并没有加入人为的延迟。同时也异于没有开启垂直同步的双重缓冲 ,一旦我们开始传输一个完整绘制好的帧给显示器时,我们不会在中途切换到另一个帧去。

最后讲一个三重缓冲会导致的问题。如果一个帧只比刷新完成晚很小一点点就被生成出来,在无垂直同步的双重缓冲情况下 ,将在屏幕上方附近产生画面撕裂,而其余部分则会比三重缓冲更准确的反应实时变化。即使在这种情况下,但至少帧的部分的画面元素双重缓冲和三重缓冲是一致 的,同时三重缓冲的延迟也非常难以察觉,至少比开启了垂直同步的双重缓冲延迟问题好得多。即使你认为这是无垂直同步的双重缓冲模式的优点,这优点也是建立 在很小几率(不影响视觉的画面撕裂)之上。

让我们带着这些理论通过例子来比较这三种绘制方式。

深度挖掘:疾驰的骏马为例

比起一堆数学计算和传统的时间图标,我们决定采用一个更加直观的表现形式。在图表中,我们会给大家演示随着时间电脑实际的生成的图像以及最终被显示器显示出来的图像。希望这会有助于说明这些方法数量和质量之间的差别。

我们使用的是一个已有的(来自维基百科上面的动画)的“游戏”例子:渲染一匹驰骋在屏幕上的马。最基础的一点是,我们的(显卡对)游戏绘制的速度比我们的 显示器刷新率高5倍(在一次缓冲区交换发生时它可以渲染5幅不同的图像)。在绘制每帧性的一致性上是虚构的,因为一些(复杂)帧将比另一些(简单帧)需要 更长的时间来绘制。我们去除掉这些变量以免使得的复杂性增加。我们这里以60Hz的刷新率和300 FPS游戏性能为基础讨论的时间和延迟,但我们并不想把图像绘制得太复杂。显然,这是一个理论上的例子,但它确实解释清楚了背后发生的事情。

首先,我们来看看关闭垂直同步时的双重缓冲。在这种情况下,缓冲区会在每一个图像绘制好后交换。这会导致最新的图像覆盖先前图像的局部。以下就是在这种情况下它看起来样子:

Derek Wilson:三重缓冲,为什么我们爱它[通俗易懂]

很好的性能 ,但画质不佳。

时间轴标记0至15 ,每两个标记之间为一个步骤,每个步骤是3.33毫秒。时间轴上每个缓冲区在3.3毫秒间隔期间就有一张图片,这张图片实时的显示了骑手和马的动作。底部 的大型图片这代表着每个垂直刷新周期在显示器上显示出来的图像。这就是我们在眼睛说看见的实际图像。其他(未绘制)帧的价值就在于最小化了输入延迟。

我们当然可以看到,在这个极端的情况下,画面撕裂可能是什么样子。在这个快速运动的例子中,我选择了只对合成其中三帧的动画,但在实际现实中可能更多也可 能更少。图像的宽度反映了实际中图形硬件传输图像给显示器绘制时所占用的时间。绘制的过程比刷新周期时间要少一些,由于我不是很精通显示器技术,所以具体 是多少时间我无从得知。按照我的假设,我用了大约一半的时间从发送帧到显示器显示出来(因此三个帧的部分被显示出来)。 。如果我不得不猜测的话,我想我高估发送帧到显示器所花费的时间。

对于上述情况, FRAPS将会显示300FPS,但实际人们得到完整显示的图像帧率仅仅只达到刷新率的最大值(在这个例子中, 60帧每秒)。一个帧完成渲染并开始在屏幕上绘制的延迟(这是输入延迟)将会小于3.3毫秒。

当我们打开垂直同步,画面撕裂消失了,但我们的实际性能下降并且输入延迟上升。以下是我们所看到的。

Derek Wilson:三重缓冲,为什么我们爱它[通俗易懂]

优良的画质,但糟糕的性能和输入延迟。

如果我们考虑所有这些图像都是一样的并且渲染的耗时都是一致的,我们就可以看到渲染“背后”的真实情况。在我们的第一幅图中没有撕裂的情况出现,但我们付 出代价是过时内容。此外,除了报告实际帧率为60FPS。计算机最终少做了大量的工作,当然,最大的牺牲就是显示器每秒钟最多显示60幅图像。

这时,我们为了消除画面撕裂的代价就是延迟从最多3.3毫秒增加到13.3毫秒。对于开启垂直同步刷新率为60Hz的显示器,渲染完成并显示出来的延迟将 会是完整的1/60秒(16.67毫秒),但是实际延迟有可能会更高。由于在前部缓冲区交换完成前没有更多的空间可以用来绘制帧,而如果图形卡绘制一幅图 像的时间本身就超过了一个循环周期的情况下再开启锤子同步将会更加严重的影响延迟。

现在移步到三重缓冲,我们可以看到它结合了上面两种办法的优势。

Derek Wilson:三重缓冲,为什么我们爱它[通俗易懂]

两全其美。

在这里。我们输入延迟降到了3.3ms,同时消除了画面撕裂。我们的实际性能回到了300FPS,当然这未必能从只监视前部缓冲的软件统计上显示出来。再次,每秒钟在显示器上最多只显示60幅图像,但是这60幅图像都是在刷新周期前最新绘制出来的。

当然这其中有些帧无垂直同步的双重缓冲时会显得比三重缓冲“新”一点,但是代价将会是潜在的图像撕裂。从挑剔的角度来看,如果你没有看到双重缓冲下的画面 撕裂,那么那些重新刷新的局部对前一帧来说差异也并不明显。换言之,只有当您看到画面撕裂,你才体会到这种方式(无垂直同步的双重缓冲)信息反馈及时的优 势。但是,即便信息反馈更为及时,在伴随着画面撕裂的情况下又有多大的用处呢?

结论

综上所述。三重缓冲使你有了开启/关闭垂直同步时的双重缓冲所有好处 。我们得到了流畅而且没有撕裂的帧。这些帧只在刷新时才被交换至前部缓冲,它们还拥有近似关闭垂直同步的双重缓冲时那样低的输入延迟。尽管“性能”并没有 得到正确的显示(FRAPS),启用三重缓冲时,图形硬件依然同未开启垂直同步的双重缓冲模式一样辛勤工作。三重缓冲确实要占用更多的图形卡显存,但对现 代的硬件来说,这算不上什么问题。

概括一下,从我们前面的例子里取出三幅帧来进行横向比较。

Derek Wilson:三重缓冲,为什么我们爱它[通俗易懂]

三重缓冲

Derek Wilson:三重缓冲,为什么我们爱它[通俗易懂]

双重缓冲

Derek Wilson:三重缓冲,为什么我们爱它[通俗易懂]

垂直同步下的双重缓冲

我们已经提出了定性和定量的论点论据以支持三重缓冲。所以,现在的问题是:这些数据有意义吗?人们会比阅读这篇文章之前更愿意开启三重缓冲吗?让我们来看看答案。

无论结果如何,我们真心希望本文有助于解释一个经常被忽视的选择。虽然它我们评测时来说没有什么意义,但三重缓冲是实用至上的。希望我们的展示能帮助我们的读者理解为什么他们应该给三重缓冲一个机会。

我们也希望更多的开发商将三重缓冲作为游戏的默认选项,因为它会给那些注重画质和性能的玩家提供最好的游戏体验。目前只有一小撮游戏把三重缓冲作为游戏的可选项, NVIDIA和AMD的驱动程序目前只允许强制OpenGL游戏开启三重缓冲。这确实需要改变,因为现在没有任何理由不让三重缓冲流行起来。

更新:

在评论中有很多讨论涉及到了我们在前文所谈到的“页翻转”(Page flipping)和实施“预先渲染队列”(Render ahead queue)的差异。在预渲染队列中,帧不会被丢弃。这意味着,如果队列已满,实际显示的画面会有更大的延迟。微软并没有在DirectX中实现三重缓冲,而是使用了这种预渲染队列(从0到8张, 3是默认值)的方式。

他们之间的主要技术差别在于“是否丢弃过时的帧”。预渲染队列强制旧的帧显示出来。队列可以帮助平滑快速帧(简单帧)和慢速帧(复杂帧)之间的差异。而代价就是更大的延迟(队列中的帧越多,就需要更多的时间去清空队列同时也让输入延迟更大) 。

为了保持画面流畅同时减少输入延迟,就必须维持一个限额的帧数目作为缓冲,同时丢弃掉那些过时的帧。这需要相当的智能调度程序来管理,具体操作细节已经超出了本文所讨论的范围。

一些游戏开发商提供了一个很小的预渲染队列,并称其为三重缓冲(因为它使用3个缓冲空间,微软对DirectX预渲染队列的定义是可以最多提供8个空间) 。当然不能简单的指责他们混淆是非,因为这个话题本身就有太多容易混淆的地方,而在某些特殊情况下(实际帧数小于或等于刷新率时)它起到的效果也确实接近于三重缓冲(但是在实际帧数高于刷新率时就大大不同,其输入延迟甚至高于垂直同步时的双重缓冲效果)。

这两种技术都允许显卡已经完成一帧的渲染后在等待垂直同步时继续工作。当使用双重缓冲(并没有使队列) ,当垂直同步启用后,每当下一帧绘制完毕,显卡就会停止工作,这样就造成了实际性能的下载。

当垂直同步关闭时,完成了双缓冲所需的任务后,通过提供一个预渲染队列就能用来平滑帧输出速度,但是一些过时的帧也被保留下来。这样可以保持帧率平稳而不 会出现大幅的变化,但(即使无垂直同步的双重缓冲)会增加画面延迟和输入延迟。即使没有开启垂直同步,预渲染队列也是多GPU系统高效工作所必需的。

因此,本文既是针对游戏玩家,也是为了开发者。如果你正在实施预渲染队列(又名翻转队列Flip queue),请不要把它称为“三重缓冲” ,因为这应留给我们前文所介绍的技术,以减少混乱。目前很多把“三重缓冲”作为选项的游戏,实际上是在用预渲染队列。我们认识到这可能会导致的混乱,我们 非常希望这篇文章和讨论有助于缓解这个问题。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/143243.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 九度 1480:最大上升子序列和(动态规划思想求最值)

    九度 1480:最大上升子序列和(动态规划思想求最值)

    2021年8月28日
    54
  • mybatis自定义分页_java分页查询接口的实现

    mybatis自定义分页_java分页查询接口的实现问题出现原因是集成mybaits时会自动加上selecttmp_page.*,rownumrow_idfrom(查询语句)tmp_page出现这个问题的原因是查询语句的列有重复的,直接查询是看不出来原因的,把重复的列名找出来然后修改…

    2022年10月4日
    0
  • circos 可视化手册- text 篇

    circos 可视化手册- text 篇欢迎关注微信公众号 生信修炼手册 不论是 heatmap 还是 scatter histogram 反应的都是基因组上某段区域对应的 value 值的分布 这里的 value 都是数值 对于 value 是字符串的情况 专门定义了 text 这种图表类型 用于展示 看一个 text 的实例 text 在图上就是一圈的字符串标记 字符串可以添加连线 表明对应的染色体位置 配置文件的写法如下 首先看下 fil

    2025年6月15日
    0
  • rabbitmq集群安装_java实现消息队列

    rabbitmq集群安装_java实现消息队列rabbitmq集群搭建失败解决随记1现象:2原因:3解决方法:1现象:1、各节点已改hosts,各节点ping节点名正常。2、md5sum.erlang.cookie各节点值一样。按以下步骤加入失败:$rabbitmqctlstop_app #停止rabbitmq服务$rabbitmqctlreset #清空节点状态$rabbitmqctljoin_clusterrabbit@rabbitmq3$rabbitmqctlstart_app

    2022年9月3日
    2
  • java如何实现换行_网页换行代码

    java如何实现换行_网页换行代码在taxtarea中输入的文本。如果含有回车或空格。在界面上显示的时候则不哪么正常。回车消失了,空格变短了。如何解决这个问题呢。有2种方法。1.使用标签w3c对pre元素是这样定义的:pre元素可定义预格式化的文本。被包围在pre元素中的文本通常会保留空格和换行符。而文本也会呈现为等宽字体。更详细的内容请参考http://www.w3school.com.cn/tags/tag_pre.a…

    2025年6月8日
    0
  • mysql怎么创建账户_mysql添加索引

    mysql怎么创建账户_mysql添加索引1.使用root账户登录到mysql:mysql -uroot -p2.创建用户名和密码:grant all on *.* to 用户名@’127.0.0.1′ identified by "密码";3.创建成功后,需设置访问权限。首先切换到mysql数据库:use mysql;4.修改新建用户的权限:update user set host = ‘%’ where user = ‘attckne…

    2022年8月18日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号