Bug 216701 - WebKit incorrectly composites hundreds of elements when using position: relative
Summary: WebKit incorrectly composites hundreds of elements when using position: relative
Status: NEW
Alias: None
Product: WebKit
Classification: Unclassified
Component: Compositing (show other bugs)
Version: Safari Technology Preview
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Nobody
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2020-09-18 09:30 PDT by Liam DeBeasi
Modified: 2021-01-15 05:36 PST (History)
4 users (show)

See Also:


Attachments
Code Reproduction (1.71 KB, text/html)
2020-09-18 09:30 PDT, Liam DeBeasi
no flags Details
Better testcase (1.87 KB, text/html)
2020-09-22 15:05 PDT, Simon Fraser (smfr)
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Liam DeBeasi 2020-09-18 09:30:36 PDT
Created attachment 409137 [details]
Code Reproduction

When an element with a 3d transform is placed in the same scrollable container as elements with position: relative, the elements with position: relative are incorrectly composited.

The Layers pane in Safari claims this is due to the relative elements overlapping the transformed element, even when they are not. Removing position: relative from these elements resolves the issue. Additionally, using a 2d transform instead of a 3d transform resolves the issue as well.

This issue was observed in STP 113, but can be reproduced in Safari 13 as well. Additionally, this issue can be reproduced on both macOS and iOS.

Steps to reproduce:

1. Open the attached code reproduction in Safari.
2. Open dev tools and navigate to the "Layers" tab. The default layout has a div with a 3d transform and a list of divs with position: relative. You should notice that there are over 150 layers rendered.
3. Uncheck the "Use 3D Transform" checkbox. This will use a 2d transform instead. You should observe that there are only 2 layers now.
4. Recheck the "Use 3D Transform" checkbox.
5. Uncheck the "Use div with position: relative" checkbox. This will remove position: relative from the list items. You should observe that there are only 3 layers now.

When selecting a list element layer, Safari indicates that it was composited because it "overlaps other compositing elements". The element is overlapping the scrollable container which is composited; however, this additional compositing only happens when the element with a 3d transform is present. In this case, no list item overlaps the element with 3d transform.

There are 2 reasons why I think this is a bug:

1. This behavior does not happen in Chrome. Chrome never composites the list items.
2. Given a scrollable list, this issue significantly degrades scrolling performance. It is most noticeable on an iPad.
Comment 1 Radar WebKit Bug Importer 2020-09-21 11:48:35 PDT
<rdar://problem/69317469>
Comment 2 Simon Fraser (smfr) 2020-09-22 15:05:03 PDT
Created attachment 409404 [details]
Better testcase
Comment 3 Simon Fraser (smfr) 2020-09-22 15:07:10 PDT
The relative-positioned elements end up in layers because the scroller is not a CSS stacking context, so conceptually the relative elements render as siblings (not descendants). There's an optimization to avoid this (layer backing sharing) but the transformed element defeats it.

If you test with the transformed element in the middle of the list you should see no layers for relative elements that come before it.
Comment 4 Liam DeBeasi 2020-09-22 15:57:06 PDT
Ah ok that's interesting. Thanks for the additional test case -- that clarifies things a bit for me. Would this be another scenario where an optimization could be made by WebKit, or would you consider this to be expected behavior?
Comment 5 Simon Fraser (smfr) 2020-09-22 15:58:13 PDT
It's obviously valuable to optimize but hard to fix given webkit's current compositing architecture.
Comment 6 Liam DeBeasi 2020-09-22 16:04:30 PDT
That's fair. I can dance around the issue for now by using 2d transforms in affected components in Ionic Framework.
Comment 7 Simon Fraser (smfr) 2020-09-22 16:34:12 PDT
Making the scroller a stacking context (will-change: transform or position and z-index) should eliminate this entirely.

Maybe a z-index > 0 on the transformed item too.
Comment 8 Liam DeBeasi 2020-09-25 10:58:06 PDT
Adding will-change: transform to the scrolling element solved it for us. Thanks for the suggestion!
Comment 9 Liam DeBeasi 2020-11-11 06:13:51 PST
Wanted to add a note for anyone who stumbles upon this and uses will-change: transform as a workaround: Make sure you only target WebKit as using will-change: transform on a scrollable element will break any position: fixed elements in the scrollable element in Chromium browsers.
Comment 10 Liam DeBeasi 2020-11-11 06:22:55 PST
Actually it looks like this position: fixed behavior is correct according to https://bugs.chromium.org/p/chromium/issues/detail?id=413456. There is an open WebKit issue to address this behavior: https://bugs.webkit.org/show_bug.cgi?id=167600

will-change: transform on a scrollable element is probably not a good solution here, but I will look into other workarounds.
Comment 11 Simon Fraser (smfr) 2020-11-11 10:14:42 PST
Try "position: relative; z-index: 0" on the scroller.
Comment 12 Liam DeBeasi 2020-11-12 13:05:19 PST
That seems to work, thank you!