(转)(笔记)screen tearing

来源:互联网 发布:15寸mac魔兽世界 编辑:程序博客网 时间:2024/05/22 05:19

喜欢玩游戏或者看电影的读者可能遇到过这样的情形:

某些游戏场面好像是几个场景“拼凑”而成的

电影画面不连贯,好像被“割裂”了

如下图:



什么是 screen tearing?

答:Screen tearing is a visual artifact (视觉假象) in video display where a display device shows information from multiple frames in a single screen draw

屏幕上显示的画面实际上来源于多个“帧”。

双缓冲技术,基本原理就是采用两块buffer。一块back buffer用于CPU/GPU后台绘制,另一块framebuffer则用于显示,当back buffer准备就绪后,它们才进行交换。不可否认,doublebuffering可以在很大程度上降低screen tearing错误,但是它是万能的吗?

一个需要考虑的问题是我们什么时候进行两个缓冲区的交换呢?假如是back buffer准备完成一帧数据以后就进行,那么如果此时屏幕还没有完整显示上一帧内容的话,肯定是会出问题的。看来只能是等到屏幕处理完一帧数据后,才可以执行这一操作了。

我们知道,一个典型的显示器有两个重要特性,行频和场频。行频(Horizontal ScanningFrequency)又称为“水平扫描频率”,是屏幕每秒钟从左至右扫描的次数; 场频(Vertical Scanning Frequency)也称为“垂直扫描频率”,是每秒钟整个屏幕刷新的次数。由此也可以得出它们的关系:行频=场频*纵坐标分辨率。

当扫描完一个屏幕后,设备需要重新回到第一行以进入下一次的循环,此时有一段时间空隙,称为VerticalBlanking Interval(VBI)。大家应该能想到了,这个时间点就是我们进行缓冲区交换的最佳时间。因为此时屏幕没有在刷新,也就避免了交换过程中出现screentearing的状况。VSync(垂直同步)是VerticalSynchronization的简写,它利用VBI时期出现的vertical sync pulse来保证双缓冲在最佳时间点才进行交换。

所以说V-sync这个概念并不是Google首创的,它在早些年前的PC机领域就已经出现了。不过Android 4.1给它赋予了新的功用,稍后就可以看到。


综上,如果没有VSync同步, 绘图速度大于显示速度那么会有问题(screen tearing).

如果没有VSync同步,还有另一个问题,就是反过来绘图速度过慢的时候.

如图:


绘图过程没有采用VSync同步的情况

引用自Google2012 I/O,,作者Chet Haase和Romain Guy,AndroidUI Toolkit Engineers

这个图中有三个元素,Display是显示屏幕,GPU和CPU负责渲染帧数据,每个帧以方框表示,并以数字进行编号,如0、1、2等等。VSync用于指导双缓冲区的交换。

以时间的顺序来看下将会发生的异常:

Step1. Display显示第0帧数据,此时CPU和GPU渲染第1帧画面,而且赶在Display显示下一帧前完成

Step2. 因为渲染及时,Display在第0帧显示完成后,也就是第1个VSync后,正常显示第1帧

Step3. 由于某些原因,比如CPU资源被占用,系统没有及时地开始处理第2帧,直到第2个VSync快来前才开始处理

Step4. 第2个VSync来时,由于第2帧数据还没有准备就绪,显示的还是第1帧。这种情况被Android开发组命名为“Jank”。

Step5. 当第2帧数据准备完成后,它并不会马上被显示,而是要等待下一个VSync。

所以总的来说,就是屏幕平白无故地多显示了一次第1帧。原因大家应该都看到了,就是CPU没有及时地开始着手处理第2帧的渲染工作,以致“延误军机”。 Android系统中一直存在着这个问题,即便是上一版本的Ice Cream Sandwich。

从Android 4.1Jelly Bean开始,VSync得到了进一步的应用。系统在收到VSync pulse后,将马上开始下一帧的渲染。

如图:



如上图所示,一旦VSync出现后,CPU不再犹豫,紧接着就开始执行buffer的准备工作。大部分的Android显示设备刷新率是60Hz,这也就意味着每一帧

最多只能有1/60=16ms左右的准备时间。

假如CPU/GPU的FPS(FramesPer Second)高于这个值,那么这个方案是完美的,显示效果将很好。可是我们没有办法保证所有设备的硬件配置都能达到要求。假如CPU/GPU的性能无法满足上图的条件,又是什么情况呢?

在分析这一问题之前,我们先来看下正常情况下,采用双缓冲区的系统的运行情况。

如图:


这个图采用了双缓冲,以及前面介绍的VSync,可以看到整个过程还是相当不错的,虽然CPU/GPU处理所用的时间时短时长,但总的来说都在16ms以

内,因而不影响显示效果。A和B分别代表两个缓冲区,它们不断地交换来正确显示画面。

现在我们可以继续分析FPS低于屏幕刷新率的情况。

如图所示:


当CPU/GPU的处理时间超过16ms时,第一个VSync到来时,缓冲区B中的数据还没有准备好,于是只能继续显示之前A缓冲区中的内容。而B完成后,又因为缺乏VSync pulse信号,它只能等待下一个signal的来临。于是在这一过程中,有一大段时间是被浪费的。当下一个VSync出现时,CPU/GPU马上执行操作,此时它可操作的buffer是A,相应的显示屏对应的就是B。这时看起来就是正常的。只不过由于执行时间仍然超过16ms,导致下一次应该执行的缓冲区交换又被推迟了——如此循环反复,便出现了越来越多的“Jank”。

那么有没有规避的办法呢?

很显然,第一次的Jank看起来是没有办法的,除非升级硬件配置来加快FPS。我们关注的重点是被CPU/GPU浪费的时间段,怎么才能充分利用起来呢?分析上述的过程,造成CPU/GPU无事可做的假象是因为当前已经没有可用的buffer了。换句话说,如果增加一个buffer,情况会不会好转呢?

如图:



Triple Buffering是MultipleBuffering的一种,指的是系统使用3个缓冲区用于显示工作。我们来逐步分析下这个新机制是否有效。首先和预料中的一致,第一次“Jank”无可厚非。不过让人欣慰的是,当第一次VSync发生后,CPU不用再等待了,它会使用第三个buffer C来进行下一帧数据的准备工作。虽然对缓冲区C的处理所需时间同样超过了16ms,但这并不影响显示屏——第2次VSync到来后,它选择buffer B进行显示;而第3次VSync时,它会接着采用C,而不是像double buffering中所看到的情况一样只能再显示一遍了。这样子就有效地降低了系统显示错误的机率。



个人总结:

Screen Tearing出现的原因无非两个: 一是在display的时候draw(也就是所谓的On Display Draw), 这种情况有可能会出现tearing(也不是必然). 二是在display的时候swap buffer(Flip).

分两种情况:

1: 如果我们使用的是单缓冲,那么当Draw的速度大于或小于刷新速度的时候,都有可能出现tearing.

2: 如果使用双缓冲做Flip,那么每次用于display的buffer一定不会同时有两帧内容(tearing),因为肯定是画好了完整的一帧才会做swap操作.

    但是如果不做Vsync同步.每次画好就做swap,也可能会出现tearing.

那么要避免出现Screen Tearing, 我们只要使用双缓冲做Flip(避免了On display Draw),并且做VSync同步,即每次等到Vsync阶段再做swap. 就可以保证不出现Screen Tearing. 并且这能控制我们画的速度不大于monitor的刷新速度,但是仍然可能会有Draw的速度比monitor的刷新速度慢这种情况,但由于我们使用的是双缓冲Vsync同步,所以就算画的比monitor的刷新速度慢,依然不会出现tearing,原因是每次用于display的buffer一定不会同时有两帧内容, 且不会在display的时候做swap. 但如果画的比monitor的刷新速度慢会出现jank, 接下来看Jank,要避免出现Jank,其实双缓冲(double buffer)理论上已经"够用了", 只要CPU/GPU画的及时(能赶在Vsync到来之前画完)那么Jank就不会有.三缓冲(triple buffer)只是在CPU/GPU速度不够的时候能减少Jank的次数而已.(双缓冲Jank发生时没有空闲buffer可用, 这时候CPU/GPU可能正好有空但画不了,于是时间被浪费了, 后面有空闲buffer可用的时候可能CPU/GPU又忙起来了,导致没赶在VSync之前画完,于是导致连续的Jank, 而三缓冲能在Jank发生时分出buffer来, CPU/GPU如果此时空闲就可以利用这段时间继续画下一个buffer, 那么接下来CPU/GPU忙的时候就可以避免连续的Jank,也就是说把CPU/GPU忙的时间, 和CPU/GPU空闲的时间均匀了一下,把CPU/GPU本来忙的时候要做的事情分给CPU/GPU空闲的时候去做, CPU/GPU只要空闲就能画buffer, 不会因为没有空闲buffer可用而被堵住. 把CPU/GPU空闲的时间资源不要浪费掉,用来画buffer,那么在接下来CPU/GPU忙的时候, 也能有buffer可以Flip,就可以避免更多的Jank).


From  https://hardforum.com/threads/how-vsync-works-and-why-people-loathe-it.928593/

I recently learned that how I thought vsync worked was wrong, and now knowing the way it really does work, I think it would be worthwhile to make sure everyone here understands it.

What is VSync?


VSync stands for Vertical Synchronization. The basic idea is that synchronizes your FPS with your monitor's refresh rate. The purpose is to eliminate something called "tearing". I will describe all these things here.

Every CRT monitor has a refresh rate. It's specified in Hz (Hertz, cycles per second). It is the number of times the monitor updates the display per second. Different monitors support different refresh rates at different resolutions. They range from 60Hz at the low end up to 100Hz and higher. Note that this isn't your FPS as your games report it. If your monitor is set at a specific refresh rate, it always updates the screen at that rate, even if nothing on it is changing. On an LCD, things work differently. Pixels on an LCD stay lit until they are told to change; they don't have to be refreshed. However, because of how VGA (and DVI) works, the LCD must still poll the video card at a certain rate for new frames. This is why LCD's still have a "refresh rate" even though they don't actually have to refresh.

I think everyone here understands FPS. It's how many frames the video card can draw per second. Higher is obviously better. However, during a fast paced game, your FPS rarely stays the same all the time. It moves around as the complexity of the image the video card has to draw changes based on what you are seeing. This is where tearing comes in.

Tearing is a phenomenon that gives a disjointed image. The idea is as if you took a photograph of something, then rotated your vew maybe just 1 degree to the left and took a photograph of that, then cut the two pictures in half and taped the top half of one to the bottom half of the other. The images would be similar but there would be a notable difference in the top half from the bottom half. This is what is called tearing on a visual display. It doesn't always have to be cut right in the middle. It can be near the top or the bottom and the separation point can actually move up or down the screen, or seem to jump back and forth between two points.

Why does this happen? Lets take a specific example. Let's say your monitor is set to a refresh rate of 75Hz. You're playing your favorite game and you're getting 100FPS right now. That means that the monitor is updating itself 75 times per second, but the video card is updating the display 100 times per second, that's 33% faster than the monitor. So that means in the time between screen updates, the video card has drawn one frame and a third of another one. That third of the next frame will overwrite the top third of the previous frame and then get drawn on the screen. The video card then finishes the last 2 thirds of that frame, and renders the next 2 thirds of the next frame and then the screen updates again. As you can see this would cause this tearing effect as 2 out of every 3 times the screen updates, either the top third or bottom third is disjointed from the rest of the display. This won't really be noticeable if what is on the screen isn't changing much, but if you're looking around quickly or what not this effect will be very apparant.

Now this is where the common misconception comes in. Some people think that the solution to this problem is to simply create an FPS cap equal to the refresh rate. So long as the video card doesn't go faster than 75 FPS, everything is fine, right? Wrong.

Before I explain why, let me talk about double-buffering. Double-buffering is a technique that mitigates the tearing problem somewhat, but not entirely. Basically you have a frame buffer and a back buffer. Whenever the monitor grabs a frame to refresh with, it pulls it from the frame buffer. The video card draws new frames in the back buffer, then copies it to the frame buffer when it's done. However the copy operation still takes time, so if the monitor refreshes in the middle of the copy operation, it will still have a torn image.

VSync solves this problem by creating a rule that says the back buffer can't copy to the frame buffer until right after the monitor refreshes. With a framerate higher than the refresh rate, this is fine. The back buffer is filled with a frame, the system waits, and after the refresh, the back buffer is copied to the frame buffer and a new frame is drawn in the back buffer, effectively capping your framerate at the refresh rate.

That's all well and good, but now let's look at a different example. Let's say you're playing the sequel to your favorite game, which has better graphics. You're at 75Hz refresh rate still, but now you're only getting 50FPS, 33% slower than the refresh rate. That means every time the monitor updates the screen, the video card draws 2/3 of the next frame. So lets track how this works. The monitor just refreshed, and frame 1 is copied into the frame buffer. 2/3 of frame 2 gets drawn in the back buffer, and the monitor refreshes again. It grabs frame 1 from the frame buffer for the first time. Now the video card finishes the last third of frame 2, but it has to wait, because it can't update until right after a refresh. The monitor refreshes, grabbing frame 1 the second time, and frame 2 is put in the frame buffer. The video card draws 2/3 of frame 3 in the back buffer, and a refresh happens, grabbing frame 2 for the first time. The last third of frame 3 is draw, and again we must wait for the refresh, and when it happens, frame 2 is grabbed for the second time, and frame 3 is copied in. We went through 4 refresh cycles but only 2 frames were drawn. At a refresh rate of 75Hz, that means we'll see 37.5FPS. That's noticeably less than 50FPS which the video card is capable of. This happens because the video card is forced to waste time after finishing a frame in the back buffer as it can't copy it out and it has nowhere else to draw frames.

Essentially this means that with double-buffered VSync, the framerate can only be equal to a discrete set of values equal to Refresh / N where N is some positive integer. That means if you're talking about 60Hz refresh rate, the only framerates you can get are 60, 30, 20, 15, 12, 10, etc etc. You can see the big gap between 60 and 30 there. Any framerate between 60 and 30 your video card would normally put out would get dropped to 30.

Now maybe you can see why people loathe it. Let's go back to the original example. You're playing your favorite game at 75Hz refresh and 100FPS. You turn VSync on, and the game limits you to 75FPS. No problem, right? Fixed the tearing issue, it looks better. You get to an area that's particularly graphically intensive, an area that would drop your FPS down to about 60 without VSync. Now your card cannot do the 75FPS it was doing before, and since VSync is on, it has to do the next highest one on the list, which is 37.5FPS. So now your game which was running at 75FPS just halved it's framerate to 37.5 instantly. Whether or not you find 37.5FPS smooth doesn't change the fact that the framerate just cut in half suddenly, which you would notice. This is what people hate about it.

If you're playing a game that has a framerate that routinely stays above your refresh rate, then VSync will generally be a good thing. However if it's a game that moves above and below it, then VSync can become annoying. Even worse, if the game plays at an FPS that is just below the refresh rate (say you get 65FPS most of the time on a refresh rate of 75Hz), the video card will have to settle for putting out much less FPS than it could (37.5FPS in that instance). This second example is where the perceived drop in performance comes in. It looks like VSync just killed your framerate. It did, technically, but it isn't because it's a graphically intensive operation. It's simply the way it works.

All hope is not lost however. There is a technique called triple-buffering that solves this VSync problem. Lets go back to our 50FPS, 75Hz example. Frame 1 is in the frame buffer, and 2/3 of frame 2 are drawn in the back buffer. The refresh happens and frame 1 is grabbed for the first time. The last third of frame 2 are drawn in the back buffer, and the first third of frame 3 is drawn in the second back buffer (hence the term triple-buffering). The refresh happens, frame 1 is grabbed for the second time, and frame 2 is copied into the frame buffer and the first part of frame 3 into the back buffer. The last 2/3 of frame 3 are drawn in the back buffer, the refresh happens, frame 2 is grabbed for the first time, and frame 3 is copied to the frame buffer. The process starts over. This time we still got 2 frames, but in only 3 refresh cycles. That's 2/3 of the refresh rate, which is 50FPS, exactly what we would have gotten without it. Triple-buffering essentially gives the video card someplace to keep doing work while it waits to transfer the back buffer to the frame buffer, so it doesn't have to waste time. Unfortunately, triple-buffering isn't available in every game, and in fact it isn't too common. It also can cost a little performance to utilize, as it requires extra VRAM for the buffers, and time spent copying all of them around. However, triple-buffered VSync really is the key to the best experience as you eliminate tearing without the downsides of normal VSync (unless you consider the fact that your FPS is capped a downside... which is silly because you can't see an FPS higher than your refresh anyway).

I hope this was informative, and will help people understand the intricacies of VSync (and hopefully curb the "VSync, yes or no?" debates!). Generally, if triple buffering isn't available, you have to decide whether the discrete framerate limitations of VSync and the issues that can cause are worth the visual improvement of the elimination of tearing. It's a personal preference, and it's entirely up to you.


原创粉丝点击