The will-animate CSS Property

Update: will-animate has been renamed to will-change.

Benoit Girard has been hard at work on adding a 'will-animate' CSS property to gecko in bug 940842. Will-animate hints to the browser that some section of the webpage will animate, and thus the browser can take advantage of that knowledge and optimize the web page's smoothness. What exactly does that mean and why do we need it? There's a lot of background information required to understand what's going on in the gecko browser, so the tl;dr - A replacement for 'translateZ(0px);' that will optimize the animation you need rather than optimizing a translation.

Will-animate is a high level hint that the browsers can use to trigger optimizations for some portions of a page. For example, it can be used tell the browser that some elements will animate on a scroll or animate in general. Currently, browsers have to use heuristics to determine which elements will potentially animate, which may not always be most optimal. At the moment, it's only a high level hint and isn't a guarantee that the browser will optimize the case. It's going to be up to each browser vendor to decide how to use the property. For now, there's a prototype implementation in Gecko, so let's see what gets optimized in Gecko.

A browser does a ton of work to display a website. The software pipeline from your HTML to a pixel is long and complicated. First, the HTML gets parsed into a Content Tree. This Content Tree essentially represents the same thing as the HTML on the web page. Next, the Content Tree is converted into a Frame Tree, which represents mostly the same thing as the Content Tree, but nodes represent rectangles that are paintable. Next, we enter the Painting phase, which takes as input the Frame Tree and issues drawing commands to create a painted Layer Tree. Finally the Compositor finishes by flattening the painted Layer Tree and issues drawing commands to the GPU to create a single picture on the screen.

The Painting phase is made up of two steps: 1) Build a Layer Tree and 2) Rasterize the layers. Building a layer tree involves analyzing the Frame Tree and deciding which nodes from the Frame Tree go into which layers, creating a Layer Tree. You can think of a layer as a blank piece of paper that needs to be drawn on. Different elements of the Frame Tree are put into different layers depending on some heuristics, but the overall goal is to minimize invalidating parts of a webpage and to only have to redraw as little of the webpage as possible at each frame. Rasterizing involves issuing drawing commands to draw each layer, for example draw a line. Once each layer has finished drawing, we've finished painting the current frame and we hand off the painted Layer Tree to the Compositor. Will-animate helps with both building the Layer Tree and rasterizing the layers.

First we'll start off with how it helps with creating the Layer Tree. Most simple text web pages are a single layer, so the Layer Tree is rather simple. You can see which parts of a web page are layers by enabling the preference 'layers.draw-borders' in your about:config on Firefox. For many simple text websites, all you'll see is a single green box around the whole page, with a few green boxes around parts of Firefox itself. The whole webpage can be rendered in a single layer (e.g. front page of reddit.com). If you go to a media heavy page such as any video you YouTube, you should see a large red box around the video section, indicating another layer. For Firefox OS, you can see the layers by enabling the preference "Draw Layers Borders".

Without will-animate on Firefox OS, for example the Settings App, the Layer Tree is created once the application is launched and the user begins scrolling. There is a layer around the scrollable area of the Settings app and the Gecko engine does some heuristics to determine that a layer is a scrollable layer, which is then optimized by the platform layer for smooth scroll animations. However, on many of these apps, in order to save memory, the layer tree is collapsed. The scrollable layer's memory is reclaimed because nothing on the screen is moving and Gecko assumes that everything will remain static, moving Frame Tree nodes into as few layers as possible. When the user scrolls again, the layer tree has to be rebuilt and once again Gecko has to detect which part of the app will scroll and optimize for that case. You can see the layer tree collapsing in this video where the borders go away after a couple of seconds, then the layer tree being rebuilt after we scroll again:

With will-animate, we optimize for two things when building the Layer Tree. First, the heuristics to determine that a layer is scrollable go away which optimizes some of that guesswork to figure out which layer has a scrollable component. Second, it prevents Gecko from collapsing the layer, which makes subsequent scrolls start just a tad faster. You can see in this second video, the layer for the scrollable area never collapses, which means from the layout and graphics point of view, the layer tree wasn't thrown away.

Will-animate also helps the Rasterizing of the Layer Tree. As a user scrolls a web page, the Layer Tree constantly has to determine which elements of the webpage are now in view as they are scrolled in and issue draw commands to the appropriate layers to draw the newly scrolled in elements. You can see what elements are being painted in by enabling the "Flash repainted area" option. As you scroll, different elements get different colors meaning they are actively being painted. Elements that were previously on the screen stay the same color as we don't have to repaint those elements. When the Layer Tree collapses, a paint command to draw the whole screen again is issued on a single static layer. You can see a video of this here:

With will-animate, because the Layer Tree isn't collapsed, we don't have to issue the final repaint at the end of the video. Also, once we start scrolling again, we only have to paint the new elements that are scrolled in, saving us a bit of work.

Overall, for the Settings app, we save ~20ms of work, which prevents us from skipping 1 frame if we want to render things at 60 FPS (1000 ms / 60 = 16.66... ms per frame). Some initial numbers show better savings like the home screen app or a whopping 72ms for the Calendar app. Benoit has proposed will-animate as a new CSS property to www-styles, so hopefully it comes to a browser near you.

 

Thanks much to Benoit Girard for proofreading.