I've been trying to set up fixed-timestep loop for my game, using the technique from here. I have no need for the interpolation step as I intend to run the logic at or above vsync speed.
Here is a simplified version of my test code (C#):
const double MILLISECONDS = 1000.0;
const double TARGET_SPEED = 60.0; //Logic tick rate in hertz
const double dt = MILLISECONDS / TARGET_SPEED;
double currentTime;
double accumulator = 0.0;
Stopwatch logicTimer = new Stopwatch();
double theta = 0;
float spinSpeed = 0.005f;
//Game loop:
while (true)
{
double newTime = logicTimer.ElapsedMilliseconds;
double frameTime = newTime - currentTime;
currentTime = newTime;
accumulator += frameTime;
while (accumulator >= dt)
{
theta = (theta + spinSpeed * dt) % (Math.PI * 2.0);
accumulator -= dt;
}
//Draw sprite, rotated theta radians
Draw();
}
With TARGET_SPEED at 60Hz on a 60Hz monitor, most of the time it runs smoothly. However, often when I run the test I see periodic jumps in the rotation. The time to complete a revolution does not change, but it appears to stand still for an extra frame or two and then jump ahead to make up the difference. The frequency of these stutters is constant during a run, but subsequent runs with the same settings will stutter at different rates.
I've measured how much theta changes each logic step, and it's rock steady across multiple runs regardless of stuttering.
This leads me to believe the problem is something to do with differences between my game's clock and my monitor's refresh clock. However, I can't figure out anything beyond that.
The problem still happens whether vsync is on or off. It happens less often when TARGET_SPEED is a multiple of 60; for any other value it happens on every run. Increasing TARGET_SPEED past 1000 or 10,000 Hz appears much smoother, but there's no way I can run a real game that fast. Drawing more or fewer sprites to control the time spent rendering doesn't seem to have any consistent effect on the problem.
So basically I'm completely stumped. Any idea why this is happening and how I can fix it?
Here's the full source file for my test project (XNA) if you want to try it for yourself.