Jump to content
  • Advertisement
Sign in to follow this  
  • entries
  • comments
  • views

thoughts on fix-your-timestep and the spiral of death

Sign in to follow this  
Norman Barrows


thoughts on fix-your-timestep and the spiral of death:

in the fix-your timestep algo its generally assumed that on average ET will be < DT.

with no ET cap, when accumulator >= 2*DT, fix-your-timestep will update twice before rendering (IE it drops a frame).

while this is only an issue with slow software, it can be a game-breaker.

the typical approach is to cap ET to some max value. This guarantees that update will run no more than N times in a row before the next render, where N = (accumulator+ET) / DT (rounded down).

but accumulator can be any value from 0 to DT. So it would probably be more accurate to cap accumulator, not ET.

But what cap value?

It depends on how many frames you're willing to drop.

To never drop frames, the cap is DT.

this would lead to a slightly different update:

instead of:accumulator+=ETwhile (accumulator>=DT) { accumulator-=DT update_all }
you get something like:accumulator+-ETif (accumulator >=DT) { accumulator-=DT update_all if (accumulator>=DT) { // we've hit spiral of death and are updating more than once per render! } }
But now, what do you do if you hit the spiral of death?

Well, we don't want to drop a frame. From the point of view of the player, when we drop a frame, update starts running faster at the same time that render slows down. This makes it hard for the player to react in time.

So we don't want to call update again, we want to render again first. Yes, this puts render in lockstep with update temporarily - same as any ET cap.

But what do we do with accumulator? Leave it the way it is? It's already full or overflowing. and next update, we'll just add more ET to it, making it even more over-full. So that won't work. Setting it to DT (just full) will guarantee an update after the next render. But if you just set it to zero, at worst, render suddenly gets its act back together and ET drops back below DT, and you render a few frames before the next update_all. So setting it to zero seems pretty safe.

This leads to the following:accumulator+=ETif (accumulator >= DT) { accumulator-=DT update_all if (accumulator>=DT) { // accumulator>=DT*2 spiral of death / frame drop! // clear accumulator, and don't call update_all. accumulator=0 } }
This algo implements fix-your-timestep with an accumulator cap of DT, and will never drop a frame. When ET goes high, it reverts to lockstep behavior of one render, one input, one update per cycle - which degrades gracefully under heavy load - just what you need when ET is high. Note that by comparison, your typical ET cap will only revert to lockstep behavior of the form: one render, one input, N updates per cycle, where N = (accumulator+ET) / DT (rounded down). With a low enough ET cap (specifically: capping accumulator to DT, or ET to DT-accumulator... its the same thing), similar behavior can be obtained with an ET cap and the more common "while (accumulator>=DT)" form of the algo.

Happy coding! :)

Sign in to follow this  


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!