2019.3468 - Timer fixes are terrible and unnecessary.

Before this daily build firing this code:

timer.performWithDelay( 5000, function() print('done') end, 1 ) local i = 0 timer.performWithDelay( 10, function() i = i + 1 if i == 500 then print('done2') end end, 500 )

Would result in printing both ‘done’ and ‘done2’ at the same time.

In 2019.3468  because timer is now bound with framerate ‘done2’ is printed few SECONDS!!! later. That denies the purpose of the timer. Now synchronizing any data based on timer is impossible without desynchronization. Also there are problems with using timers and physical bodies because of being bound to frames.

Other issue with having timers bound to frame rate is desynchronization if framerate is unstable/framedrops occur.

Please take a look at this issue and bring back old timer implementation.

Sorry, we discovered that firing timer more than once per frame broke a lot of actual existing games. Inserting timers with periods more than one frame would accurately work. We could add a flag or a setting, that timer would work as you suggest, but it should not break existing games.

Having additional flag or a setting seems like a reasonable solution.

Is this really true? I.e. wasn’t the previous way timers were fired, the one for a long time, so isn’t there a big chance to break much more existing apps, once they get rebuilt with a new version of the SDK and they may break in very subtle ways not noticed immediately or dependent on the devices or levels or whatever will be tested?

If anything, such a fundamental change which is actually a workaround for badly written code, should be the one to opt in by a flag. If anyone creates a repeating timer with frequency higher than the actual framerate, this code should handle multiple triggers per frame as it’s what it actually requested. If one wants a trigger per frame, that’s what enterFrame is for.

Vlad, is there a place where there is a bit more information on what exactly has changed than what is mentioned in the daily build notes?

It seems like this is a substantial change and deserves or needs more attention than this.

I for one am wondering how timers used to work, and how they work now.

Hello, everyone. You are right, expanded explanation is due. Sorry for not providing it in full. Here’s what happened:

  1. On September 6 I accepted pull request which made recurring timer not trim time to the beginning of the frame. 
  2. As unintended consequence, it enabled behaviour that timers with interval less than a frame would fire multiple times per frame.
  3. We got several complaints, because apparently many people was relying for timer working similar to frame count.
  4. In commit at Feb 26 I changed behaviour of timer so it: didn’t fire timers more than once per frame and did not crop time: issue which was supposed to be fixed in (1).
  5. Today, I made a commit which would introduce “timer.allowIterationsWithinFrame”. Flag, if set to true, timers created after it would have behaviour similar to that which we had between (1) and (4).
     
    I will wait for comments until tonight, and if there’s no objections I’ll push changes to daily build. Please, share your thoughts on this solution.
     
     
    EDIT: Come demo

 

timer.performWithDelay(     5000,     function()         print('done')     end,     1 )   timer.allowIterationsWithinFrame = true local i = 0 timer.performWithDelay(     10,     function()         i = i + 1         if i == 500 then             print('done2')         end     end,     500 ) timer.allowIterationsWithinFrame = false     local j = 0 timer.performWithDelay(     10,     function()         j = j + 1         i f j == 500 then             print('done3')         end     end,     500 )

 

This code would have done and done2 printed about at the same time. and done3 some time later

I’ll start with your point 1. Was this a request done by many people? Because if not, I would say:

" Things were working just fine and nobody was confused. Can’t we just go back to the way it was working before? ".

If not, I would say that the following should be the absolute golden rule:

A timer works according to the time you specify. Not more, not less.

If you let a timer fire more than once per frame, it should count as multiple times. If you don’t want this, either work using the enterFrame event, or set your timer to more than “1000 milliseconds divided by 60, or 30”. People relying on timers set shorter than this time are asking for trouble either way, as game logic will not follow faster.

I would really not like Corona to take a sloppy approach to coding just because some people use the wrong coding approach. The right coding approach in 99% of use cases, to be blunt, is using enterFrame events and deltaTime to calculate increments. Relying on a timer with a 60fps frequency and thinking this will work better is poor understanding of the Corona runtime, in my opinion.

Am I making any sense here?

Sheesh. This brings back nightmares from my MacroMedia Director days, when my game logic ran at a different framerate from my “display” logic.

To be honest, this timer issue really brings up a lot of questions… For example:

  1. What happens when you call a function five times per frame, updating a sprites position?

  2. How granular can you go with the timer function, approximately? I know this will depend on device etc…, but still I’m curious.

  3. Also, why would anyone want to go down to a higher frequency than 30 or 60fps, if your display can’t keep up? Do people think things will go faster or smoother using a really short timer?

  4. In a similar vein, but even stronger: why the hell would you want to use a timer similar to the frame rate? If it’s similar, just use the frame rate!

It really boggles the mind to me. Can’t think of any good reason to use supershort timers, that can’t be better supported with a game loop tied to the frame rate.

We try not to break backwards compatibility. We didn’t have a public release in a while, and it was behaviour for previous dozens of public releases. Many people use only public releases, and even without that we got several complaints that behaviour changed. Breaking existing games because something is “more right” is very bad approach to making public APIs, we try to help our developers support existing games, not make them search for changes we introduced.

I, personally, do not understand point in timers recurring more than once per frame, I don’t think this is a good pattern to follow due to performance impact. Calculations which happen more than once per frame are inherently wasteful, since their result can not be displayed to user. Hence, default behaviour is not to allow timers to work like that. But, if you really want timer which fire more than once per frame, you can now opt-in.

I don’t really see right/wrong division as you show it. There’s code which was working for literally a decade, that makes it right im my view, and change which allowed timers to fire more than once per frame was a breaking change.

Okay, I found a situation where a supershort timer would be used: a bonus score counter. But that’s not a situation where the accuracy or trimming to start of frame would be an issue.

So I’ll rephrase: I can’t think of any good reason to use supershort timers, where the trimming to beginning of frame would be an issue. If you supershort timers are really used for syncing things, you’re doing it the wrong way in my opinion.

[member=‘thomas6’], what are your suggestions? I don’t see value in short recurring timers too, but it doesn’t meant other people don’t see it as well. We’re not all coding superstars here, and some problems may be easier solved with short timers, than lots of coding. It may be suboptimal from performance point of view, but easier to make and less error-prone. For example situation when your timer period is not hardcoded, but result of something else. Like you get a coin every N, and N can change, like player can upgrade something, etc. It is not best way to implement it with timer, but it is certainly easiest, and easiest often meas less bugs.

Also, please, let’s keep civil tone. About your questions - you can get answers to all of them trying previous daily build. Answers are pretty straightforward. Timer is not a magic box, they are called consecutively, and really simple Lua code I linked before.

I don’t see harm in making short timers behaviour opt-in. What do you suggest?

I’m getting even more confused, Vlad.

Didn’t you / Corona just break backwards compatibility to make things work “more right”, with the latest changes to how timers work?

Hi Vlad,

I’ll let other chime in here, since I have to be honest: I had not noticed any difference in my own projects.

That being said, my suggestion is twofold:

  1. In general, things should happen in the most logical, intuitive and predictable way. That means, if you set a timer to run a specific function 100 times per second, that function should run a 100 times per second. I would go for temporal accuracy, since the thing is called a timer, after all, meaning that it is not trimmed to the start of a frame. I take it that when you trim to start of frame, and a function was called 5 times since the last frame update, this function would then run 5 times at the start of the frame? This would mean a larger single performance hit each frame, instead of 5 smaller ones during the duration of the frame, which does not seem positive for performance reasons, and seems to be an argument against trimming to frame start.

  2. Whenever possible, making changes in how the Runtime works, allow for backwards compatibility in some way, like your opt-in suggestion.

For the record, and after that I’ll really leave this thread to others, I do code using the enterFrame event for practically everything I do out of personal preference, and then evaluate the event.time parameter to decide what should happen in the new frame.

Your example of “Like you get a coin every N, and N can change” would then be implemented by checking each frame, if the time passed between the last coin and the new frame time is bigger then N, and if so, spawn a new coin.

I have a very strong feeling you know exactly what I’m trying to explain, Vlad, so I’m not posting this to lecture or prove myself right, but rather as a pointer for people looking for how to code stuff like this. Peace out! :slight_smile:

I see there’s some confusion. Let me summarize:

Default behaviour of previous Public Release 3326 and next daily build with suggested changes would be same, except timers would be a little bit more precise *. Timers would not fire more than once per frame as before.

There would be new ability to use timer.allowIterationsWithinFrame = true, and get behaviour when timers would fire more than once per frame.

*) we had a “rounding error” in timers, and all of this is really a consequence of trying to fix it.

Sounds good! Thanks!

@vlads

Thanks for the additional details about the history of the changes.

Using the flag solves the problem in the best possible way. I actually think that the correct way to handle timers is the September change. Missing timers due to longer frametimes is just the same as tunneling in collisions, it’s a bug and cropping time is the same just in the other direction.

Firing just once a frame as well as cropping the time after the timer fired was an arbitrary decision no different from firing only ever 3 frames or at least once a frame and nothing a framework should do/decide.

But given it was the behaviour for years there’s also no way to expect all devs to update their code and data. Knowing what happend, I’m actually surprised there wasn’t a raging mob around the forums back then :slight_smile:

So, just for clarification:  if set to true, you’d still get the buggy version’s ~16 callbacks next frame?  (assuming 1ms interval at 60fps)

I can’t see any reasonable use for that.  Perhaps an alternate way of addressing it might be to add an additional property of the event indicating how many “implied” cycles were skipped.  So if someone thought they needed 16 callbacks just to add 16 to a counter, they could instead do:

myCounter = myCounter + event.impliedCyclesSkipped -- or whatever the property name, who cares

If chose to do that, then just make sure it’s clear whether the cycle count is of only those truly skipped (ie, does NOT include the current actual callback) or of how many cycles elapsed since last (ie, DOES include the current actual callback), just a potential “off-by-one” issue.  (my pref would be a count of only those that were truly skipped, so that it’d be normally zero for all intervals > frame time)

fwiw

This seems to be a pretty interesting topic :slight_smile:

Adding up a simple counter is obviously a stupid usage, roughly as stupid as setting up a 1ms timer expecting it to fire exactly once every frame, relying on an implementation detail/sideeffect, when there’s an enterFrame event existing just (and only) for this exact use case.

I honestly can’t see any reasonable reason to not fire 16 timers if I setup a timer to fire every 1ms and since last frame 16ms have passed because that’s just exactly what I requested. When I create 100 rects I also expect to get 100 rects and not 17 because for whatever reason someone thought 17 would be enough.

The counter of course is a decent option and optimization.

davebollinger - getting multiple events in same frame is opt-in behaviour. By default you get one call per frame, as I wrote in summary above.

There was also hypothetical use for such construction.

And we will not skip iterations without reducing counter. That’s just bonkers. If you asked for 20 iterations, you’ll get 20 iterations. Flat would only change if they would be in same frame or 20 frames.