Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Question about Allegro 5 timers tutorial: variable timestep when dt ~= 0

This thread is locked; no one can reply to it. rss feed Print
Question about Allegro 5 timers tutorial: variable timestep when dt ~= 0
Mr. Accident
Member #3,508
May 2003
avatar

I was playing around with the timing method described in this Allegro 5 timers tutorial, and I noticed that when I increased the rendering workload to the point where it started to lag considerably, the reported "framerate" would jump through the roof, up to over a million frames per second.

Looking at the code, this is because it uses the rather simplistic method of taking the reciprocal of the time delta at each step as the "framerate." When the rendering takes enough time to start causing update lag, it skips rendering and just continues with the event loop, with the result being that the next update sees a delta-t very close to zero, hence the very high reciprocal.

What I'm wondering is, if you're using variable timesteps, why even bother performing an update with a time delta so close to zero as to make no odds? Wouldn't it make more sense if, when lag causes a frame skip, you skip the backed-up update ticks as well, and just wait for the next scheduled tick? Just processing all the queued updates without rendering would give each one a time delta only as long as it took to process the previous update, which at least for simpler games would be negligible.

(Perhaps the tutorial example was just left that way for simplicity, since obviously skipping updates would require also accumulating the skipped deltas, since while negligible individually they might add up if a lot of frames get skipped...)

I guess in a game with more CPU-intensive logic, just the act of processing an update without rendering might take enough time for the game state to change appreciably, but as a general rule, does it make sense to just toss out updates with extremely small time delta values?

StevenVI
Member #562
July 2000
avatar

Howdy, I wrote the tutorial you are referencing.

I kind of had to diagram out the program's execution to remember how it goes:

  • Every 1/refresh_rate seconds, a timer event is fired.

  • When the timer event is processed, update the position.

  • If the timer event is processed more than 1/refresh_rate seconds after it was fired, do not draw, and hope (cross your fingers!) that the next frame's update code will execute fast enough so that the frame can be drawn.

why even bother performing an update with a time delta so close to zero as to make no odds? Wouldn't it make more sense if, when lag causes a frame skip, you skip the backed-up update ticks as well, and just wait for the next scheduled tick?

An interesting point. I suppose that you are correct in that it would not be necessary. If you see a very small time step (you'd have to figure out what "very small" is though, 1/10 of a frame? 1/100 of a frame?), it might make sense to just tack it on to the end of the next frame.

From my perspective, it is more work than it's worth, since it shouldn't affect too much. That said, the "cross your fingers" part in my outline could cause issues on CPU-heavy programs. I guess since I have never had issues (yet) with the way I do things, I haven't worried about that particular aspect.

__________________________________________________
Skoobalon Software
[ Lander! v2.5 ] [ Zonic the Hog v1.1 ] [ Raid 2 v1.0 ]

Neil Walker
Member #210
April 2000
avatar

Is the delta time approach just one persons view of handling speed consistency or the new A5 'recommended' route to using timers? I use the fixed FPS method (e.g. similar to http://wiki.allegro.cc/index.php?title=Timers) and have never had any problems.

Neil.
MAME Cabinet Blog / AXL LIBRARY (a games framework) / AXL Documentation and Tutorial

wii:0356-1384-6687-2022, kart:3308-4806-6002. XBOX:chucklepie

count
Member #5,401
January 2005

No. Do it the way you like more.
I think most a5 examples (if not all) come with fixed timing.

I'm more the delta time guy :)

Elias
Member #358
May 2000

No, delta time is always bad for various reasons (you need to integrate, the game will work differently depending on available CPU...) - but that's unrelated to A5. You can use whatever you want.

--
"Either help out or stop whining" - Evert

count
Member #5,401
January 2005

Elias said:

the game will work differently depending on available CPU

Huh? How comes that?
If my movement for example is calculated like this:

void update(float delta)
{
    x = x + (speed_x * delta);
}

How can that differ based on the used cpu?
Not trolling. I just want to learn why this could cause problems.

Elias
Member #358
May 2000

Well, assume delta is 0.1 for player A, 0.5 for player B, a game sprite is at x = 0 at the beginning and speeding up from 0 to 10 during 1 second.

Player A:

frame 0: x = 0.00 + (0.00 * 0.10) = 0.00
frame 1: x = 0.00 + (1.00 * 0.10) = 0.10
frame 2: x = 0.10 + (2.00 * 0.10) = 0.30
frame 3: x = 0.30 + (3.00 * 0.10) = 0.60
frame 4: x = 0.60 + (4.00 * 0.10) = 1.00
frame 5: x = 1.00 + (5.00 * 0.10) = 1.50
frame 6: x = 1.50 + (6.00 * 0.10) = 2.10
frame 7: x = 2.10 + (7.00 * 0.10) = 2.80
frame 8: x = 2.80 + (8.00 * 0.10) = 3.60
frame 9: x = 3.60 + (9.00 * 0.10) = 4.50

After one second, the sprite is at x = 4.5 pixel.

Now the very same sprite, starting at x = 0, speeding up from 0 to 10 during 1 second, for player B with delta 0.5:

frame 0: x = 0.00 + (0.00 * 0.50) = 0.00
frame 1: x = 0.00 + (5.00 * 0.50) = 2.50

After one second, the sprite is at x = 2.5 pixel. So player A moved almost twice as fast.

You can of course solve this if you properly integrate... but it gets quite hard. And now assume you are actually using some kind of physics library with collision response and so on - then it's basically impossible to get it working the same for everyone when not using a fixed frequency.

--
"Either help out or stop whining" - Evert

count
Member #5,401
January 2005

Wicked! Thanks for that example!

Would it help doing something like this? (I mean it would help, but is it a good way to something like it?)
This would basically turn the delta timed method into a fixed timed method:

total_delta = 0;
while(game)
{
    delta_time = get_delta();
    tatal_delta += delta;
    if(tatal_delta >= 0.5) // 0.5 = only a value picked to show the example
    {
        update_game(total_delta);
        total_delta = 0;
    }
}

Elias
Member #358
May 2000

Yes. Myself I like to use interpolation while rendering, so the game looks as smooth as possible. E.g. if the sprite is updated at a fixed rate of 30 times a second, but a monitor vsync happens 75 times a second, you can interpolate quite well where it should be at each vsync.

Like, assume the sprite was at pixel 100 at tick 0 (time 0), and it's at position 110 at tick 1 (time 0.0333 with 30 FPS). The first two renders happen at times 0 and 0.0133 (75Hz). So you can now interpolate:

time 0 ->  pixel 100
time 0.0333 -> pixel 110

so you render at: 100 + 0.0133 * (110 - 100) / (0.0333 - 0) = 104.000

So now even though the game logic only ever had the sprite at position 100 and 110, I render it at 104 (which is just the same position the delta time method would have rendered it at). So I get no visual difference, but keep all the advantages of using fixed step.

Still, this is all just preference, you can use whatever you want (also delta time).

--
"Either help out or stop whining" - Evert

Mr. Accident
Member #3,508
May 2003
avatar

How do you interpolate between tick 0 and tick 1 when tick 1 hasn't happened yet? Or do you precompute the next scheduled tick before interpolating, or some other scheme like that? Also, couldn't assuming linear interpolation cause slightly inconsistent movement in between logical frames if the object in question is accelerating?

I'm kinda curious in general about the relative advantages and drawbacks of variable vs. fixed timestep. In the past I've always liked the fixed time approach because of its simplicity -- is the only real problem with it the fact that it might not appear as smooth if the update frequency is lower than the refresh rate?

Also, I get that updating the visual position of elements at the same frequency as the display's refresh rate provides the smoothest results, but how noticeable are the differences, generally? I don't have any high-refresh-rate monitors to test on; just LCDs that all run at 60Hz. :P

amber
Member #6,783
January 2006
avatar

I'm kinda curious in general about the relative advantages and drawbacks of variable vs. fixed timestep

Fixed timesteps are good and variable timesteps are bad. I hate to sound like "that's the way it is and that's all there is to it," but, variable timesteps introduce a lot of problems, most of which I experienced the hard way when I was playing with variable timesteps ;D

- It's difficult to get physics to behave. Even rather sophisticated numerical integrators have problems if the timestep changes too much. The kind of stupid algorithms most Allegro games use will suffer quite badly.

- Any sort of "timer based" (do this after N ticks/seconds/whatever) events will be very difficult to coordinate.

The one advantage you cited can be mitigated by just using a smaller fixed timestep. :)

Neil Black
Member #7,867
October 2006
avatar

amber said:

Fixed timesteps are good and variable timesteps are bad.

And that seems to be the only side we're hearing in this thread. If variable timestep is so bad then why is it even mentioned at all?

Mr. Accident
Member #3,508
May 2003
avatar

amber said:

- It's difficult to get physics to behave. Even rather sophisticated numerical integrators have problems if the timestep changes too much.

Well, under normal circumstances, why would the timestep change considerably? It seems to me that this would only occur during, for example, sudden periods of unusually heavy load. In this case, couldn't you mitigate the problem by capping the timestep to a reasonable amount above the expected value?

The overall game speed would slow down in these (hopefully uncommon) circumstances, but that seems reasonable if the alternative is wonky integration.

amber said:

- Any sort of "timer based" (do this after N ticks/seconds/whatever) events will be very difficult to coordinate.

I don't see why it would be that difficult. Instead of checking for a number of ticks since the timer started, count the number of seconds. Something like this, maybe:

void timer::update(double dt) {
    time_remaining -= dt;

    if(time_remaining < 0) {
        fire_event();
    }
}

amber said:

The one advantage you cited can be mitigated by just using a smaller fixed timestep. :)

True, but some CRT monitors support pretty high refresh rates. And if I increased my logic framerate to accommodate those, I'd be running way faster than necessary on most other systems.

But on the other hand, given the integration difficulties Elias pointed out, I guess you might run into considerable gameplay differences if your update logic can run anywhere from 60Hz to 120Hz or higher, depending on the hardware...

That's partly why I was asking just how noticeable the lack of smoothness really is on high refresh rate monitor, given, say, a fixed update frequency of 60 frames per second. That's what I've always used in the past, and if it doesn't really have any noticeably deleterious effects, I'd just as soon keep using that. :)

SiegeLord
Member #7,827
October 2006
avatar

The overall game speed would slow down in these (hopefully uncommon) circumstances, but that seems reasonable if the alternative is wonky integration.

With fixed step timing you don't need to slow down, so that's the other alternative.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

amber
Member #6,783
January 2006
avatar

Well, under normal circumstances, why would the timestep change considerably? It seems to me that this would only occur during, for example, sudden periods of unusually heavy load. In this case, couldn't you mitigate the problem by capping the timestep to a reasonable amount above the expected value?

You could. Of course, then you're very close to just putting the timestep into an "accumulator" when it's below an minimum value, making the minimum and maximum equal, and then you're using a fixed timestep. ;)

Quote:

I don't see why it would be that difficult. Instead of checking for a number of ticks since the timer started, count the number of seconds. Something like this, maybe:

void timer::update(double dt) {
    time_remaining -= dt;

    if(time_remaining < 0) {
        fire_event();
    }
}

Changing the unit of measurement doesn't eliminate the problem. The timer is still not firing on time-- it's firing whenever an update happens that causes the time_remaining to drop below 0. This may or may not be on time. The problem is at its worst when the time_remaining is close to but a little over the usual value of dt.

For a timer of 0.02, if dt is 0.01, we seem safe:

- update 0: time_remaining = 0.02; time_elapsed = 0.00
- update 1: time_remaining = 0.01; time_elapsed = 0.010
- update 2: time_remaining = 0.00; time_elapsed = 0.020 -- right on time

If dt is a little bigger (0.011), we're late.

- update 0: time_remaining = 0.02; time_elapsed = 0.000
- update 1: time_remaining = 0.09; time_elapsed 0.011
- update 2: time_remaining = -0.002; time_elapsed 0.022 -- late

If dt is a little smaller (0.009), we're really late.

- update 0: time_remaining = 0.02; time_elapsed = 0.000
- update 1: time_remaining = 0.011; time_elapsed = 0.009
- update 2: time_remaining = 0.002; time_elapsed = 0.018
- update 3: time_remaining = -0.007; time_elapsed = 0.027 -- very late

Quote:

how noticeable the lack of smoothness really is on high refresh rate monitor, given, say, a fixed update frequency of 60 frames per second.

At least to me, it's not usually that noticeable, except if you're moving something very fast. Things going much more than 4 pixels/tick start to look really choppy.

Mr. Accident
Member #3,508
May 2003
avatar

amber said:

You could. Of course, then you're very close to just putting the timestep into an "accumulator" when it's below an minimum value, making the minimum and maximum equal, and then you're using a fixed timestep. ;)

Well, that would be a fixed timestep of a sort, yes, but in a situation like the tutorial example, that nominal "fixed" rate will still be different depending on the hardware refresh rate. I mean, in ideal circumstances, the "variable" timestep will be constant throughout, with dt always being 1/(refresh rate).

amber said:

Changing the unit of measurement doesn't eliminate the problem. The timer is still not firing on time-- it's firing whenever an update happens that causes the time_remaining to drop below 0. This may or may not be on time. The problem is at its worst when the time_remaining is close to but a little over the usual value of dt.

Well, strictly speaking you have this problem even with fixed timesteps, if you try to set a timeout that isn't a multiple of the timestep. :P But I see your point; in particular if your game might conceivably be running at 60Hz, 75Hz, 80Hz, or even 100Hz or higher, this inhibits your ability to count durations with precision.

Neil Walker
Member #210
April 2000
avatar

Physics engines like box2d use fixed time steps, in fact use sub-stepping. Don't know where I'm going with this, just thought I'd mention it :)

Neil.
MAME Cabinet Blog / AXL LIBRARY (a games framework) / AXL Documentation and Tutorial

wii:0356-1384-6687-2022, kart:3308-4806-6002. XBOX:chucklepie

Go to: