Open Bug 2000047 Opened 9 months ago Updated 9 months ago

Optimize no-op 'height'/'width' css animations, since view transitions might generate them

Categories

(Core :: CSS Transitions and Animations, enhancement)

enhancement

Tracking

()

People

(Reporter: dholbert, Unassigned)

References

(Depends on 1 open bug)

Details

(Keywords: perf:responsiveness, power)

Attachments

(2 files)

Bramus from Chrome DevRel describes an optimization that Chrome's got on Canary right now:
See https://www.bram.us/2025/11/13/animating-css-width-or-height-no-longer-force-a-main-thread-animation-in-chrome-under-the-right-conditions/

Essentially:

  • width and height animations run on the main thread.
  • View transitions generate animations for width, height, and transform
  • In some cases, the width and height values don't actually change at all, which means the view-transition animations aren't really doing anything for those properties.
  • So: if we can detect and specially-handle those, it'd be great to optimize them away entirely (or consider them to be handled by the compositor).

This would help us avoid needlessly running animation callback code at 60FPS on the main thread, for what is actually a no-op animation.

Attached file testcase 1

Here's a testcase which has a no-op animation for css width and height.

This is an easy case where width and height are explicitly specified up-front as well as in the keyframes.
(Bramus's blog post mentions that they also handle cases where the underlying width and/or height are auto but still don't actually change in terms of the resolved CSS pixel value, and other edge cases.)

STR for this testcase:
(1) Load the testcase.
(2) Capture a profile.
(3) In the profile, look for continuous activity on the content process's main thread.

ACTUAL RESULTS:
Refresh driver ticks every 16ms or so.

EXPECTED RESULTS:
No refresh driver ticks.

Here's a reference case where I'm just animating 'opacity'. This gives EXPECTED RESULTS.

Here's a profile of testcase 1: https://share.firefox.dev/4818Cyv
Here's a profile of reference case 1: https://share.firefox.dev/4qPMdg2

(Notice the main-thread activity for testcase 1 shown by markers in the track at the top, vs. the lack of main-thread activity for testcase 2.)

I'm not sure how much of a user-facing perf impact this would actually have, since no-op width/height animations should be relatively cheap; I guess the wakeups themselves just keep the CPU briefly-but-frequently active. So presumably there are some power savings to be had from keeping the main-thread quiet here. --> Adding power keyword.

Keywords: power

(In reply to Daniel Holbert [:dholbert] from comment #4)

I'm not sure how much of a user-facing perf impact this would actually have, since no-op width/height animations should be relatively cheap

Ah, Bramus clarified on Twitter/X that the main benefit here isn't about reducing the cost of the animation, but rather to keep the animation looking smooth/responsive even in the presence of unrelated random jank from other stuff that's happening at the same time.

In some cases, those values don't actually change at all, which means the view-transition animations aren't really doing anything.

Note that these generated keyframes still do something when the width/height don’t change, namely moving the pseudo across the screen from its old to its new position. This is done using a transform though, so it’s all good :)

I'm not sure how much of a user-facing perf impact this would actually have, since no-op width/height animations should be relatively cheap;

The most visual outcome is that the animations are no longer subject to jank. E.g. https://cdpn.io/pen/debug/MYgNVay keeps running buttery smooth in Chrome 144+ while the Main Thread gets (artificially) blocked.

(In reply to Bramus from comment #6)

Note that these generated keyframes still do something when the width/height don’t change, namely moving the pseudo across the screen from its old to its new position. This is done using a transform though, so it’s all good :)

Right, gotcha. I clarified comment 0 to make that clearer. Thanks!

(Note that we historically had some special-case code for synchronizing animations of transform with associated animations of properties like width and height; looks like we synchronized them by shifting all the animations to the main thread, but we removed that in bug 1540906. --> Adding that bug as see-also, since I suspect this bug here would involve some of the same sorts of reasoning and might benefit from things that we learned in that exploration.)

See Also: → 1540906

(In reply to Bramus from comment #6)

The most visual outcome is that the animations are no longer subject to jank. E.g. https://cdpn.io/pen/debug/MYgNVay keeps running buttery smooth in Chrome 144+ while the Main Thread gets (artificially) blocked.

(FWIW: in Firefox, when random-synthetic-jank happens in this^ codepen testcase, the animation seems to immediately snap to the end [right away, when the jank starts]; whereas in Chrome release, the animation pauses for the duration of the jank [which is the thing that the optimization seeks to address] and then snaps to the end [or wherever it should be]).

I wonder if Firefox's immediately-snap-to-the-end behavior is related to the outcome we ended up with in bug 1540906, or something else...

You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: