Jump to content

  • Log In with Google      Sign In   
  • Create Account

#ActualHodgman

Posted 01 April 2013 - 05:32 AM

I added a bunch of logging to my timing code, and tried adding Frank's fixup.
Originally, I had an accumulator for a fixed time-step, and no interpolation (which simplifies things), very simple:

static float accumulator = 0;
accumulator += deltaTime;
const float stepSize = 1/60.0f;
while( accumulator >= stepSize )
{
	accumulator -= stepSize;
	DoPhysics( stepSize );
}

And then I added this 'snap to vsync' code above it:

static float buffer = 0;
float actualDelta = deltaTime + buffer;
float frameCount = floorf(actualDelta * 60.0f + 0.5);//I did this a bit differently - rounding to nearest number of frames
frameCount = max(1.0f, frameCount);
deltaTime = frameCount / 60.0f;
buffer = actualDelta - deltaTime;

Without the fixup, the accumulator would gradually increase it's remainder, until over about 1000 frames, a 'spare' 1.66ms of time builds up in the accumulator, and the physics loop runs twice in a single frame. Occasionally when this occurs, the next frame will only measure a delta of slightly less than 1/60th, e.g. 0.016654s, which means that no physics update occurs this frame. Then occasionally the next frame will be slightly over 1/60th, which when added to the accumulator, results in another two physics steps.
So typically, I'm getting 1 physics step a frame. Then once every few thousand frames, I take 2 steps in one frame, then 0, then 2, then back to normal. I hadn't noticed this small quirk, and only found it now due to this thread!
 
With the fixup, things are much more stable around this edge case. When the 'buffer' and/or accumulator build up enough extra time, then I still do two physics updates in a frame. However, the case where this is followed by zero the next frame is gone (and also the case where the 'zero' frame is followed by a 'two' frame is also gone).
 
So, from that, it seems to be a pretty good idea in general, and I think if I was using interpolation/extrapolation in my fixed-time-step code, then this fix would be even more important! As is, my step size matches my refresh rate, but in general I can't rely on this being true, so I need to add interpolation at some point. Without the fix, I'm guessing the original jittery timings would have a large impact on the perceived quality of the interpolation. Thanks frank biggrin.png

 

[edit]I also did some testing on what my actual average frame time was, but it was a bit inconsistant run-to-run. One run it averaged to 0.01667, another 0.01668, and another 0.01666... Also, at different times, the average would either be slowly creeping upwards or downwards.


#2Hodgman

Posted 01 April 2013 - 03:41 AM

I added a bunch of logging to my timing code, and tried adding Frank's fixup.
Originally, I had an accumulator for a fixed time-step, and no interpolation (which simplifies things), very simple:
static float accumulator = 0;
accumulator += deltaTime;
const float stepSize = 1/60.0f;
while( accumulator >= stepSize )
{
	accumulator -= stepSize;
	DoPhysics( stepSize );
}
And then I added this 'snap to vsync' code above it:
static float buffer = 0;
float actualDelta = deltaTime + buffer;
float frameCount = floorf(actualDelta * 60.0f + 0.5);//I did this a bit differently - rounding to nearest number of frames
frameCount = max(1.0f, frameCount);
deltaTime = frameCount / 60.0f;
buffer = actualDelta - deltaTime;
Without the fixup, the accumulator would gradually increase it's remainder, until over about 1000 frames, a 'spare' 1.66ms of time builds up in the accumulator, and the physics loop runs twice in a single frame. Occasionally when this occurs, the next frame will only measure a delta of slightly less than 1/60th, e.g. 0.016654s, which means that no physics update occurs this frame. Then occasionally the next frame will be slightly over 1/60th, which when added to the accumulator, results in another two physics steps.
So typically, I'm getting 1 physics step a frame. Then once every few thousand frames, I take 2 steps in one frame, then 0, then 2, then back to normal. I hadn't noticed this small quirk, and only found it now due to this thread!
 
With the fixup, things are much more stable around this edge case. When the 'buffer' and/or accumulator build up enough extra time, then I still do two physics updates in a frame. However, the case where this is followed by zero the next frame is gone (and also the case where the 'zero' frame is followed by a 'two' frame is also gone).
 
So, I from that, it seems to be a pretty good idea in general, and I think if I was using interpolation/extrapolation in my fixed-time-step code, then this fix would be even more important.

#1Hodgman

Posted 01 April 2013 - 03:28 AM

I added a bunch of logging to my timing code, and tried adding Frank's fixup.
Originally, I had an accumulator for a fixed time-step, and no interpolation (which simplifies things), very simple:

static float accumulator = 0;
accumulator += deltaTime;
while( accumulator >= stepSize )
{
	accumulator -= stepSize;
	DoPhysics( stepSize );
}

And then I added this 'snap to vsync' code above it:

static float buffer = 0;
float actualDelta = deltaTime + buffer;
float frameCount = floorf(actualDelta * 60.0f + 0.5);//I did this a bit differently - rounding
frameCount = max(1.0f, frameCount);
deltaTime = frameCount / 60.0f;
buffer = actualDelta - deltaTime;

Without the fixup, the accumulator would gradually increase it's remainder, until over about 1000 frames, a 'spare' 1.66ms of time builds up in the accumulator, and the physics loop runs twice in a single frame. Occasionally when this occurs, the next frame will only measure a delta of slightly less than 1/60th, e.g. 0.016654s, which means that no physics update occurs this frame. Then occasionally the next frame will be slightly over 1/60th, which when added to the accumulator, results in another two physics steps.
So typically, I'm getting 1 physics step a frame. Then once every few thousand frames, I take 2 steps in one frame, then 0, then 2, then back to normal. I hadn't noticed this small quirk, and only found it now due to this thread!

 

With the fixup, things are much more stable around this edge case. When the 'buffer' and/or accumulator build up enough extra time, then I still do two physics updates in a frame. However, the case where this is followed by zero the next frame is gone (and also the case where the 'zero' frame is followed by a 'two' frame is also gone).

 

So, I from that, it seems to be a pretty good idea in general, and I think if I was using interpolation/extrapolation in my fixed-time-step code, then this fix would be even more important.


PARTNERS