Current version

v1.10.4 (stable)

Navigation

Main page
Archived news
Downloads
Documentation
   Capture
   Compiling
   Processing
   Crashes
Features
Filters
Plugin SDK
Knowledge base
Donate
Contact info
Forum
 
Other projects
   Altirra

Search

Archives

01 Dec - 31 Dec 2013
01 Oct - 31 Oct 2013
01 Aug - 31 Aug 2013
01 May - 31 May 2013
01 Mar - 31 Mar 2013
01 Feb - 29 Feb 2013
01 Dec - 31 Dec 2012
01 Nov - 30 Nov 2012
01 Oct - 31 Oct 2012
01 Sep - 30 Sep 2012
01 Aug - 31 Aug 2012
01 June - 30 June 2012
01 May - 31 May 2012
01 Apr - 30 Apr 2012
01 Dec - 31 Dec 2011
01 Nov - 30 Nov 2011
01 Oct - 31 Oct 2011
01 Sep - 30 Sep 2011
01 Aug - 31 Aug 2011
01 Jul - 31 Jul 2011
01 June - 30 June 2011
01 May - 31 May 2011
01 Apr - 30 Apr 2011
01 Mar - 31 Mar 2011
01 Feb - 29 Feb 2011
01 Jan - 31 Jan 2011
01 Dec - 31 Dec 2010
01 Nov - 30 Nov 2010
01 Oct - 31 Oct 2010
01 Sep - 30 Sep 2010
01 Aug - 31 Aug 2010
01 Jul - 31 Jul 2010
01 June - 30 June 2010
01 May - 31 May 2010
01 Apr - 30 Apr 2010
01 Mar - 31 Mar 2010
01 Feb - 29 Feb 2010
01 Jan - 31 Jan 2010
01 Dec - 31 Dec 2009
01 Nov - 30 Nov 2009
01 Oct - 31 Oct 2009
01 Sep - 30 Sep 2009
01 Aug - 31 Aug 2009
01 Jul - 31 Jul 2009
01 June - 30 June 2009
01 May - 31 May 2009
01 Apr - 30 Apr 2009
01 Mar - 31 Mar 2009
01 Feb - 29 Feb 2009
01 Jan - 31 Jan 2009
01 Dec - 31 Dec 2008
01 Nov - 30 Nov 2008
01 Oct - 31 Oct 2008
01 Sep - 30 Sep 2008
01 Aug - 31 Aug 2008
01 Jul - 31 Jul 2008
01 June - 30 June 2008
01 May - 31 May 2008
01 Apr - 30 Apr 2008
01 Mar - 31 Mar 2008
01 Feb - 29 Feb 2008
01 Jan - 31 Jan 2008
01 Dec - 31 Dec 2007
01 Nov - 30 Nov 2007
01 Oct - 31 Oct 2007
01 Sep - 30 Sep 2007
01 Aug - 31 Aug 2007
01 Jul - 31 Jul 2007
01 June - 30 June 2007
01 May - 31 May 2007
01 Apr - 30 Apr 2007
01 Mar - 31 Mar 2007
01 Feb - 29 Feb 2007
01 Jan - 31 Jan 2007
01 Dec - 31 Dec 2006
01 Nov - 30 Nov 2006
01 Oct - 31 Oct 2006
01 Sep - 30 Sep 2006
01 Aug - 31 Aug 2006
01 Jul - 31 Jul 2006
01 June - 30 June 2006
01 May - 31 May 2006
01 Apr - 30 Apr 2006
01 Mar - 31 Mar 2006
01 Feb - 29 Feb 2006
01 Jan - 31 Jan 2006
01 Dec - 31 Dec 2005
01 Nov - 30 Nov 2005
01 Oct - 31 Oct 2005
01 Sep - 30 Sep 2005
01 Aug - 31 Aug 2005
01 Jul - 31 Jul 2005
01 June - 30 June 2005
01 May - 31 May 2005
01 Apr - 30 Apr 2005
01 Mar - 31 Mar 2005
01 Feb - 29 Feb 2005
01 Jan - 31 Jan 2005
01 Dec - 31 Dec 2004
01 Nov - 30 Nov 2004
01 Oct - 31 Oct 2004
01 Sep - 30 Sep 2004
01 Aug - 31 Aug 2004

Stuff

Powered by Pivot  
XML: RSS feed 
XML: Atom feed 

§ How to fight tearing

Tearing is a display artifact that occurs when images are presented to the screen without regard for the current status of the output circuitry. It occurs because pixels are sent to the screen gradually in book order rather than instantaneously. What happens is that the output DAC reads across the same area of the screen that is bring written to, so the monitor ends up showing a half-updated image. The result is a momentary frame that has half of an old frame and half of a new one, with a clean horizontal split across (the tear). Because the location of the tear varies according to timing, it usually jumps all over the place, which can be distracting.

I don't advertise VirtualDub as a player, but I've had several requests to add an option to fix this problem lately, so I decided to look into it. Tearing can be quite noticeable if you're previewing nearly full-screen 24 fps video. The most straightforward way to solve this problem is by synchronizing updates to the output scan, a technique known as vertical sync (vsync). This forces the tear to always occur off-screen so that it is not noticeable. Well, after doing some preliminary investigation, it's doable, but not as easy as I had hoped. As usual, DirectX is part of the reason why.

Background

Display output circuitry is generally geared toward CRTs, which use an electron scanning beam to draw an image. On a VGA display, this beam scans the screen in book order at 31.5KHz horizontally and 60 or 70Hz vertically. The period in which the beam returns from the right to the left is the horizontal retrace or horizontal blank, and the return from bottom to top is the vertical retrace or vertical blank. During both these periods, no video is displayed. The vertical blank period is much longer than the horizontal blank, and is definitely the region of choice for manipulating display memory unseen.

LCDs don't use an electron scanning beam, but still receive information in the same order at approximately the same rates, so they can show tearing effects as well.

The simplest method of avoiding tearing is to avoid updating the screen when any visible area of it is being displayed. One way to do this is to synchronize rendering with the output scan so that it always happens during the vertical blank. This is called vertical sync or vsync. During vertical blank, the screen is changed either by direct rendering, updating the entire screen from an off-screen buffer (blit/copy), or by swapping the active screen by switching display pointers (flip). None of this ever happens while the screen is being displayed, and thus the monitor always shows only full frames.

A downside to vsync is the dead time between when a frame is completed and when it can become the actively displayed frame. In a single-buffered or double-buffered system, this is dead time, and creates cliffs in performance if the system just barely misses 60 fps update speed with a 60Hz refresh, it has to drop to a consistent 30 fps. Switching to a three-buffer system, triple-buffering, resolves this problem by allowing the renderer to start drawing a third frame while a second one is pending. This produces an uneven frame rate and consumes more memory, but allows arbitrary frame rates regardless of refresh rate. It also requires a way to do an asynchronous flip, but most video hardware allows this, either through a vertical blank interrupt or a double-buffered display pointer register.

If the entire screen doesn't need to be updated, such as when displaying video in windowed mode, another solution is beam following. Many video display chips can report the current vertical location of the output scan, which then allows the screen to be updated during the visible portion as well. Assuming the blit is faster than the output scan, it is OK to blit whenever the beam is not scanning the portion being updated. If the beam is before the update region, the blit will outrace the beam, and if the beam is after it, the blit will stop before it hits the scanning point. This greatly increases the amount of "safe blit time" available, and reduces waiting time.

Avoiding tearing with GDI

Neither the vertical blank nor the beam position can be accessed with GDI. Therefore, unless you're running on Windows Vista with the desktop composition engine enabled, the answer is: you don't.

Avoiding tearing in DirectX

Flipping is the nicest and fastest way to avoid tearing because it requires very little effort; all that is required is to change one display pointer. Unfortunately, to do this the entire screen has to be flipped, and VirtualDub isn't a full-screen application. It's possible to flip an overlay surface, but no modern hardware supports those for RGB pixel formats. So blit scheduling is the way to go.

Calling WaitForVerticalBlank() is a solution, if you don't mind burning a ton of CPU time. It's also not really the most ideal since you're often starting the blit later than necessary.

DirectDraw 3 has a nice flag that can be set on a blit operation called DDBLTFX_NOTEARING (0x00000008). According to the documentation, it schedules blits to avoid tearing on-screen, which is exactly what I want. And when I tried it, it did squat. Checking around with Google, I found that others had hit this issue, and then found the following quote in the Windows Device Driver Kit (DDK) documentation:

Note that the DDBLTFX_NOTEARING, DDBLTFX_MIRRORLEFTRIGHT, and DDBLTFX_MIRRORUPDOWN flags are unsupported on Windows 2000 and later and are never passed to the driver.

Some testing revealed that MIRRORLEFTRIGHT and MIRRORUPDOWN still work, so it wasn't out of the question for NOTEARING to be handled in the runtime. To test that conclusion, I started stepping through the disassembly of IDirectDrawSurface2::Blt() with a data read breakpoint on the blit FX flag word. Testing flag 0x01, OK... 0x02, OK... 0x04, OK... 0x70, OK... do the blit... hey, wait a minute....

Seems that the Windows XP DirectDraw runtime simply drops DDBLTFX_NOTEARING on the floor, and the documentation hasn't been updated to note that fact. Grreaat.

DirectShow does it, though. Current versions normally use the Video Mixing Renderer (VMR) which is Direct3D based, but older versions relied on the DirectDraw-based Video Renderer, which also had no-tearing logic. After attaching the debugger to GraphEdit and forcing the old Video Renderer, I discovered that it doesn't use DDBLTFX_NOTEARING at all. It simply beam-avoids by polling the beam location in a tight loop and delays the blit until the danger zone has passed. Interestingly enough, this doesn't take as much CPU as might be expected.

Basically, avoiding tearing in DirectDraw on Windows 2000/XP requires some manual work.

Direct3D is a little more intelligent about how it does polling it uses Sleep(1) to release CPU time while avoiding the beam. Depending on whether D3DPRESENT_INTERVAL_IMMEDIATE to D3DPRESENT_INTERVAL_ONE is used, it can also optionally increase the scheduling timer rate to make this more accurate (10ms is a long time for a 16ms frame!). I was hoping that the hardware could queue up a vsynced or beam-avoided blit, but nope, the runtime polls. Unfortunately, there doesn't seem to be a way to tell the runtime to present as quickly as possible while avoiding the beam, so if I change the present mode and update two panes the best I can get is 30 fps with a wait in between, because the runtime does a Sleep() to ensure that the present rate doesn't exceed the refresh rate. Bleah. Part of the problem is that I'm only using one swap chain to service all panes, since I see no reason to allocate extra backbuffers, but I suspect that multiple swap chains might still stall unnecessarily.

In the end, I'll probably end up beam-avoiding in the D3D9 minidriver the same way as in the DirectDraw minidriver. Another way to do it would be to allocate a second swap chain which really only needs one buffer since we're running windowed and asynchronously schedule the blit using a 1ms multimedia timer callback that polls the beam. Problem is, there isn't a good way to schedule the blit from another thread, since Direct3D isn't a terribly multithreading-friendly API.

Comments

Comments posted:


Sadly, in D3D non-fullscreen mode (i.e. non-exclusive), Either the microsoft code or the display driver code pretty much ignores "D3DPRESENT_INTERVAL_ONE" when using VMR9 Renderless mode and you get tons of tearing. Using D3D Fullscreen mode does respect it, but there's no proper way to use standard windows, so your entire application's interface has to be converted to Direct3D (which is a major pain).

Blight (link) - 03 11 05 - 13:26


Strange. I know for a fact that the runtime supports D3DPRESENT_INTERVAL_ONE in windowed mode; it did eliminate tearing when I tried it, and most of the logic I described above isn't necessary in full-screen mode because hardware generally can queue flips. Check your device caps and make sure D3DCAPS_READ_SCANLINE is set; if not, IDirect3DDevice9::GetRasterStatus() can't be used, and the runtime probably can't beam-avoid.

Phaeron - 04 11 05 - 01:26


What "D3DPRESENT_PARAMETERS" parameters are you using for Windowed mode?

Blight - 04 11 05 - 21:15


Pretty basic. Everything zero except Windowed=TRUE, SwapEffect=D3DSWAPEFFECT_COPY, BackBufferFormat=D3DFMT_UNKNOWN, PresentationInterval=D3DPRESENT_INTERVAL_IMMEDIATE, BackBufferWidth=GetSystemMetrics(SM_CXMAXIMIZED), BackBufferHeight=GetSystemMetrics(SM_CYMAXIMIZED). I simply changed the presentation interval to D3DPRESENT_INTERVAL_ONE.

Phaeron - 05 11 05 - 16:53


Doesn't work for me in all cases, especially when dealing with higher resolution video (like 720p/1080p) in windowed mode... works ok in fullscreen mode with the same parameters.

I don't set the backbuffer width to the size you do, I set it to the video's size. But I don't think that should make a difference.

Blight - 06 11 05 - 03:31


Hi,

I did a bit of investigation on this. One thing I noticed that on my nVidea card, if I create an overlay surface and try to lock it for writing, the lock will wait until the beam is past the overlay surface. This lock appears to be tied to a win32 event object, so the system allows other threads to run while waiting. I havent used this in production code however. I guess beam avoidance is more portable.

Nevada - 11 12 06 - 16:37


So finally, VBL isn't managed by any API under windows?
But is there any interrupt occuring from the video card when the vbl area is reached by the electron beam? And for the HBL?

ep (link) - 24 12 06 - 23:52


Just to get a reproducible frame rate - pc power at work(NO!!). It all seemed so easy on the Amiga. When I think of the Atari display list processors. The accounting heritage of pc's haunts us still.

Dave Geden - 22 06 07 - 10:03


appears that with some hackery you may be able to get it in GDI. I can't believe it's not available that is nuts. http://www.compuphase.com/vretrace.htm#V..

roger (link) - 08 05 12 - 12:21

Comment form


Please keep comments on-topic for this entry. If you have unrelated comments about VirtualDub, the forum is a better place to post them.
Name:  
Remember personal info?

Email (Optional):
Your email address is only revealed to the blog owner and is not shown to the public.
URL (Optional):
Comment: /

An authentication dialog may appear when you click Post Comment. Simply type in "post" as the user and "now" as the password. I have had to do this to stop automated comment spam.



Small print: All html tags except <b> and <i> will be removed from your comment. You can make links by just typing the url or mail-address.