Layers and Compositing<!-- --> | <!-- -->Web Performance Tips

Layers and Compositing

Web developers typically aren't exposed to low-level browser rendering primitives, yet the concepts of Layers and Compositing are foundational to how the browser renders web applications as pixels on screen! In this tip, I'll explain what Layers are, why they exist, and how they are Composited together to produce on-screen Frames to the user's display.

Prerequisites

Layers

Layers are groups of visual elements that share the same coordinate plane. They are rendered onto a 2-dimensional mesh and retained in GPU memory. Below is an example of Layers (and we'll dive into detail later):

A recording of a simple scroll operation in the Layers View

All modern web browsers support Layers, yet the exact implementation details may differ and evolve as browsers are continually changing.

I encourage you to read the Layer source code in Chromium, found here!

Why do Layers exist?

Layers exist as an optimization. The browser will render (draw pixels for) multiple Layers and retain the results in GPU memory for subsequent use. When a Frame needs to be produced (i.e. scrolling, animating), it's faster to transform and compose multiple pre-rendered Layers rather than re-drawing the viewport on demand.

Furthermore, Layers can be visually updated independently by a separate thread, the Compositor Thread without competition from heavy JavaScript or other activity taking place on the Main Thread.

This is a tradeoff, where GPU memory is traded to optimize rendering performance, but generally the tradeoff pays significant dividends for end user experience.

When are Layers created?

The conditions for when a Layer is created will vary by browser implementation. Common examples where the browse will create Layers include:

  • Scrollable areas
  • Regions driven by CSS animations
  • Videos rendered through <video>
  • Canvas elements through <canvas>
  • Regions explicitly promoted by CSS via will-change: transform

In Chromium, here is the list of conditions that promote a group of visual elements to dedicated layer.

How do Layers operate during Runtime?

Consider the following diagram:

A diagram showing the Compositor Thread, Raster Threads, and Main Thread coordinating the steps described below.

Each browser operates differently, but at a high level, it works like this:

  1. At some cadence, the Browser's Main Thread will run the Rendering Steps of Style and Layout, and begin trying to visually represent the DOM and CSSOM
  2. The Render Tree is updated as a result with positioning and styling information
  3. The Paint phase runs. Despite its name, this doesn't draw anything on the screen but identifies graphical regions that need to be drawn and how to draw them. These instructions are transmitted to a separate thread, the Compositor Thread.
  4. On the Compositor Thread, visual regions of the Render Tree are grouped into Layers, forming a Layer Tree
  5. The Compositor thread coordinates with a dedicated GPU process and other Raster worker threads draw each Layer in the Layer Tree as textures / bitmaps in GPU memory (this is called Rasterization)
  6. The resulting Layers + pointers to the newly produced bitmaps are spliced together during the Composite phase to produce a Frame generation instruction
  7. The Frame generation instruction is sent to the GPU process and drawn onto the display

Below, I visualize some of the key data structures in this proces and their various transformations:

A visualization showing the various data structures ordered as described above.

Note: There is a lot of nuance and depth in this complex process! I highly recommend the most recent (and evolving!) Chromium rendering architecture overview, which can be found here.

How are Layers different from z-index?

z-index is used to signal stacking context to the browser, which signals the order used by the Paint phase to draw visual objects, (usually) within a single layer. For example, multiple visual elements with different z-index values will likely be stacked visually and drawn onto one Layer.

Layers aren't used to order visual elements on-screen. They are lower-level graphics primitive and reside closer to the actual GPU/Rastering pipeline than to CSS styling.

Examples

Animations

If authored correctly, CSS animations can be completely offloaded to the compositor thread, allowing a smooth, 60 FPS animation to take place despite any heavy activity on the main thread.

This is by processing the animation instructions completely on the compositor thread on the in-memory layer. I have a dedicated tip on this mechanism.

Scrolling

Scrolling can be processed entirely by transforming in-memory Layers!

The browser will process scroll events on the Compositor thread first, translating the in-memory graphical Layer, before letting the Main Thread execute any scroll JavaScript event handlers (thereby ensuring a smooth scroll, even if JavaScript were executing).

Expensive, Frequently Drawn Regions

While unusual, certain groups of visual elements may be expensive to draw / re-drawn depending on the scenario (like in this example with SVGs and OneDrive).

A web developer can explicitly signal to the browser that they'd like a set of visual elements grouped into a dedicated layer. This can be done with the following line of CSS:

will-change: transform

Exploring Layers at Runtime

Chrome Layers View

You can view Layers at runtime using the Chromium F12 DevTools!

It can be found here (it's hidden behind a few menus):

  1. Right-click, Inspect to open the DevTools
  2. Three dot menu in the top-right corner
  3. More Tools
  4. Layers

A screenshot of the Menus view in Chromium for the Layers tool.

Microsoft Edge 3D View

Microsoft Edge has a powerful 3D viewer. Details on this tool can be found here.

Should I optimize for Layers?

In general, I would suggest only optimizing for Layers when they become problematic. In most cases, if the HTML is semantic, the DOM is reasonably sized, and the CSS is well designed, the browser will automatically do the right thing for you; Layering issues likely won't become a performance bottleneck.

However, if during your regular profiling of your web application you identify degradations in Rendering phases or in Layering and Compositing, you now have the tools to understand what's going on behind the scenes!

That's all for this tip! Thanks for reading! Discover more similar tips matching Browser Internals.