How Exactly Does VSync Work?

Started by
7 comments, last by szecs 13 years, 11 months ago
I'm very confused. I have a test app, just a basic cube in OpenGL with the ability to rotate it using the keyboard. I capped the logic of my application to 50 frames per second but let it render as fast as possible. Just out of curiosity I added a counter at the end of my main loop to count the amount of times the main loop is iterated. Now, my monitor has a refresh rate of 60hz and with VSync enabled I determined that my main loop was iterated ~110 times per second. With VSync disabled the main loop completed 1400 iterations per second on average. My code is basically like this:

main {
    if (isTimeForNextFrame()) {
        input();
        logic();
    }

    render();
    counter++;
}


My question is, what's with these numbers? What exactly does VSync do to cap the rendering and why is it affecting my main loops iterations per second? [Edited by - X Abstract X on April 30, 2010 11:42:30 PM]
Advertisement
SwapBuffers blocks until it renders :( You could run your renderer in a separate thread. I think there's also some utility function provided with OpenGL that allows you to check if there's currently a buffer being rendered (thereby allowing you the chance to skip calling SwapBuffer altogether).
So 60 times a second, the contents of your video memory is pushed to your monitor to be displayed on the screen.

If you changed video memory while it was being pushed to the screen youd get half of one frame and half of another frame.

60 times a second is fast but this problem is EXTREMELY noticeable and is called "tearing".

anyhow, what vsync does is wait til the screen refresh is done and then alters the video memory (by copying the back buffer to the front buffer) so that it has plenty of time to update the memory before it's shown again.

That eliminates tearing by making sure that by the time the monitor refresh happens, that the video memory is not in a state of change (:
Quote:
What exactly does VSync do to cap the rendering and why is it affecting my main loops iterations per second?


See vertical synchronization. Quite simply, vsync waits until the display device finishes rendering a frame before pushing a new frame to the display (prevents page tearing). This wait is affecting the iterations per second. However, you shouldn't worry about how many times a second your loop is executed, since you're regulating the logic frequency. In other words, it is guaranteed that the application states of two machines running at different speeds (provided they are running at an frequency equal to or greater than your logic frequency) will be the same at any given time.

Also, if you're not performing interpolation of any sort in between logic frames, then you really aren't gaining anything by rendering as fast as possible, since the render frames in between logic frames will basically be the same image. [smile] Besides, if your display device can only present 60 unique images a second, why should you try to feed it 1000 unique images a second if only 60 of them will actually be displayed?

EDIT:
"Fix Your Timestep" is an excellent read on the subject of regulating update frequencies yet rendering as fast as possible.

[Edited by - _fastcall on May 1, 2010 1:56:15 AM]
If swapbuffers() blocks until it renders, why is my main loop executing 110 times and not 60 times?

@_fastcall: Hmm, I was reading "Fix Your Timestep" earlier and it seems to me that it is only really useful for physics intensive games to interpolate. In my current set-up I accumulate the delta time for each frame and only render if the logic is keeping up with the specified fps.

bool isTimeForNextFrame() {    unsigned int frameStartTime = SDL_GetTicks();    unsigned int deltaTime = frameStartTime - _previousFrameStartTime;    _previousFrameStartTime = frameStartTime;    _accumulatedTime += deltaTime;    if (_accumulatedTime >= _msPerFrame)        _frame++;        _accumulatedTime -= _msPerFrame;        return(true);    }    return(false);    }while (gameRunning) {    while (isTimeForNextFrame()) {        logic();    }    render();}


Does doing that make any sense?
"Fix Your Timestep" doesn't apply exclusively for intense physics games, it applies to any game that wishes to ensure that the simulations are accurate across multiple machines of varying speeds while achieving high frame rates at the same time. For instance, the setup you have right now, won't run smoothly if you set the logic update frequency to update once per second, even though you're rendering the same state more than a thousand times per second. If you interpolate between the two states, you get the appearance of running the game at thousands of frames per second, even though your logic is only being updated once per second. Here's a setup I used for the pong game:

static const int ticksPerSecond = 1000; // How many ticks in a secondstatic const int logicUpdateFrequency = 30; // How many times per second to update logicstatic const int ticksPerLogicFrame = ticksPerSecond / logicUpdateFrequency; // How many ticks to a framestatic const float dt = 1.f / logicUpdateFrequency; // The elapsed game time a logic update takesint elapsedTicks;  // How long in ticks an iteration of the main game loop takesint previousTime, currentTime = 0; // Time trackersint bukkit = 0; // The accumulator (lol)bool running = true;while ( running ) {	// Event Handling	// .	// .	// .		// Logic	previousTime = currentTime;	currentTime = SDL_GetTicks();	elapsedTicks = currentTime - previousTime;		for ( bukkit += elapsedTicks; bukkit >= ticksPerLogicFrame; bukkit -= ticksPerLogicFrame )		update( dt );		// Rendering	render( static_cast<float>( bukkit ) / ticksPerLogicFrame ); // range from [0 1), 0 being the previous state, 1 being the current state}


And a sample entity might do something like so:

class SampleEntity {public:	void Update( float dt );	void Render( float interp );private:	float oldAlpha, currentAlpha;	Vector2 oldPos, currentPos;	Vector2 heading;};void SampleEntity::Update( float dt ) {	// Set this entity's previous state	oldPos = currentPos;	oldAlpha = currentAlpha;		// Do some wandering	heading += Vector2( rand()%10 - 5, rand()%10 - 5 );	if ( heading.x || heading.y )		heading.normalize();	// Set this entity's current state	currentPos += heading * dt;	currentAlpha = sin( gCurrentTime * 180 / twopi ) * 127 + 128;}void SampleEntity::Render( float interp ) {	Vector2 finalPos = (currentPos - oldPos) * interp + oldPos;	float finalAlpha = (currentAlpha - oldAlpha) * interp + oldAlpha;		myRenderer->DrawFoobar( finalPos, finalAlpha );}
Thanks. You've convinced me, this is worth doing even if I don't need the benefits right now. I'm going to fix my timestep!
Quote:Original post by _fastcall
"Fix Your Timestep" doesn't apply exclusively for intense physics games, it applies to any game that wishes to ensure that the simulations are accurate across multiple machines of varying speeds while achieving high frame rates at the same time. For instance, the setup you have right now, won't run smoothly if you set the logic update frequency to update once per second, even though you're rendering the same state more than a thousand times per second. If you interpolate between the two states, you get the appearance of running the game at thousands of frames per second, even though your logic is only being updated once per second. Here's a setup I used for the pong game:

*** Source Snippet Removed ***

And a sample entity might do something like so:

*** Source Snippet Removed ***


I was getting crazy around this subject, even after reading "Fix your Time Step".
Your explanation made everything clear.

Thanks!
Alfred Reinold Baudisch[Game Development Student] [MAC lover] [Ruby, Ruby on Rails and PHP developer] [Twitter]
Yup, when I read that article, I didn't understand a thing.

I found this one easier to understand:
http://dewitters.koonsolo.com/gameloop.html it took a while to understand (but a craping always helps, when I need to think things over)

This topic is closed to new replies.

Advertisement