为什么游戏画面会产生撕裂?垂直同步与防撕裂技术分析
很多朋友在玩游戏的时候都可能遇到过下图这种画面撕裂的情况。尤其是对于FPS玩家,这种撕裂在迅速转动镜头时非常明显。
不过通常游戏设置里面都会有相应的选项来进行调整,比如是否开启垂直同步,是否启用三级缓冲等,打开之后画面撕裂的问题就会有明显的改善。
除了垂直同步以外,我们在购买的显示器和显卡的时候也可能听说过G-Sync、FreeSync等相关的技术,那么这些概念到底是什么意思?他们又是如何解决画面撕裂的问题呢?
撕裂原因
首先我们需要先从画面刷新的基本原理讲起。对于任何一个显示器,其屏幕画面的更新都不是瞬间完成的,而是要从上到下进行逐行扫描刷新,整个屏幕刷新一次是需要一定时间的。
华为手机支持960fps录制,本地显示器录了一段,可以明显看到逐行更新。
我们常说的60HZ显示器其实表示的是屏幕刷新率,也就是显示器每秒可以更新60次。但单次从第一行扫描到最后一行时间并不需要16.6ms(1s/60),也许5ms就可以搞定。
另一方面,屏幕要显示的画面数据其实来源于GPU的Buffer,而GPU往buffer里面写数据的速度与显示器是无关的。GPU产生帧率与显示器帧率往往不一致,如果不加以同步就必然产生撕裂,简单来说,GPU会在显示器读取buffer时把buffer改成下一帧内容,导致显示器上半屏是后一帧图像,下半屏是前一帧图像。
下面是在apex中关闭垂直同步录的,
可以看到其中有撕裂
双缓冲
双缓冲是显卡的渲染基本特性,GPU会使用两个buffer,每次渲染完后都把画面写入backbuffer。而显示器只从frontbuffer读取画面,通过交换指针来交换两个buffer,这个操作可认为是瞬间完成的。
不过swapbuffer时,显示器还没读取完frontbuffer,依然会产生撕裂。
垂直同步
双缓冲下,想要不撕裂,就需要保证显示器在显示完一个buffer后,再去swapbuffer,这就是垂直同步。简单讲,就是显示器在显示完后会向显卡发送一个v-sync信号。
不过垂直同步有两个明显的的缺点:
1.锁帧,这会把游戏帧率限制到显示器帧率。比如你使用的是60帧的显示器,那么无论你使用的是I9的CPU还是4090的显卡,在fps游戏中,玩家操作依然会有明显的延迟。比如下面情况(使用unrealInsight工具进行性能分析),开垂直同步锁了60帧之后,会发现CPU一直都在idle。
2.降帧。当GPU帧率低于60帧,或突然下降时,比如45帧,最终显示的帧率会变成30帧。
关于第2点,我们再进一步分析一下。
理想情况:GPU输出帧率高于60帧,输出一帧画面后等v-sync信号,最终显示器顺序输出每帧图像
帧率低的时候:当游戏卡了一下,GPU不能在16.6ms内完成一帧时,显示器产生v-sync信号后,发现没新数据,下一帧就继续使用老数据,导致两次显示了同一帧。再等到下一个v-sync信号,GPU输出了第三帧画面,正常显示,我们会发现中间的第二帧画面丢失了。
另外考虑如果GPU稳定以45帧输出画面,那么每两个v-sync信号只有一个会显示新画面,最终显示器输出的是30帧。
三缓冲
对于低帧率和帧率波动情况,可以用三缓冲来缓解,当帧率高于显示器帧率时,依然是垂直同步锁帧方案。
可以再加一个backbuffer,让GPU在完成一帧后不等垂直同步信号,接着运行,把数据写入另一个backbuffer。当swapbuffer时,顺序选取下一个buffer,相当于多了一个蓄水池。
比如上图,GPU错过了第二个v-sync信号,就把图像缓存起来,第三个v-sync信号时就能显示第二帧图像了。
这里注意,注意GPU每帧输出的图像都必须顺序显示,中间不能丢。而且第一次GPU遇到卡顿的时候是无法避免丢失的,因为那个时候另一个backbuffer里面还没有东西。
这样偶尔有一帧卡,因为多了个缓冲,依然能以60hz输出图像给显示器。当GPU稳定45帧运行时,显示器也能以45帧输出图像,而不是30帧。
但这样也存在问题,帧的逻辑时间可能长短不一,但最终都以16.6ms间隔显示在屏幕上,想象一个匀速直线运动物体,前一帧移动了16ms的距离,后一帧移动了26ms的距离,那么反映到显示器上物体移动就不匀速了。目前看,这种情况无法避免,因为GPU输出的画面是对游戏世界的离散采样,只有让帧率足够高,才能缓解这种问题。
Apex里的垂直同步选项里可选双缓冲和三缓冲,而且解释也比较清楚。
另外三缓冲不是游戏独有的技术,普通软件也能使用。安卓早期UI不流畅,因此在Project Butter中引入了vsync和三缓冲,app都可以使用该特性,也可以用开关mLayerTripleBufferingDisabled进行控制。
Android 之 Project Butter 详细介绍
https://www.jianshu.com/p/aea19baa3608
可变帧率显示器 G-sync Freesync
可变帧率显示器就是freesync和G-Sync技术,分别是AMD和英伟达的技术,要有对应显卡来让显示器帧率与GPU输出画面帧率同步。比如GPU在某段时刻的帧率分别是50,51,57,那么显示器也会立刻改变自己的显示帧率。这样可以做到延迟最低,也不会撕裂。那这个时候是不是还需要开启垂直同步呢?
开垂直同步
帧率依然会限制在屏幕最高帧率,只是GPU画面不需要等v-sync信号,显示延迟更低。
关垂直同步
不锁帧,GPU帧率低于显示器最高帧率时,也不会撕裂,GPU帧率高于显示器帧率,还会撕裂。
因此只要GPU帧率低于显示器帧率,垂直同步开关没影响,都是不撕裂+立即显示,就看游戏能不能跑到更高帧率了。
找了一台支持G-Sync的144hz显示器,关闭垂直同步,游戏稳定100hz左右,确实一直处于流畅状态,也没有撕裂。
FastSync/EnhancedSync
有没有办法在普通显示器上也实现既高帧率,又不撕裂?可以,回顾三缓冲方案,如果GPU帧率很高,可以在两次v-sync间输出两帧,那么就写入两个backbuffer,下一次v-sync来时,选取最新的backbuffer即可,另一个丢弃。这样就既开启了垂直同步,又不锁60帧,能尽快显示最新的画面。这种方式不妨称为FastSync。
这么做输入延迟高于关掉v-sync,但低于开v-sync,很适合fps游戏。
不过依然有最高帧率限制,在两次v-sync间如果把backbuffer都写满了,还是要等,最高帧率为(backbuffer数*显示器帧率)。假如一个游戏backbuffer开了3个,对于普通玩家的60帧显示器,最高帧率可以到达180,也基本够用了。
在虚幻引擎中,r.D3D12.UseAllowTearing和r.D3D12.SwapChainBufferCount分别控制了是否开启FastSync和backbuffer的数量。
映射到对DX接口的操作在这里
如果使用了这种方式,注意开启防撕裂时要关闭垂直同步。
建议
如果只是普通显示器,而且GPU帧率是显示器帧率的2倍或3倍,就使用FastSync/EnhancedSync。
如果显示器支持freesync或G-Sync,fps游戏就不开垂直同步了。
尽量不要使用双缓冲的v-sync,用三缓冲的。
参考:
Android 之 Project Butter 详细介绍:https://www.jianshu.com/p/aea19baa3608
什么是画面撕裂?垂直同步,G-sync,Freesync到底有啥用?:https://zhuanlan.zhihu.com/p/41848908
AMD:https://www.hardwaretimes.com/what-is-v-sync-should-you-turn-it-on-or-off/
垂直同步到底要不要开?:https://www.bilibili.com/video/BV16x411e7bp/
往期文章推荐
游戏开发技术系列【想做游戏开发,我应该会点啥?】
虚幻引擎技术系列【使用虚幻引擎4年,我想再谈谈他的网络架构】
游戏科普系列【盘点游戏中那些“欺骗玩家眼睛的开发技巧”】
C++面试系列【史上最全的C++/游戏开发面试经验总结】
我是Jerish,网易游戏工程师,6年从业经验。该公众号会定期输出技术干货和游戏科普的文章,关注我回复关键字可以获取游戏开发、操作系统、面试、C++、游戏设计等相关书籍和参考资料。
更多推荐
所有评论(0)