Long delay in first several requestAnimationFrame callbacks upon first run of an animation
Categories
(Core :: Layout, defect, P3)
Tracking
()
People
(Reporter: code, Unassigned)
Details
User Agent: Mozilla/5.0 (X11; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0
Steps to reproduce:
On FF for Windows or Linux (release or nightly), run a series of animation frames with a large number of DOM element positions changing upon each frame, each frame being rendered upon a callback from requestAnimationFrame().
A good example of code that reproduces the issue is here:
https://codepen.io/trajanmcgill/pen/OJPOZLx
Am happy to provide extra data if requested.
Actual results:
A significant lag occurs (roughly an entire second) at the beginning of the animation, with one or more long delays between the first several frames, and then the rest of it runs smoothly.
Worthy of note:
-The lag is not in the user function called by rAF. It is happening in between animation frames, while no JS code is running. That is, the time from start to end of the frame-drawing function is very short, but measuring from when that function releases the main thread back to when the function is called again to set up the next frame shows the delay is outside of user code. For some reason, in between a couple of the very early frames, requestAnimationFrame() is taking a really long time to do its callback.
-The lag occurs only between the first few frames, and then the between-frame time settles down, and every other frame runs completely as expected.
-This only occurs on the very first run of the animation after reloading the page. Subsequent runs of the exact same animation work just fine, until reloading the page.
Expected results:
More or less smooth animation, as seen in Chrome and Edge. Or as seen in FF after the very first run of the animation.
Comment 1•6 years ago
|
||
Hi,
Thanks for the details. I was able to reproduce the bug on Ubuntu 16.04 on the following versions:
Release 71.0 (64-bit), Beta 72.0 (64-bit) and Nightly 74.0a1 (2020-01-07) (64-bit).
I wasn't able to reproduce this issue on Windows 10. I've also chosen a component, feel free to change it if there's a better one.
Best regards, Flor.
| Reporter | ||
Comment 2•6 years ago
|
||
Interesting. I tried in another place.
System 1:
- Bug appeared on Linux Mint 17.3: yes
- Bug appeared (and still appears) on Windows 10 build 1903, FF 71 and FF 72: yes
System 2:
- Bug appears on Windows 10 build 1903, FF 72: no
I don't have a live CD handy or I'd try System 2 running Linux as well. The most obvious difference between these systems is that System 1 has dual laptop graphics cards, whereas System 2 only has Intel graphics. But that might be a false clue, because my NVidia utility is reporting that Firefox is not making any use of the secondary graphics card.
Flor, when you tested on Windows 10, was it using the same hardware you used for testing on Ubuntu, or a different machine?
Comment 3•6 years ago
|
||
It was a different machine. My windows 10 is on a laptop and ubuntu was on a desktop.
Doesn't look like a media issue, moving to layout in the hopes it gets to the right home. :heycam, do you know if that's the appropriate place for this?
| Reporter | ||
Comment 5•6 years ago
|
||
Here is an updated code example:
https://codepen.io/trajanmcgill/pen/GRgQyxW
This one logs all the frame start and end times and the time spent waiting for requestAnimationFrame() to call for the next frame.
What I am seeing pretty consistently is that the first call to requestAnimationFrame() does its callbacks quickly. The next few breaks between frames are all on the long side (compared to the ideal of 16ms), but there is a huge break-- like 3/4 of a second on my machine-- in between the second and third frames (that is, the third call to rAF takes a really long time to result in a new frame being called for). Then the entire rest of the animation runs pretty smoothly, with reasonable consistency in every frame break.
When re-running the animation, then, without reloading the page, it runs smoothly the whole time.
Reloading the page, however, causes the problem to come back on the first run of the animation like before.
Comment 6•6 years ago
|
||
Hey Trajan, can you please take a profile so that we can probably identify what the hot spot is and figure out what we can do there? You can install an add-on for that here. https://profiler.firefox.com/
Updated•6 years ago
|
| Reporter | ||
Comment 7•6 years ago
|
||
Thanks for following up.
Here's what I'm currently seeing, which I'm not sure how to explain, because now behavior has diverged between the Windows and Linux platforms. In any case, below are links for two profiler runs, one on Linux (where the problem persists) and one on Windows (where I'm not presently seeing it in the nightly build).
Page being profiled: https://codepen.io/trajanmcgill/pen/GRgQyxW
Run scope: Profile begins after clicking "Prepare Animation" button but before clicking "Run Animation", and lasting until after the animation finishes.
Problem occurs within the first few seconds, when the time after the end of one animation frame but before the next call to the requestAnimationFrame callback is a very long time (on the order of 3/4 of a second). This is always on the same inter-frame break (after the third call to requestAnimationFrame).
Linux Release: 71.0 (64-bit): BUG OCCURS
Linux Nightly: 73.x (64-bit): BUG OCCURS
Linux Nightly: 74.0a1 (2020-02-07) (64-bit): BUG OCCURS - profiler recording: https://perfht.ml/2S7Qj4U
Windows 10 Release: 72.0.2 (64-bit): BUG OCCURS (both regular startup and safe mode)
Windows 10 Nightly: 74.0a1 (2020-02-07) (64-bit): BUG DOES NOT OCCUR - profiler recording: https://perfht.ml/2SvCBrq
I'd be happy to generate any additional profiler recordings or supply other info if needed.
Updated•3 years ago
|
This is still an issue for me in Firefox 119 (64 bit) on Windows, and unlike Trajan, it is in the first call to the callback, not in subsequent frames. This fiddle illustrates the problem - press the Play button to launch the animation and see the console for the times:
https://jsfiddle.net/sidewayss/u7obcks6/4/
That code uses document.timeline.currentTime as equivalent to the requestAnimationFrame callback's timestamp, and it verifies that fact in the console. See the 3rd and 4th numbers in the console message. I am seeing delays that vary between 80ms and 124ms at 60hz. Chrome, oddly enough, has no delay whatsoever between the initial call to requestAnimationFrame and the call to the first callback, not even a fraction of a frame. If you run that fiddle on Chrome you will see what I mean, fwiw. I would assume a single-frame delay based on the fiddle's method of using document.timeline.currentFrame as the baseline time. These delay times are more like 5-8 frames.
Is this a bug or is it, four years later, now considered a feature?
This bare-bones fiddle illustrates without a doubt that only the first frame is delayed:
https://jsfiddle.net/sidewayss/rbt70q8z/
NOTE: The time between the first and second frames is less than a single frame @60hz, but the time between all subsequent frames is 16.67ms, at least rounded to two decimal places. Not only that, but from the second frame on, the total elapsed time divides by 16.67ms with no remainder, aligning to 60hz frame boundaries as defined by document.timeline.currentTime in the button click event handler. So:
- The first frame is delayed by some multi-frame, non-60hz amount
- The second frame duration is shortened to align with 60hz
- From there on, all the durations are even and aligned with 60hz
The original example for this bug is far from bare-bones, and it's entirely possible that the delays between later frames is due to the code in the callback taking longer than 16.67ms to execute.
| Reporter | ||
Comment 10•2 years ago
|
||
First, worth noting that it's unclear if we are dealing with really the same issue, several years on. I am not currently seeing the original version of the problem at all. I really don't think the original example had anything to do with callback code taking too long to execute, because 1) it was always exactly one callback interval that had the issue, between the third and fourth frames, even though all frames had to do the same amount of work; and 2) it was demonstrated that the time delay was happening outside of user code.
Arguably, a first-frame delay is better than a delay after the animation has begun, but it is still surprising to me that it should take multiple frames worth of time just to hit that callback the first time. Presumably it should be able to set up the first callback faster than that, but I don't see a bug here in frames after the first. There must be some kind of housekeeping happening that is delaying the first one, and then after that, since the callback is executing quickly enough, it really should be able to complete the repaint fast enough to land on even multiples of the refresh rate.
I am suspecting the "housekeeping" has something to do with actual repainting, or UI events, going on simultaneously with the setup of the animation callback, but I'm not sure what, because here is an oddity-- try your sample code and note what happens when instead of simply clicking the "play" button you hold the button down for a few seconds and then release it. For me, it makes that initial delay a whole lot longer.
Comment 11•2 years ago
|
||
Trajan, I agree that it is unclear that we are dealing with the same issue. Are you suggesting that I file a separate bug report? I was thinking of doing that, but it's always best to add to an existing issue, and this seems similar enough to warrant that as a first attempt. I was going to wait until someone at Mozilla responded here, but that has yet to happen.
If you intend to sync to external sources, the delay in the first frame can be a hassle, depending on how your code is structured. I saw that you have a github project for syncing to external sources, but didn't dig into it, so I'm not sure how you handle that. Regardless it's an odd behavior and if you're syncing to external sources it requires you to waste the first frame setting the element to its current value or ignoring the frame entirely. Not a complete disaster, but a hassle.
Interesting behavior holding down the mouse button on the Play button. The code would appear to be capturing the value of document.timeline.currentTime on mousedown, then calling requestAnimationFrame on mouseup. I can't think of another explanation off-hand. Bizarre indeed.
Comment 12•2 years ago
|
||
Maybe somehow the mousedown event is freezing document.timeline.currentTime, preventing it from updating until the click event handler runs?
Note that in Chrome holding down the mouse button makes no difference, no issue there. Chrome has the odd behavior of the first frame's timestamp often being greater than the original document.timeline.currentTime by a fraction of a millisecond. Running my fiddle in Chrome you will see negative values for the first frame, not always, but often. Off-topic here, but possibly the strangest behavior of all. I'll be posting something on monorail to at least see if I can find out what causes it, whether or not they consider it a bug. If you like I can cc you on that.
| Reporter | ||
Comment 13•2 years ago
|
||
I can't say with certainty whether it should be a separate bug report...I never seemed to get any traction on this one in the first place, beyond someone initially asking for me to profile the code running.
The tricky thing about syncing animations, especially with external components, is that you kind of always need to check what the current value will be against the external timing source, because you can't guarantee how much it will have moved forward in between your frames. Part of why I built that other project was because a lot of code out there, especially at that time, seemed to amount to "set two things in progress that run 'at the same rate' and trust that they will stay in sync." That doesn't work very well. If you really want something locked to an external source you need to continually check where you are and be able to skip ahead or slow down based on that. Especially if you are syncing to media, which could get paused, scrubbed, or stuck buffering for a second, or could just have some initial playback startup lag that knocks you out by a quarter of a second from the start and you never check sync again and recover.
Comment 14•2 years ago
|
||
My plan was to wait two weeks from my original comment and then submit a fresh bug. That's a week from today. It will probably get as much traction as this one, but it's worth a try. I'll refer to this bug in the new submission, so it should link up here. If not, I can cc you if you like.
My main experience with sync is the Web Audio API. All I need to do is synchronize the start time for the animation and the audio, assuming that both have fixed durations. If there are glitches that cause a frame to skip or some other animation anomaly it doesn't matter because the durations stay constant. The animation might blink, but it did that anyway, and it will sync back up in the next frame. Web Audio is a lot more precise than other media in terms of time. Sounds like you were dealing with more complex synchronization situations.
I did do some sync with HTML5 video alongside the Web Audio (muted video with Web Audio soundtrack), and there were never any issues, but I can see that if the video stream has a glitch it could get problematic. I hadn't run into the issue, so I didn't dig into it. So your code checks the current playback elapsed time for all the media sources every frame and finds a way to sync the animation? Does it sync multiple media sources too, in case just one of them glitches, or were you only dealing with one external media element at a time? Sorry, it's off-topic for this bug, but it interests me. Maybe I'll have to take a look at your code and contact you directly...
Thanks for your replies here.
| Reporter | ||
Comment 15•2 years ago
|
||
Yes, I suppose any other conversation aside from this bug itself should go elsewhere. Would be happy to discuss further if you like. Feel free to reach out at the email address on my GitHub profile or on the concertjs web site. In the meantime, the things found here are indeed kind of odd, and maybe if I get a little time I'll try to reproduce in particular what is going on with the button press thing and maybe get another bug report if you don't do so yourself (also feel free to cc me if you do report that, as I'm curious about it even though it doesn't affect anything I'm currently working on).
Comment 16•2 years ago
|
||
I created a new bug report for my issue here (referring to this bug):
https://bugzilla.mozilla.org/show_bug.cgi?id=1864047
It includes a new version of my fiddle that provides additional data on the "hold down the mouse on the Play button" behavior.
Description
•