Wednesday, 14 April 2021

Debugging scroll jank with chrome devtools. "Updated Layer Tree"

On android, scroll is very janky in the chat feed of a react application I am building. Upon inspecting the devtools in the performance panel I can see a significant amount of time is spent rendering

enter image description here

I've tried reading through a number of resources

And I've tried a bunch of things, none of which had a significant impact.

  • I've disabled any :hover selectors.
  • No classes are changing during scroll, I've verified this with a mutation observer on the parent element with subtree: true[0].
  • There are no mouseEnter or mouseLeave event listeners on the elements in the list.
  • I'm not reading or writing offsetHeight, scrollTop or any other values on this list during scroll. I'm using InersectionObserver where applicable.
  • contain: layout on each list item, contain: layout on the parent.
  • Manually removing the global react scroll event listener.
  • Removed all images from being rendered in the list.
  • Enabling "Layout shift regions", "Layer borders".
  • Applying will-change: transform; to the parent

The only thing I managed to do which had a significant impact was changing the fundamental layout from:

html,
body {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  left: 0;
  overflow: hidden;
}
.scroll-parent {
  height: 100vh;
  overflow-y: scroll;
  display: flex;
  flex-direction: column-reverse;
}

to

html,
body {}
.scroll-parent {}

The latter still produced regular "Updated Layer Tree" events but they didn't add up to a significant time and more time was spent in the "Paint" section.

Enabling "Rendering > Layer borders" revealed that there were two overlapping "sets" of tiles in the former css layout.

The issue is that I need the former overflow:hidden; on the body to avoid the toolbar from auto hiding on ios safari. I also don't think that the performance should be this bad just because I have a div which has some inner scroll. AFIK scrolling should not cause the layer tree to update, it should only cause compositing? I could also resort to virtualized scrolling but I've scrolled down much longer pages with no jank so I think this would just be another workaround rather than addressing a core limitation.

Digging into the devtools I can see that the main culprit seems to be "Updated layer tree" but I'm not really sure where to go from there.

enter image description here

I had a go with chrome://tracing but I can't really say I know what I'm looking at

enter image description here enter image description here

I've been working on a minimal reproducible example. This is as close as I could get https://codesandbox.io/s/test-layout-reflow-on-scroll-u1kjr?file=/src/App.tsx There are observable "Updated Layer Tree" halmarks when profiling the replica but the performance is not nearly as bad as in the real app. I put it down to the CSS complexity being much lower so the "Updated Layer Tree" runs much quicker in the replica, but I probably shouldn't be making any assumptions.

  • What are some steps I can do to better profile what might be the root cause?
  • Is it normal to have so many "Updated Layer Tree" events during a scroll?

[0]

observer.observe(element, { subtree: true, childList: true, attributes: true, characterData: true })


from Debugging scroll jank with chrome devtools. "Updated Layer Tree"

No comments:

Post a Comment