7
votes

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.

3
you are missing to analyze what the source of the performance problem is, this could be a lot of things - a background task, resource loading, too complex code, ...LearnCocos2D
There is no performance issue. The game runs 200+ fps. The stuttering is the issue. Stuttering means the object is not moving smoothly across the screen. You can see the position at different times on the screen in the log I posted. The positions are correct, and linear with the time. Let me know if the question is not clear enough. I have no idea why people are downvoting it!Ed Rowlett-Barbu
With VSync on, it stays at 60fps. The stuttering still occurs.Ed Rowlett-Barbu
I don't see how you can be seeing stuttering in the 60fps case as your figures show you pretty identical delta. If you ignore the timestep logic and just simply add 16.6 to the ypos each frame do you still see stuttering?user146043
I do, yes. That's the weird part.Ed Rowlett-Barbu

3 Answers

2
votes

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.

0
votes

It seems like you're not synchronizing your 60Hz frame loop with the GPU's 60Hz VSync. Yes, you have enabled Vsync in Nvidia but that only causes Nvidia to use a back-buffer which is swapped on the Vsync.

You need to set the swap interval to 1 and perform a glFinish() to wait for the Vsync.

-1
votes

A tricky one, but from the above it seems to me this is not a 'frame rate' problem, but rather somewhere in your 'animate' code. Another observation is the line "Update(stepSizeMicro / 1000000.f);". the divide by 1000000.f could mean you are losing resolution due to the limitations of floating point numbers bit resolution, so rounding could be your killer?