views:

295

answers:

5

I'm running an animation in a WinForms app at 18.66666... frames per second (it's synced with music at 140 BPM, which is why the frame rate is weird). Each cel of the animation is pre-calculated, and the animation is driven by a high-resolution multimedia timer. The animation itself is smooth, but I am seeing a significant amount of "tearing", or artifacts that result from cels being caught partway through a screen refresh.

When I take the set of cels rendered by my program and write them out to an AVI file, and then play the AVI file in Windows Media Player, I do not see any tearing at all. I assume that WMP plays the file smoothly because it uses DirectX (or something else) and is able to synchronize the rendering with the screen's refresh activity. It's not changing the frame rate, as the animation stays in sync with the audio.

Is this why WMP is able to render the animation without tearing, or am I missing something? Is there any way I can use DirectX (or something else) in order to enable my program to be aware of where the current scan line is, and if so, is there any way I can use that information to eliminate tearing without actually using DirectX for displaying the cels? Or do I have to fully use DirectX for rendering in order to deal with this problem?

Update: forgot a detail. My app renders each cell onto a PictureBox using Graphics.DrawImage. Is this significantly slower than using BitBlt, such that I might eliminate at least some of the tearing by using BitBlt?

Update 2: the effect I'm seeing is definitely not flicker (which is different from tearing). My panel is double-buffered, sets the control styles for AllPaintingInWmPaint, UserPaint, OptimizedDoubleBuffer etc., overrides onPaintBackGround and so on. All these are necessary to eliminate flicker, but the tearing problem remains. It is especially pronounced when the animation has very fast-moving objects or objects that change from light to dark very quickly. When objects are slow-moving and don't change color rapidly, the tearing effect is much less noticeable (because consecutive cels are always very similar to each other).

A: 

You would better to use directx (or opengl) for such tasks. But if you want to use only winforms use DoubleBuffered property.

Andrey
I use DoubleBuffered to eliminate flicker, but it doesn't help with tearing.
MusiGenesis
Also, I'm trying to make my app cross-platform with Mono, so I want to avoid platform-specific things like DirectX, if possible.
MusiGenesis
so use opengl! it is cross-platform analog of directx. well, i thought winforms are not cross-pl.
Andrey
BTW, Mono support for Windows.Forms is quite acceptable these days.
reinierpost
good news! still for multimedia tasks you should use opengl/directx.
Andrey
@Andrey: I'll take a look at opengl.
MusiGenesis
A: 

Double buffer it.

You can enable double buffering using windows styles or, whats probably easier, is to draw to a picture from offscreen and then swap them.

If this doesnt work then the best thing to do is bitblit and double buffer.

Essentially its the same.

Have a reference to two bitmaps, one is the screen, the other is the buffer. You draw to the buffer first, then blit that entire thing to the screen. This way you only ever write live data to the buffer. The sceen simply shows something you made earlier (blue peter style)

Chris
Sorry, I should have been more clear in my question. Double buffering (which I'm already using) does not help with tearing effects.
MusiGenesis
Ah ok no worries. Dont use the picture box then and write directly to the screen (with double buffering)
Chris
+1  A: 

I tried the double buffering idea on the project I'm working on at the moment, but I didn't get very good results with it. In the end, I created the following:

  1. A System.Drawing.Bitmap for my offscreen buffer. Decode the animation into this bitmap.
  2. A UserControl the same size as the image in (1) and where the OnPaintBackground method was empty (no drawing, no call to base class) and the OnPaint did a Graphics.DrawImage to copy the offscreen image to the screen.

Now, you've got a weird animation rate so the tearing is almost certainly to do with a mismatch between screen update rate and screen refresh rate. You are updating the screen midway through the screen's refresh so the screen is drawing the old frame at the top of the screen and the new frame at the bottom of the screen. If you can synchronise the frame rate with the display refresh rate, the tearing should disappear.

Skizz
What you describe is essentially what I'm doing, although I use two Bitmaps as offscreen buffers, so that I can update one while the other is being drawn to the screen. I'm going to try syncing my animation with my refresh rate (60Hz) and see what that does for me.
MusiGenesis
+2  A: 

Tearing occurs when your image update is not in sync with the refresh rate of the monitor. The monitor shows part of the previous image, part of the new image. When the objects in the image move fast, the effect is quite noticeable.

It isn't fixable in Windows Forms, you can't get to the video adapter's v-sync signal. You can in a DirectX app.

Hans Passant
A: 

Tearing is an artifact from a frame being drawn on top of another. The only safe ways of avoiding it is to a) wait from vsync or b) draw behind the beam (this is rather tricky). Double buffer alone doesn't guarantee against tearing since you can have double buffer but still draw having the vsync off. Some cards might also have vsync wait option "forced off". You need to check the documentation regarding vsync and how to check where it is. This is the only safe way to do it. Also, keep in mind that this will lock your framerate.

MikeT
There must be some way of animating this without tearing using my chosen framerate (the framerate is constrained because the animation has to be in sync with music), because WMP is able to render it fine without tearing. Maybe WMP does some kind of between-frame interpolation?
MusiGenesis