Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fixed timestep stuttering with VSync on

In a 2D OpenGL engine I implemented I have a fixed timestep as described in the famous fix your timestep article, along with blending.

I have a test object that moves vertically (y axis). There is stuttering in the movement (preprogrammed movement, not from user input). This means the object does not move smoothly across the screen.

Please see the uncompressed video I am linking: LINK

The game framerate stays at 60fps (Vsync turned on from Nvidia driver)

The game logic updates at a fixed 20 updates/ticks per second, set by me. This is normal. The object moves 50 pixels per update.

However the movement on the screen is severely stuttering.

EDIT: I noticed by stepping in the recorded video above frame by frame that the stuttering is caused by a frame being shown twice.

EDIT2: Setting the application priority to Realtime in the task manager completely eliminates the stutter! However this obviously isn't a solution.

Below is the object y movement delta at different times, with VSync turned off First column is the elapsed time since last frame, in microseconds (ex 4403 ) Second column is movement on the y axis of an object since last frame. Effectively, the object moves 1000 pixels per second, and the log below confirms it.

time since last frame: 4403    ypos delta since last frame: 4.403015
time since last frame: 3807    ypos delta since last frame: 3.806976
time since last frame: 3716    ypos delta since last frame: 3.716003
time since last frame: 3859    ypos delta since last frame: 3.859009
time since last frame: 4398    ypos delta since last frame: 4.398010
time since last frame: 8961    ypos delta since last frame: 8.960999
time since last frame: 7871    ypos delta since last frame: 7.871002
time since last frame: 3985    ypos delta since last frame: 3.984985
time since last frame: 3684    ypos delta since last frame: 3.684021

Now with VSync turned on

time since last frame: 17629     ypos delta since last frame: 17.628906
time since last frame: 15688     ypos delta since last frame: 15.687988
time since last frame: 16641     ypos delta since last frame: 16.641113
time since last frame: 16657     ypos delta since last frame: 16.656738
time since last frame: 16715     ypos delta since last frame: 16.715332
time since last frame: 16663     ypos delta since last frame: 16.663086
time since last frame: 16666     ypos delta since last frame: 16.665771
time since last frame: 16704     ypos delta since last frame: 16.704102
time since last frame: 16626     ypos delta since last frame: 16.625732

I would say they look ok.

This has been driving me bonkers for days, what am I missing?

Below is my Frame function which is called in a loop:

void Frame()
{
static sf::Time t;
static const double ticksPerSecond = 20;
static uint64_t stepSizeMicro = 1000000 / ticksPerSecond; // microseconds
static sf::Time accumulator = sf::seconds(0);

gElapsedTotal = gClock.getElapsedTime();

sf::Time elapsedSinceLastFrame = gElapsedTotal - gLastFrameTime;
gLastFrameTime = gElapsedTotal;


if (elapsedSinceLastFrame.asMicroseconds() > 250000 )
    elapsedSinceLastFrame = sf::microseconds(250000);

accumulator += elapsedSinceLastFrame;

while (accumulator.asMicroseconds() >= stepSizeMicro)
{
    Update(stepSizeMicro / 1000000.f);
    gGameTime += sf::microseconds(stepSizeMicro);
    accumulator -= sf::microseconds(stepSizeMicro);
}
uint64_t blendMicro = accumulator.asMicroseconds() / stepSizeMicro;
float blend = accumulator.asMicroseconds() / (float) stepSizeMicro;
if (rand() % 200 == 0) Trace("blend: %f", blend);
CWorld::GetInstance()->Draw(blend);
}

More info as requested in the comments:

  • stuttering occurs both while in fullscreen 1920x1080 and in window mode 1600x900

  • the setup is a simple SFML project. I'm not aware if it uses VBO/VAO internally when rendering textured rectangles

  • not doing anything else on my computer. Keep in mind this issue occurs on other computers as well, it's not just my rig

  • am running on primary display. The display doesn't really make a difference. The issue occurs both in fullscreen and window mode.

like image 476
Ed Rowlett-Barbu Avatar asked Oct 06 '14 14:10

Ed Rowlett-Barbu


1 Answers

I have profiled my own code. The issue was there was an area of my code that occasionally had performance spikes due to cache misses. This caused my loop to take longer than 16.6666 milliseconds, the max time it should take to display smoothly at 60Hz. This was only one frame, once in a while. That frame caused the stuttering. The code logic itself was correct, this proved to be a performance issue.

For future reference in hopes that this will help other people, how I debugged this was I put an

if ( timeSinceLastFrame > 16000 ) // microseconds
{
    Trace("Slow frame detected");
    DisplayProfilingInformation();
}

in my frame code. When the if is triggered, it displays profiling stats for the functions in the last frame, to see which function took the longest in the previous frame. I was thus able to pinpoint the performance bug to a structure that was not suitable for its usage. A big, nasty map of maps that generated a lot of cache misses and occasionally spiked in performance.

I hope this helps future unfortunate souls.

like image 148
Ed Rowlett-Barbu Avatar answered Oct 22 '22 07:10

Ed Rowlett-Barbu