2019.3468 - Timer fixes are terrible and unnecessary.

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.

@Michael Flad - don’t shoot the messenger.  (besides I don’t use timers, so I don’t have any personal “stake” in it)

The routine has always “lied” to you, in the sense that if you asked for a 1ms timer, but it can only fire per-frame, then something HAS to give – so, whether you get one callback per frame or 16.67 queued up and fired sequentially, both are technically “wrong”.

>>because that’s just exactly what I requested

Ah, but it isn’t what you requested, not exactly - you requested that it fire every 1ms, and clearly it cannot since it’s frame-based.  The only remaining question is what it should do on that next frame to attempt to “apologize” for that quantization of time:

  - callback once and just ignore the missed intervals? (historical behavior)

  - callback 16 times “right now” to make-up for missed intervals? (temporary regression behavior, new opt-in behavior)

  - callback once and just “indicate” the number of missed intervals?

  - other?

On several occasions I’ve remarked in the forum that “timer.performWithDelay” is better understood if imagined to be named “timer.performOnNextFrameAfterAtLeastThisMuchDelay” to better match its actual behavior (or, at least, it matched at the time of writing, recent changes notwithstanding).  Except that no-one wants to type all that, of course!

IMO, the long-standing pre-3326 behavior of timer seemed fine if you just “understood” what it was actually doing, and didn’t expect it to act like a real-time asynchronous thread.

Hehe wasn’t meant to be a shot :slight_smile:

And I don’t use timers for anything controlling the logic of my games either as I prefer it to be as deterministic as possible and get the same results in each run (which also means, I tend to not use dt based logic if I don’t need to), IMO the only way to seriously be able to debug code - so no stake on my side either. I just noticed the posting and thought, especially before @vlads post about the history of changes, it’s a pretty serious change that might break lots lo existing logic as many users seem to use timers a lot, including to control their game logic.

Of course, no timer will ever be able to satisfy a generic request as it would need to support an infinite small frametime.

You also have a point with the way you see how the timer should work, there’s probably no way to make it perfect for everyone.

What I don’t agree with is the pre September version throwing away the remaining time after a repeating timer fired. It means that a repeating timer defined to trigger every 1.1x frames triggers ever second frame when it actually should only skip ever 10th - that’s a very different result.

In the end I think the very best option would be to add some flags to the options table when requesting a timer, one to either get a count of or actually send missed events and one to throw away or keep the remaining time. Using defaults to match the pre September version as I guess it’s still what most existing code is relying on.

Can someone give a definitive answer to the following?

Do timers in Corona have the ability to run their functions “sort of” when they are called, independent of the display frame rate? (so, multiple times, inbetween frames, at intervals).

Or is all timer and other logic (like touch events) handled per frame, each time a new frame starts?

It might be a silly question but I don’t know if anyone knows this all for sure.

It all happens on a per frame basis, everything else is really hard and for actually no real benefit - you’d see any change after the frame anyway and if you want your simulation to be finer grained than a single frame you’re better in handling this on your on at a very defined point in time rather than code that might interrupt your own code at will.

If anything, my suggestion above with repeated timer events getting a field with the actual time they were meant to fire in a perfect environment (with infinite short frametimes), would help to f.i. calculate inbetween frame progress/movement etc. related to your ideal timer.

But once you really need this, in general, it’s much better to handle all of it on your own, it’s very simple if you need timers for a very specific reason rather than writing a general purpose solution meant to support everything us developer may come up with.

Hi Michael, thanks for the clear info!

And thanks for the tips, although I have to say I was already in full agreement. I am a 100% enterFrame gameLoop and deltaTime coder, so I have zero need for accurate timers. Woohoo!

fwiw, sample “diagnostics” if you really want to know what’s going on at a “pseudo-subframe” level w new opt-in:

timer.allowIterationsWithinFrame = true print("BEFORE FIRST TIMER FRAME, current real time :", system.getTimer()) timer.performWithDelay(1, function(event) local now = system.getTimer() print("TIMER CALLBACK:") print(" event time (aka timer's start-of-frame time) :", event.time) print(" callback execution real time (aka 'right now'):", now) print(" real-time vs event-time delta (aka 'overhead'):", now-event.time) print(" event.source time (aka scheduled time) :", event.source.\_time) print(" real-time vs event-source-time (aka 'overdue'):", now-event.source.\_time) end, 10)

Hey guys. So, latest build contains my fixes. Now there’s timer timer.allowIterationsWithinFrame, defaulted to false. If put to true, you’ll get all the events as soon as possible. Default behaviour is to separate them by frame enter.

Both modes correct old behaviour of cropping timer to frame time.

While the display may only update at 60fps, I am hoping that calling audio.play will play sound sooner than the next upcoming frame.

This will finally enable accurate rhythmic triggering of sounds.

DEVS - So - if I call audio.play during a timer-triggered event, before the next upcoming frame, will audio.play truly play the sound at any desired number of milliseconds before the next enterframe event? If so, I’ll be VERY happy!

Gary

Hi Gary,

I think the topic of accurate audio timing has been beaten to death on this forum - several times I might add!

The documentation is pretty clear on this: audio works through OpenAL and works on a “best effort” basis, meaning you have very little chances of getting supertight timing.

Making timers, frames and other parts of code tighter and more accurate would not help out a lot, since all that would do is place the “play this sound”-command in a sort of buffer at an accurate time. The problem is the buffer of audio commands is read out at a “as soon as we have the processor time and resources” pace.

More in-depth information here:

https://developer.coronalabs.com/partner/audionotes

For what it’s worth: I think I have followed ALL the threads on tight audio, without success. I have a history as an audio producer/engineer of sorts, and have tried to no end to create some sort of sequencer in Corona. I’ve tried every trick in the book, including having 16 variations of each sample, each with 1,2,3 etc… milliseconds of silent spacing in front of the sample. My personal opinion: nothing works (well), and it is absolutely impossible to get timing to sound supertight.