Sign in to follow this  

Vsync, double buffering, and smooth animation

This topic is 3462 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey guys, I was just wondering if anyone can explain to me exactly how these things all work. I understand how vsync works, and it's disadvantages. But I hear of people who disable it, but I don't see how that is possible. In my application I have a white square moving around on a black background, and it looks horrible. Is that just because of the simplicity of the scene that the tearing is so apparent? Or is there a way to pull off less tearing without vsync? I've also noticed that no other programs really have these problems. With vsync on or off. I'd just like to hear some of your strategies to reduce tearing. Thanks.

Share this post


Link to post
Share on other sites
I always use Double Buffering!
In my game it makes just a small difference when I disable VSync.
Quote:
I understand how vsync works, and it's disadvantages. But I hear of people who disable it, but I don't see how that is possible.
I don't know what you are using to draw to the screen, but I use SDL and OpenGL and can enable/disable VSync like that:
SDL_GL_SetAttribute ( SDL_GL_SWAP_CONTROL, 0); /* Disable VSync */
SDL_GL_SetAttribute ( SDL_GL_SWAP_CONTROL, 1); /* Enable VSync */
SDL_GL_SetAttribute ( SDL_GL_DOUBLEBUFFER, 0 ); /* Disable Double Buffering */
SDL_GL_SetAttribute ( SDL_GL_DOUBLEBUFFER, 1 ); /* EnableDouble Buffering */

Share this post


Link to post
Share on other sites
Quote:
Original post by darknebula42
In my application I have a white square moving around on a black background, and it looks horrible.

Can we see the animation code?

Share this post


Link to post
Share on other sites
With double-buffering enabled, tearing is almost non-existant on non-Vsync'ed applications.
Tearing is very common when we're not Vsync'ed (we're either too slow or too fast) and we're writting directly to the front buffer (that is, double-buffering disabled). Therefore any update is instantly showed to the monitor even if the whole scene rendering isn't complete

Terminology disgregation:
Some (i.e. DirectX) refer to Triple buffering to what other say it's Double Buffering.
Following DX concepts (I don't know about OGL) Single-Buffering is the usage of just one front buffer.
Double-Buffering is the usage of two buffers: The back buffer, where the scene is rendered to, and the front buffer, which is updated by copying the contents of the back-buffer. Even if Vsync is disabled, the front buffer is not shown until the whole copy isn't finished. Even if it is shown, just copying a buffer from video memory to video memory is very fast, thus tearing only happens on very rare situations with insanely low framerates.
Triple-Buffering uses three buffers: Front buffer, and two (hence this is why some call it double-buffering, assuming the front buffer will be always present) back buffers. When the first back buffer is used, and we're ready to render a new frame (the CPU is in this case much faster than the GPU) it uses the second one.
Disadvantage: This increases the delay between user input (i.e. pressing a key) and showing the results in screen by 2 frames. But rarely it is noted. Consumes more VRAM.
Advantage: If the third or fourth frame will need much more processing power (in which a double-buffered would suffer FPS drops) the CPU will have more time available to complete the task and avoid the performance decrease. In other words, light frames compensate the heavy ones. (Saying it easy: performance boost)

Advantages of Vsync:
*Removes completely tearing
*On fast machines, ensures that the game is running at the desired speed in all of them. (We can though, develop our own fps limiter, but it won't prevent tearing)
*Saves energy on laptops and heat (including desktop PCs) unless we're at low framerates. (Because otherwise the GPU is idle when waiting)
*It is better to leave it on in high-end GPUs

Disadvantages of Vsync:
*Since the system waits to render the screen, (when we're at low fps) we may decrease performance even more. This is why many games disables them, or include the option at least.
*Leave VSync off in low-end GPUs. (argueable)

Phew.... It's long. I hope you find this usefull. I'm not done though. But I'm afraid I'm out of time. Good luck!

Dark Sylinc

Edit: Fixed some typo errors

Share this post


Link to post
Share on other sites
Quote:
Original post by darknebula42
With vsync on or off. I'd just like to hear some of your strategies to reduce tearing. Thanks.

You kept me thinking about it. I'm confused. Are you sure you understand what tearing is? Because I don't see a game or hardware tearing since the 16-bit console era.
If you do, are you developing for PDAs, cellphones, GameBoy?

Strategies:
*Use double-buffering
*Use VSync
*Don't make a game that needs more processing power than the targeted device is capable of.
*Don't allow high monitors refresh-rate (some users may hate you)
*Use display lists on a device capable of doing vector graphics. I don't know if such gpu hardware exists nowadays, and better yet, is in the mass market.
*Decrease gfx detail when running at low fps.

Share this post


Link to post
Share on other sites
Sorry. I'm using SDL, and OpenGL.

Here is my drawing:

c is a clock which measures the time it takes for the loop to finish

void MainMenu::logic()
{
if(game->win.keys.isDown(SDLK_ESCAPE))
game->stateStack.pop();

// find the time passed to interpolate position
if(game->win.keys.isDown(SDLK_RIGHT))
x += c.stop() * 0.2;
if(game->win.keys.isDown(SDLK_LEFT))
x -= c.stop() * 0.2;
c.reset(); // reset the clock to zero
}

void MainMenu::draw()
{
glLoadIdentity();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glTranslatef(x, y, 0);
gl::Rectangle(100, 100).draw();

// Is this good practice here? :
glFlush();
SDL_GL_SwapBuffers();
glFinish();
}





Here is my main loop:


time::Clock loopClock;
const unsigned int defaultWaitTime = 14;
unsigned int waitTime = defaultWaitTime;

while(win.isOpen())
{
time::sleep(waitTime);
loopClock.reset();
// ...
game.stateStack.logic();
// ...
game.stateStack.draw();

if(loopClock.stop() < defaultWaitTime)
waitTime = defaultWaitTime - loopClock.check();
}





These are in use:
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);

Which enables Vsync and DoubleBuffering for OpenGL.
Thanks for all your help so far everyone!

With the code above, anyone who can't use VSync, it looks horrible and there is tearing. It looks like the sides of the rectangle are alive even. The sides of the rectangle are jerking? And the tops aren't that bad. Even on a Vsync enabled video card the movement isn't too amazing either. I could give out an exe, but I only have a linux binary available, no Windows exe, but if anyone really wants me to I could make one.

Share this post


Link to post
Share on other sites
Your time calculations in main loop are incorrect - if that is all of the code.
Variable waitTime gets wrong value when that "if" is not executed. There should be "waitTime = defaultWaitTime" in else branch.

That may affect some timing things - but shouldn't do the behavior you describe...

Also that drawing might be a bit illogical, this order might be a slightly better:
glFlush();
glFinish();
SDL_GL_SwapBuffers();
(although, when I checked my own code, I didn't even have that glFinish)

Also, just to make sure, you are calling those functions only once per frame?
Because in code you presented there is a possibility to have two MainMenu objects in game.stateStack, which would draw the screen twice per "frame" which would create very weird visual effects.


Edit:
And now I think I am wrong with that timing issue, actually you shouldn't wait at all if rendering time was longer than defaultWaitTime.
Also please note that when you call that time::sleep (which probably calls SDL_Delay) your app might sleep much longer than the specified time.

Share this post


Link to post
Share on other sites
If you are calling SDL_GL_SwapBuffers(), you shouldn't call glFlush() or glFinish(). Also, even if you weren't calling SDL_GL_SwapBuffers(), there's never a need to call both. See here for more info.

Share this post


Link to post
Share on other sites
I forgot to mention that if I disable a constant FPS, it still looks like it's tearing. I'm certain it's not drawing one than more main menu at a time. And when I add the else to make sure the wait time is zero it looks smooth but at a certain time interval it shakes. It's smooth for about a second, then skips, smooth for a second, then skips, etc. Thanks again!

edit:
It's strange I can move the square for a certain period of time, stop, and start moving again and get into a rhythm where the square movements looks fine! That should give some clues. (this is when I still have the constant frame rate at about 59 fps), when the sleep is turned off though, the tearing increases but I don't get those weird "jumps".

edit2: to clarify, it looks as if the square as it's moving forward about every second is jumping a small distance backwards and being forward at the same time.

[Edited by - darknebula42 on June 22, 2008 3:09:28 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Matias GoldbergEntire post

With all respect, your post is full of wrong statements. You can't write directly to the front buffer, so you can't disable double-buffering at all (unless, like you pointed out, by double buffering you mean triple buffering). So saying "any update is instantly showed to the monitor" is rediculous. If that was possible, you would be able to see the scene being rendered piece by piece when playing a game on a slow machine, which you don't, courtesy of double buffering.

Quote:
With double-buffering enabled, tearing is almost non-existant on non-Vsync'ed applications.

Since you can't disable double buffering, I will assume you mean triple buffering, in which case this statement is also false, since the only advantage of triple buffering is the one you described in your post, and not resolving the vsync issue.

Quote:
Even if Vsync is disabled, the front buffer is not shown until the whole copy isn't finished. Even if it is shown, just copying a buffer from video memory to video memory is very fast, thus tearing only happens on very rare situations with insanely low framerates.

I don't think you really understand why tearing happens. Let me briefly explain. The graphics card keeps feeding the contents of the front buffer to the monitor from top left to bottom right. Now, you can't write directly to the front buffer, so all your rendering happens on the back buffer. When you finish rendering your scene to the back buffer, you call Present(), telling the graphics card that you want it to send the contents of this back buffer of yours to the screen. But the card can only send the contents of the FRONT buffer to the screen. Both buffers are in video memory, so the card has to do one of two things. It has to either copy the contents of your back buffer to the front buffer (D3DSWAPEFFECT_COPY), or flip (swap) the buffers (D3DSWAPEFFECT_FLIP).

The first (copying) needs no explanation, but you should think about it as an atomic operation. Flipping is much like a pointer swap - the card will stop sending data to the monitor from the front buffer and start sending it from your back buffer, thus making it the new front buffer. It will modify your back buffer pointer to point to the old front buffer, so obviously the back buffer will contain the contents of the old front buffer (this is sometimes exploited when implementing fake motion blur effects and any other technique which requires access to the previous frame).

"Ok, but where does tearing come from?" I hear you say. Well, recall that the card constantly sends the contents of the front buffer to the screen from top left to bottom right. Now when you call Present() with vsync disabled, the contents of the front buffer will change (either due to the contents of the back buffer being copied over it or the card switching buffers). This change happens regardless of how much of the old front buffer has been sent to the card, and for the rest of the screen update, the pixels that the card will send will be from the new front buffer contents, causing the upper half of the screen to display pixels from the old frame and the lower half to display pixels from the new frame. In fact, if the card is fast enough of the scene is simple enough, this change in the contents of the front buffer can happen more than once during a screen update, causing the screen to display parts of more than just two images. Try writing an application that simply clears the back buffer to a different color each frame and presents it with vsync disabled and you will notice at least 4 or 5 tears!

If vsync is enabled, the card will not copy or flip buffers until the screen finishes updating and the entire contents of the front buffer have been sent to the monitor. Then the copy or swap happens before the next screen update happens, so the screen never shows more than one image at a time.

Oh crap, I got carried away again. Anyway, hope this helps.

Share this post


Link to post
Share on other sites
Okay. I packaged it all up if anyone wants to actually see what's going on: test.zip. Before the huge post above I explain a little bit more about what is going on. Anyone with experience might be able to pick out what's up from my descriptions, thanks.

Share this post


Link to post
Share on other sites
Quote:
Original post by darknebula42
Okay. I packaged it all up if anyone wants to actually see what's going on: test.zip. Before the huge post above I explain a little bit more about what is going on. Anyone with experience might be able to pick out what's up from my descriptions, thanks.


It gives me this error: "This application has failed to start because the application configuration is incorrect. Reinstalling the application may fix this problem."

BTW, did you remove the calls to glFlush() and glFinish()?

Share this post


Link to post
Share on other sites
Yes I did. Hrm..I remember having this problem before. I'm using Visual Studio 2008, and maybe I need to include some kind of manifest file or something?

edit: I updated it, could you try it again. Sorry about that. Thanks!

Share this post


Link to post
Share on other sites
This time it worked.

I see what you mean, the tearing is very noticeable. Unfortunately, I can't think of anything else to suggest.

Share this post


Link to post
Share on other sites
I tried your test program - I can't see any tearing at all (although I did notice that the animation seems to "jump" ahead/behind occasionally). I think your problem simply lies with an imperfect timer implementation running the animation.

Screen tearing should look like this if it were happening (courtesy of mspaint).

Share this post


Link to post
Share on other sites
Quote:
Original post by Hodgman
I tried your test program - I can't see any tearing at all.
...
Screen tearing should look like this if it were happening (courtesy of mspaint).


That's strange, for me the tearing was quite noticeable (like in your picture).

What could cause such a variation?

Share this post


Link to post
Share on other sites
That demo runs in windowed mode which is typically single buffered (except in Aero).
That is instead of merely swapping a pair of hardware pointers the buffers have to be copied to screen in sync with the raster beam, something that is handled less than perfectly on just about any hardware I've ever seen. I guess you might have some luck manually polling the raster line and waiting for the bottom of the window like D3D 8/9 does, but I have no idea how this would interact with GL.

As far as I know the only way to completely eliminate tearing in windowed mode is to use YUV overlays..

Share this post


Link to post
Share on other sites
Quote:
Original post by Hodgman
I tried your test program - I can't see any tearing at all (although I did notice that the animation seems to "jump" ahead/behind occasionally). I think your problem simply lies with an imperfect timer implementation running the animation.

Screen tearing should look like this if it were happening (courtesy of mspaint).


That is actually how mine looks, exactly! And you can't take screenshots of it.

implicit: Very interesting information. So you're saying there really isn't such thing as a double buffered Windowed mode that works? I'll do some test with full screen and check out some more information. Thanks.

edit: Is this what you're talking about, implicit? Swap Control

edit2: I put it in full screen, with double buffering on and still have the same issues!

[Edited by - darknebula42 on June 24, 2008 4:35:19 PM]

Share this post


Link to post
Share on other sites
Tearing happens because you end up with parts of 2 frames on the screen, and one frame is very different the the other. So you see a very obvious 2 pictures. If you are moving a set number of pixels each frame, and you aren't capping it, then a high frame rate will make tearing very likely, or very noticeable. If your movement is very smooth, and there isn't much difference between any 2 frames, you won't notice tearing as much.

Share this post


Link to post
Share on other sites
Quote:
Original post by Daaark
Tearing happens because you end up with parts of 2 frames on the screen, and one frame is very different the the other. So you see a very obvious 2 pictures. If you are moving a set number of pixels each frame, and you aren't capping it, then a high frame rate will make tearing very likely, or very noticeable. If your movement is very smooth, and there isn't much difference between any 2 frames, you won't notice tearing as much.


Yes, that's quite true. But when even moving at about 10 pixels per second it looks bad. And there is no way I shouldn't be able to not move 150 frames per second without tearing if double buffering is enabled.

Share this post


Link to post
Share on other sites
Quote:
Original post by Hodgman
I tried your test program - I can't see any tearing at all (although I did notice that the animation seems to "jump" ahead/behind occasionally). I think your problem simply lies with an imperfect timer implementation running the animation.

Screen tearing should look like this if it were happening (courtesy of mspaint).


I'm using SDL_GetTicks(), does that not have a high enough resolution?

Share this post


Link to post
Share on other sites
Quote:
Original post by hikikomori-san
With all respect, your post is full of wrong statements. You can't write directly to the front buffer

Yes you can (not in current version DX of course, it would be better if I said "you could". I see you're also seeing from a 3D prespective. I'm also taking 2D rendering in mind). See you didn't program 8-bit & 16-bit console games.

Quote:
Original post by hikikomori-san
It has to either copy the contents of your back buffer to the front buffer (D3DSWAPEFFECT_COPY), or flip (swap) the buffers (D3DSWAPEFFECT_FLIP).

Thanks for finishing my explanation. ;-) I did say it wasn't complete.

Quote:
Original post by hikikomori-san
This change happens regardless of how much of the old front buffer has been sent to the card, and for the rest of the screen update, the pixels that the card will send will be from the new front buffer contents, causing the upper half of the screen to display pixels from the old frame and the lower half to display pixels from the new frame


Yep. The screen is filled with mixed old & new content. This is what happened in the front-buffer-only attempt. Plus, tearing was more evident because if we rendered multiple layers, the screen was being updated when not all of the layers have finished drawing, while being mixed with old stuff from the previous frame.
This resulted in very evident tearing. But I have to admit I wrongly stated that using Double buffering solves tearing. (by double-buffer = front+back) But it reduces the effect

In 3D applications, there's no point in calling triple buffering to what you call double buffering, because like you say, you can't render to the front buffer directly. However this make sense for 2D-only applications. This term became popular around 1998 if I recall correctly. Plus the word "triple" sounded cooler (I'm not kidding, this is a marketing issue). And it was a performance optimization to include this option, because it seemed to boost it, but the lack of VRAM prevented many cards from benefit from it (common cards had around 4-8 MB, high-end ones could reach 16MB, or the CPU/GPU wasn't just fast enough).

Like MS says "A tear occurs when a page flip or blt happens at the wrong time". You explained what happens when a flip occurs at the wrong time. I explained what happens when a blit is performed at the wrong time.

Here's more info (I've just googled it):
Wikipedia about triple-buffering
Wikipedia about double-buffering
http://msdn.microsoft.com/en-us/library/ms796537.aspx

Cheers
Dark Sylinc

Share this post


Link to post
Share on other sites
Quote:
Original post by darknebula42
Yes, that's quite true. But when even moving at about 10 pixels per second it looks bad. And there is no way I shouldn't be able to not move 150 frames per second without tearing if double buffering is enabled.
Double buffering really has nothing to do with this. It doesn't matter if you have 180 buffers. If you are rendering fast enough that your machine can render more frames than the machine can actually show on screen, and those frames are not similar enough in appearance to each other, you will see tearing.

When VSync is off, the buffer can be swapped at any time, including when another frame is being drawn on the monitor. So if you render out another scene and then dump it onto the front buffer while the screen is in the middle of drawing the previous frame, the second half of the screen will show the next frame.

If those 2 frames have different content, your eye will see a tear at the point where the new frame was inserted.

You can do some things to prevent it, or minimize it's effect, but you can't stop it completely.

Don't tie the movement and logic of your game to the frame rate, and make sure that you are moving nice and smooth, and it will look good at 60hrtz, regardless of what frame rate you run it at. You're a slave to the refresh rate of your minitor.

Share this post


Link to post
Share on other sites
Quote:
Original post by Daaark
Quote:
Original post by darknebula42
Yes, that's quite true. But when even moving at about 10 pixels per second it looks bad. And there is no way I shouldn't be able to not move 150 frames per second without tearing if double buffering is enabled.
Double buffering really has nothing to do with this. It doesn't matter if you have 180 buffers. If you are rendering fast enough that your machine can render more frames than the machine can actually show on screen, and those frames are not similar enough in appearance to each other, you will see tearing.

When VSync is off, the buffer can be swapped at any time, including when another frame is being drawn on the monitor. So if you render out another scene and then dump it onto the front buffer while the screen is in the middle of drawing the previous frame, the second half of the screen will show the next frame.

If those 2 frames have different content, your eye will see a tear at the point where the new frame was inserted.

You can do some things to prevent it, or minimize it's effect, but you can't stop it completely.

Don't tie the movement and logic of your game to the frame rate, and make sure that you are moving nice and smooth, and it will look good at 60hrtz, regardless of what frame rate you run it at. You're a slave to the refresh rate of your minitor.


So am I correct that using Linear Interpolation is the correct way to do smooth movement?
x += time it took to get here from last movement * velocity

So... sync with the hertz of the monitor or tearing will happen no matter what is the answer?

Share this post


Link to post
Share on other sites
Quote:
Original post by darknebula42
[...]So... sync with the hertz of the monitor or tearing will happen no matter what is the answer?
V-sync is the only way to ensure tearing is eliminated.

Share this post


Link to post
Share on other sites

This topic is 3462 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this