CSS animation causing 60Hz restyles despite the current and next animation steps being identical
Categories
(Core :: CSS Transitions and Animations, defect)
Tracking
()
| Performance Impact | medium |
People
(Reporter: florian, Unassigned)
References
(Blocks 1 open bug, )
Details
(Keywords: perf:resource-use, power)
Attachments
(1 file)
|
741 bytes,
text/html
|
Details |
I noticed that https://www.maif.fr/particuliers/habitation/assurance-emprunteur/assurance-emprunteur.html was causing restyles at 60Hz all the time when it's the selected tab, despite nothing visibly changing on screen most of the time.
Using the devtools, I found the relevant animation (it's a link that bounces for a second or so every 15s) and made a minimized test case that I'm attaching.
Here is a profile of the minimized test case: https://share.firefox.dev/3BhvFDj
When this page is visible, the main thread of the relevant content process uses about 2% of a core continuously on my macbook pro.
Would it be possible to stop the refresh driver until the next animation step when the current and next steps are identical?
Comment 1•4 years ago
|
||
Hiro or Boris might be able to assess this a bit better than me, they probably have the relevant code paged-in better than me.
Doesn't seem impossible, though not sure how easy it is... Presumably animation iteration events etc should still fire?
Comment 2•4 years ago
|
||
We could probably optimize this case.
We have an optimization (skip restyling) that animation will be throttled if computed timing value hasn't been changed last time we did restyle. In this animation case, the timing function is a cubic bezier function, thus the computed values will be different all over the places. So the optimization doesn't work for this case. But in most places the computed timing values are over 1.0, thus the computed style values (i.e. top and box-shadow) will be clamped to top: 0px and box-shadow: 0 0px 0px rgba(0, 0, 0, 0). We will have to tell whether the animating style will be clamped for over 1.0 or under 0 computed timing value.
One caveat is we can't optimize if the animating element has another animation for same styles with composite: add or composite: accumulate. There may be other exceptions I am missing.
Comment 3•4 years ago
|
||
Hmm I was missing an important fact. The computed style value depends on the timing value and the specified value in each keyframe, thus we will have to compute the style in question anyway? that's not an optimization at all.
Comment 4•4 years ago
|
||
If boris doesn't have any ideas to optimize this case, I will close this as WONTFIX (I actually do want to mean as CANTFIX).
Comment 5•4 years ago
|
||
Unfortunately, I don't have a good idea to optimize this case for now. It seems the computation is necessary for each tick. Maybe we can suspend the animations by JS in some cases but there is no automatic way I can think of.
Comment 6•4 years ago
|
||
Okay, thank you Boris.
Florian, I am afraid I'd say we can't fix this. Note that we have another optimization for these kind of cases, nsChangeHint should work in this case to avoid frame reconstructions and building display items and so on.
Comment 7•4 years ago
|
||
(In reply to Hiroyuki Ikezoe (:hiro) from comment #2)
We have an optimization (skip restyling) that animation will be throttled if computed timing value hasn't been changed last time we did restyle. In this animation case, the timing function is a cubic bezier function, thus the computed values will be different all over the places. So the optimization doesn't work for this case. But in most places the computed timing values are over 1.0, thus the computed style values (i.e. top and box-shadow) will be clamped to
top: 0pxandbox-shadow: 0 0px 0px rgba(0, 0, 0, 0). We will have to tell whether the animating style will be clamped for over 1.0 or under 0 computed timing value.
Could you explain this part some more?
Is the "timing value" the "input progress value" or "output progress value" as used in css-animations-1?
In the animation in the example, yes there is clamping, but also the timing function shouldn't make any difference. For example, if the timing function were set to linear, I think Florian would expect the same optimization: The top and box-shadow values at the final keyframe (4%) are the same as at the end of the animation, so no matter what the timing function is, interpolating between those identical values won't change the computed value.
Comment 8•4 years ago
|
||
(In reply to Markus Stange [:mstange] from comment #7)
(In reply to Hiroyuki Ikezoe (:hiro) from comment #2)
We have an optimization (skip restyling) that animation will be throttled if computed timing value hasn't been changed last time we did restyle. In this animation case, the timing function is a cubic bezier function, thus the computed values will be different all over the places. So the optimization doesn't work for this case. But in most places the computed timing values are over 1.0, thus the computed style values (i.e. top and box-shadow) will be clamped to
top: 0pxandbox-shadow: 0 0px 0px rgba(0, 0, 0, 0). We will have to tell whether the animating style will be clamped for over 1.0 or under 0 computed timing value.Could you explain this part some more?
Is the "timing value" the "input progress value" or "output progress value" as used in css-animations-1?
I meant the output.
In the animation in the example, yes there is clamping, but also the timing function shouldn't make any difference. For example, if the timing function were set to linear, I think Florian would expect the same optimization: The
topandbox-shadowvalues at the final keyframe (4%) are the same as at the end of the animation, so no matter what the timing function is, interpolating between those identical values won't change the computed value.
Huh, the last keyframe would be the last _specified keyframe? Did I mis-remember keyframe parsing rule? I will have to re-read the spec.
Comment 9•4 years ago
•
|
||
Hmm I think I am correct. From the spec, the number 10;
Similarly, if there is no keyframe in property-specific keyframes with a computed keyframe offset of 1, create a new keyframe with a computed keyframe offset of 1, a property value set to the neutral value for composition, and a composite operation of add, and append it to the end of property-specific keyframes.
That said, I do often misunderstand English sentence, please correct me if I am missing something.
Comment 10•4 years ago
|
||
Oh, I may not have been very precise in my wording. And I didn't really know how the behavior between the last specified keyframe and the end of the animation was defined.
So, would it be correct to say that, between 4% and 100%, we interpolate from "replace-with box-shadow:0 2px 3px rgba(0,0,0,.5)" to "add box-shadow:0 0 0 0 rgba(0,0,0,0)"? And then the challenge would be to see that both outcomes are identical?
Comment 11•4 years ago
|
||
(In reply to Markus Stange [:mstange] from comment #10)
Oh, I may not have been very precise in my wording. And I didn't really know how the behavior between the last specified keyframe and the end of the animation was defined.
So, would it be correct to say that, between 4% and 100%, we interpolate from "replace-with box-shadow:0 2px 3px rgba(0,0,0,.5)" to "add box-shadow:0 0 0 0 rgba(0,0,0,0)"? And then the challenge would be to see that both outcomes are identical?
Yep, your understanding sounds correct me, but if there's static box-shadow property specified, the last value would be the specified one. So to tell the exact computed style value would be identical, I think we will end up computing the style with the timing value and the keyframe values. That sounds to me we are restyling it.
Comment 12•4 years ago
|
||
I see.
I think we will end up computing the style with the timing value and the keyframe values. That sounds to me we are restyling it.
The feature request here is: Compute a "wake-up" timestamp ahead of time, so that we can sleep until that timestamp is reached... and then tick the refresh driver. Concretely: When we're at the 4% keyframe, we want to detect that the next keyframe (the implicit 100% keyframe) will have the same computed values, so that we know that the computed style will not change between 4% and 100%. Then we want to pre-compute the timestamp at which we will hit the 100% keyframe, and use that as the "wake-up" timestamp.
If another style change happens during that sleep time, it will tick the refresh driver, and we can re-do the prediction and compute a new wake-up timestamp.
Comment 13•4 years ago
|
||
A hard part (I think it's an impossible part) is the timing function is cubic-bezier, it's hard to predect the timing value on each frame (computing the value only for the last frame is going to be meaningless), if the timing function is a linear function it can be predected, but linear function will not overflow/underflow.
A similar case we could optimize is something like this;
@keyframe anim {
0% { top: 0px; }
4% { top: 100px; }
100% { top: 100px; }
}
and the timing function between 4% and 100% isn't cubic bezier.
Comment 14•4 years ago
|
||
Sorry, just commenting from the sidelines here. It sounds like there are at least two components here:
- Detecting when the style values at endpoints of an interpolation interval are identical as they are in this case (but only because the underlying style happens to match the last specified keyframe). When that's the case, the keyframe easing is not going to make any difference since we will produce the same value for the entirety of the interval regardless of the easing. If the effect easing is not linear, however, this optimization would be difficult.
- Actually pausing the refresh driver for an interval of time where we've determined nothing is going to change. I believe dbaron had wanted to do this.
Optimizing this particular test case would require both of the above.
Note that we already have at least the following optimizations:
- A check that our position in our an interpolation interval hasn't changed (e.g. because we are using a step timing function). In this case we skip doing restyling but we don't pause the refresh driver. In some cases we might be able to detect this in advance and benefit from 2 above.
- The nsChangeHint handling that hiro mentioned that should avoid reconstructing frames etc. when the style we compute ends up being the same.
Comment 15•4 years ago
|
||
(In reply to Hiroyuki Ikezoe (:hiro) from comment #13)
A similar case we could optimize is something like this;
@keyframe anim {
0% { top: 0px; }
4% { top: 100px; }
100% { top: 100px; }
}and the timing function between 4% and 100% isn't cubic bezier.
I didn't follow this part. CSS animations only ever set keyframe easing and so even if there is a cubic bezier easing function between 4% and 100%, and even if it has overflow/underflow, it's never going to cause the output to be anything other than top: 100px.
i.e. when interpolating between top: 100px and top: 100px, even if the output progress value of the easing function is 1.5 as in the case of a cubic bezier that overshoots, the output is still top: 100px.
Comment 16•4 years ago
|
||
Oh right, you are absolutely right. Timing function things have often been confusing me.
| Reporter | ||
Comment 17•4 years ago
|
||
Reopening as based on the last few comments, it seems there's something actionable here.
Comment 18•4 years ago
|
||
Florian, can you elaborate what the actionable step in your mind is? Is it same as bug 715051?
Comment 19•4 years ago
|
||
(I am wondering how common the case in comment 8 is, and I can't think of any other cases we can optimize)
| Reporter | ||
Comment 20•4 years ago
|
||
(In reply to Hiroyuki Ikezoe (:hiro) from comment #18)
Florian, can you elaborate what the actionable step in your mind is?
Comment 14 sounded like there were 2 distinct things that could be done.
Is it same as bug 715051?
It does look similar, but I probably wouldn't dupe against a bug reported for "Gonk (Firefox OS)" that hasn't been touched in 10 years.
Comment 21•4 years ago
|
||
Okay, I filed bug 1727712 for the skipping restyles part. It can be relatively a low hanging fruit since it can be optimized upon the existing optimization frame work.
Updated•4 years ago
|
| Reporter | ||
Updated•4 years ago
|
Updated•4 years ago
|
Updated•3 years ago
|
Description
•