WebKit Bugzilla
New
Browse
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
228833
REGRESSION (
r268932
): CPU usage higher than expected with sibling elements running WebAnimations
https://bugs.webkit.org/show_bug.cgi?id=228833
Summary
REGRESSION (r268932): CPU usage higher than expected with sibling elements ru...
Liam DeBeasi
Reported
2021-08-05 09:33:27 PDT
Created
attachment 434994
[details]
Code reproduction When using Web Animations, WebKit continually invalidates + recalculates style and paints even when using only transform or opacity. I can reproduce this issue in iOS 14.5+ and cannot reproduce this issue in iOS 14.4 and older which is why I think this is a performance regression. Steps to reproduce: 1. Open code reproduction on an iPhone running iOS 14.5 or newer. You should see two blue circles translating up the page. This animation will run continuously. 2. Open Dev Tools and take a Timeline recording. You should notice that WebKit is continually painting and invalidating/recalculating styles. You should also notice that both the CPU usage and energy impact are moderate to high. 4. Repeat steps 1-2 on a device running iOS 14.4. You should notice that WebKit does not continually paint and invalidate/recalculate styles. You should also notice that both the CPU usage and energy impact are low. Expected Behavior: I would expect that WebKit does not paint and invalidate/recalculate styles on a running animation that can be hardware accelerated. Actual Behavior: WebKit paints and invalidates/recalculates styles on a running animation that can be hardware accelerated. Other Information: - This has significant performance implications for any website making use of animations in Safari/WebKit as the main thread is constantly performing work. Additionally, this increases the strain on the battery resulting in the battery getting drained faster. - This does not reproduce in Chrome or Firefox.
Attachments
Code reproduction
(1.04 KB, text/html)
2021-08-05 09:33 PDT
,
Liam DeBeasi
no flags
Details
iOS 14.5 timeline
(499.35 KB, text/plain)
2021-08-05 09:40 PDT
,
Liam DeBeasi
no flags
Details
iOS 14.4 Timeline
(18.03 KB, text/plain)
2021-08-05 09:40 PDT
,
Liam DeBeasi
no flags
Details
Patch
(5.07 KB, patch)
2021-11-03 12:41 PDT
,
Antoine Quint
koivisto
: review+
ews-feeder
: commit-queue-
Details
Formatted Diff
Diff
View All
Add attachment
proposed patch, testcase, etc.
Liam DeBeasi
Comment 1
2021-08-05 09:40:16 PDT
Created
attachment 434995
[details]
iOS 14.5 timeline Added Safari Timeline for code reproduction on iOS 14.5
Liam DeBeasi
Comment 2
2021-08-05 09:40:52 PDT
Created
attachment 434996
[details]
iOS 14.4 Timeline Added Safari Timeline for code reproduction on iOS 14.4
Simon Fraser (smfr)
Comment 3
2021-08-05 10:28:18 PDT
I can confirm. Also on macOS, inspector shows "composite" every frame on iOS and macOS
Radar WebKit Bug Importer
Comment 4
2021-08-05 10:29:32 PDT
<
rdar://problem/81573075
>
Simon Fraser (smfr)
Comment 5
2021-08-05 11:06:30 PDT
Regressed at
https://trac.webkit.org/changeset/268932/webkit
Simon Fraser (smfr)
Comment 6
2021-08-05 11:29:41 PDT
I think this happens because we get to the end of a rendering update, but haven't cleared the Animations bit: Page 0x3bc5f9680 renderingUpdateCompleted() - steps [] unfulfilled steps [Animations]
Simon Fraser (smfr)
Comment 7
2021-08-05 11:36:56 PDT
The problem is that the layoutIfNeeded() at the end of a rendering update dirties the Animations and LayerFlush bits, so we trigger rendering updates on every frame. te(this=0x000000018c5f9680, requestedSteps={ size = 1 }) at Page.cpp:1469:9 frame #1: 0x00000001672d8df5 WebCore`WebCore::RenderLayerCompositor::scheduleRenderingUpdate(this=0x000000018c3e9720) at RenderLayerCompositor.cpp:574:12 frame #2: 0x00000001672d4249 WebCore`WebCore::RenderLayerCompositor::notifyFlushRequired(this=0x000000018c3e9720, (null)=0x000000018c3ef780) at RenderLayerCompositor.cpp:567:5 frame #3: 0x00000001672d421f WebCore`WebCore::RenderLayerBacking::notifyFlushRequired(this=0x000000018c34d870, layer=0x000000018c3ef780) at RenderLayerBacking.cpp:3747:18 frame #4: 0x0000000166ccfea9 WebCore`WebCore::GraphicsLayerCA::noteLayerPropertyChanged(this=0x000000018c3ef780, flags=16, scheduleFlush=ScheduleFlush) at GraphicsLayerCA.cpp:4709:22 frame #5: 0x0000000166cd13e9 WebCore`WebCore::GraphicsLayerCA::setTransform(this=0x000000018c3ef780, t=0x00007ffee5eeab80) at GraphicsLayerCA.cpp:675:5 frame #6: 0x00000001672c2937 WebCore`WebCore::RenderLayerBacking::updateTransform(this=0x000000018c34d870, style=0x000000018cecf7b8) at RenderLayerBacking.cpp:647:26 frame #7: 0x00000001672c7e3c WebCore`WebCore::RenderLayerBacking::updateGeometry(this=0x000000018c34d870, compositedAncestor=0x000000018c3e9850) at RenderLayerBacking.cpp:1278:5 frame #8: 0x00000001672db448 WebCore`WebCore::RenderLayerCompositor::updateBackingAndHierarchy(this=0x000000018c3e9720, layer=0x000000018c3e9d10, childLayersOfEnclosingLayer={ size = 0, capacity = 0 }, traversalState=0x00007ffee5eebb58, scrollingTreeState=0x00007ffee5eebb78, updateLevel={ size = 0 }) at RenderLayerCompositor.cpp:1341:27 frame #9: 0x00000001672db9a0 WebCore`WebCore::RenderLayerCompositor::updateBackingAndHierarchy(this=0x000000018c3e9720, layer=0x000000018c3e9be0, childLayersOfEnclosingLayer={ size = 0, capacity = 0 }, traversalState=0x00007ffee5eebda8, scrollingTreeState=0x00007ffee5eebdc8, updateLevel={ size = 0 }) at RenderLayerCompositor.cpp:1406:13 frame #10: 0x00000001672db9a0 WebCore`WebCore::RenderLayerCompositor::updateBackingAndHierarchy(this=0x000000018c3e9720, layer=0x000000018c3e9850, childLayersOfEnclosingLayer={ size = 0, capacity = 0 }, traversalState=0x00007ffee5eebf50, scrollingTreeState=0x00007ffee5eebf98, updateLevel={ size = 0 }) at RenderLayerCompositor.cpp:1406:13 frame #11: 0x00000001672d88fa WebCore`WebCore::RenderLayerCompositor::updateCompositingLayers(this=0x000000018c3e9720, updateType=AfterStyleChange, updateRoot=0x000000018c3e9850) at RenderLayerCompositor.cpp:916:9 frame #12: 0x00000001672d7a7b WebCore`WebCore::RenderLayerCompositor::didRecalcStyleWithNoPendingLayout(this=0x000000018c3e9720) at RenderLayerCompositor.cpp:549:12 frame #13: 0x000000016684d937 WebCore`WebCore::FrameView::updateCompositingLayersAfterStyleChange(this=0x000000018cecc010) at FrameView.cpp:810:39 frame #14: 0x0000000165a3a6d9 WebCore`WebCore::Document::resolveStyle(this={ origin = file://, url = file:///Volumes/Data/Development/system/webkit/testcontent/animations/animation-api-two-fixed.html, inMainFrame = Detached, backForwardCacheState = NotInBackForwardCache }, type=Normal) at Document.cpp:2088:46 frame #15: 0x0000000165a3b3c0 WebCore`WebCore::Document::updateStyleIfNeeded(this={ origin = file://, url = file:///Volumes/Data/Development/system/webkit/testcontent/animations/animation-api-two-fixed.html, inMainFrame = Detached, backForwardCacheState = NotInBackForwardCache }) at Document.cpp:2176:5 frame #16: 0x000000016683d287 WebCore`WebCore::FrameView::updateLayoutAndStyleIfNeededRecursive(this=0x000000018cecc010) at FrameView.cpp:4445:43 frame #17: 0x00000001668b72ae WebCore`WebCore::Page::layoutIfNeeded(this=0x000000018c5f9680) at Page.cpp:1463:15 * frame #18: 0x00000001668b7ba5 WebCore`WebCore::Page::updateRendering(this=0x000000018c5f9680) at Page.cpp:1577:5 * frame #0: 0x00000001668b73dc WebCore`WebCore::Page::scheduleRenderingUpdate(this=0x000000018c5f9680, requestedSteps={ size = 1 }) at Page.cpp:1469:9 frame #1: 0x0000000165364be7 WebCore`WebCore::DocumentTimeline::scheduleAnimationResolution(this=0x000000018c37fae0) at DocumentTimeline.cpp:170:25 frame #2: 0x0000000165368c15 WebCore`WebCore::DocumentTimeline::animationAcceleratedRunningStateDidChange(this=0x000000018c37fae0, animation=0x000000018cecf660) at DocumentTimeline.cpp:436:9 frame #3: 0x0000000165376249 WebCore`WebCore::WebAnimation::acceleratedStateDidChange(this=0x000000018cecf660) at WebAnimation.cpp:1254:49 frame #4: 0x0000000165376183 WebCore`WebCore::KeyframeEffect::addPendingAcceleratedAction(this=0x000000018c363840, action=TransformChange) at KeyframeEffect.cpp:1658:18 frame #5: 0x00000001653763ee WebCore`WebCore::KeyframeEffect::transformRelatedPropertyDidChange(this=0x000000018c363840) at KeyframeEffect.cpp:1686:5 frame #6: 0x0000000165378ad2 WebCore`WebCore::KeyframeEffectStack::applyKeyframeEffects(this=0x0000000195ec49a0, targetStyle=0x0000000195ebd660, previousLastStyleChangeEventStyle=0x0000000195ebd5a0, parentElementStyle=0x000000018cecdc58) at KeyframeEffectStack.cpp:145:21 frame #7: 0x000000016762fe9c WebCore`WebCore::Styleable::applyKeyframeEffects(this=0x00007ffee5ee9d98, targetStyle=0x0000000195ebd660, previousLastStyleChangeEventStyle=0x0000000195ebd5a0, parentElementStyle=0x000000018cecdc58) const at Styleable.h:93:60 frame #8: 0x000000016762e9c0 WebCore`WebCore::Style::TreeResolver::createAnimatedElementUpdate(newStyle=WebCore::RenderStyle @ 0x0000000195ebd540, styleable=0x00007ffee5ee9d98, parentChange=None, parentStyle=0x000000018cecdc58, parentBoxStyle=0x000000018cecdc58) at StyleTreeResolver.cpp:355:37 frame #9: 0x000000016762e0aa WebCore`WebCore::Style::TreeResolver::resolveElement(this=0x00007ffee5eec7c0, element=0x000000018cecf3d0) at StyleTreeResolver.cpp:226:19 frame #10: 0x0000000167630835 WebCore`WebCore::Style::TreeResolver::resolveComposedTree(this=0x00007ffee5eec7c0) at StyleTreeResolver.cpp:556:35 frame #11: 0x0000000167631784 WebCore`WebCore::Style::TreeResolver::resolve(this=0x00007ffee5eec7c0) at StyleTreeResolver.cpp:619:5 frame #12: 0x0000000165a3a5d4 WebCore`WebCore::Document::resolveStyle(this={ origin = file://, url = file:///Volumes/Data/Development/system/webkit/testcontent/animations/animation-api-two-fixed.html, inMainFrame = Detached, backForwardCacheState = NotInBackForwardCache }, type=Normal) at Document.cpp:2071:37 frame #13: 0x0000000165a3b3c0 WebCore`WebCore::Document::updateStyleIfNeeded(this={ origin = file://, url = file:///Volumes/Data/Development/system/webkit/testcontent/animations/animation-api-two-fixed.html, inMainFrame = Detached, backForwardCacheState = NotInBackForwardCache }) at Document.cpp:2176:5 frame #14: 0x000000016683d287 WebCore`WebCore::FrameView::updateLayoutAndStyleIfNeededRecursive(this=0x000000018cecc010) at FrameView.cpp:4445:43 frame #15: 0x00000001668b72ae WebCore`WebCore::Page::layoutIfNeeded(this=0x000000018c5f9680) at Page.cpp:1463:15 frame #16: 0x00000001668b7ba5 WebCore`WebCore::Page::updateRendering(this=0x000000018c5f9680) at Page.cpp:1577:5
Simon Fraser (smfr)
Comment 8
2021-08-05 12:26:08 PDT
Interestingly, the bug goes away if I make a trivial change to the class attribute ("bubble ") or disable style sharing.
Simon Fraser (smfr)
Comment 9
2021-08-05 15:24:49 PDT
Liam, does this affect any live web sites that you know of?
Liam DeBeasi
Comment 10
2021-08-06 06:54:51 PDT
I discovered this issue while demoing Ionic's animation library benchmark website:
https://flamboyant-brattain-9c21f4.netlify.app/
(Select "Ionic" and a bubble count and click "Start Test") Other than the benchmark website, I am not aware of any other sites impacted by this.
Antoine Quint
Comment 11
2021-11-03 11:15:13 PDT
Style sharing is disabled for elements with CSS Transitions or CSS Animations in SharingResolver::canShareStyleWithElement(): if (style->transitions() || style->animations()) return false; This should likely check whether there are any keyframe effects for the candidate element.
Antoine Quint
Comment 12
2021-11-03 12:41:15 PDT
Created
attachment 443227
[details]
Patch
Antti Koivisto
Comment 13
2021-11-03 13:41:17 PDT
Comment on
attachment 443227
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=443227&action=review
> Source/WebCore/style/StyleSharingResolver.cpp:263 > - if (style->transitions() || style->animations()) > + if (candidateElement.hasKeyframeEffects(PseudoId::None))
Transitions are "keyframe effects" too?
Antoine Quint
Comment 14
2021-11-03 14:43:57 PDT
(In reply to Antti Koivisto from
comment #13
)
> Comment on
attachment 443227
[details]
> Patch > > View in context: >
https://bugs.webkit.org/attachment.cgi?id=443227&action=review
> > > Source/WebCore/style/StyleSharingResolver.cpp:263 > > - if (style->transitions() || style->animations()) > > + if (candidateElement.hasKeyframeEffects(PseudoId::None)) > > Transitions are "keyframe effects" too?
Yes.
Antoine Quint
Comment 15
2021-11-04 01:58:02 PDT
Committed
r285256
(
243867@main
): <
https://commits.webkit.org/243867@main
>
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug