Skip to end of metadata
Go to start of metadata

Overview


When you write HTML code, and the browser renders it, the process by which the HTML elements end up as a rendered web page on the screen can roughly be summarized as follows:

  • JavaScript Execution(V8): Run the JavaScript code in your webpage. This is done before rendering is started.
  • Style & Layout: Apply the CSS style to the elements being rendered and generate the geometry and position of the elements.
  • Paint: Fill out the pixels for the element into layers.
  • Composite: Draw the layers onto the screen.

Although the first 3 stages (JavaScript, styling/layout, and painting) are done entirely on the CPU, the fourth stage (compositing) can be handled by the GPU. The goal of this document and your animations should be to move as much of your animations into this last stage as possible.

Profiling Tools

The first thing you should do when you start investigating sub-optimal animation performance is to use the profiling tools you have available with the Emulator. There are mainly 2 ways: using the control panel and using the Chromium Developer Tools (DevTools).

Control Panel

You have the following options available to you in the Control Panel:

  1. Show Paint Regions: Enables the rendering of paint regions in the Emulator. These green rectangles appear anywhere on the screen where painting is don. They start off strong and slowly fade away.
  2. Show Layer Borders: Enabling this option renders a transparent bronze-colored rectangle around DOM elements in your application which are considered a layer in the chromium rendering engine. These layers translate directly into hardware layers and so any animations on them can be performed entirely in hardware, without the need to trigger any repaints by the CPU.
  3. Show FPS Counter: This option enables a live FPS counter to appear on the top-right corner of the Emulator screen. It shows you the current frames-per-second (fps) count of your web app as well as a running after and a graph. You should try to keep the FPS count at at least 30 fps at all times, preferably 60 fps.

DevTools

DevTools is a tool that comes with most Chromium-based browsers (like Opera or Chrome) that enable the user to inspect the internal workings of their HTML page. This gives them information like where each HTML element is rendered, what CSS properties it has, and even more detailed information like JavaScript and rendering profiling in addition to other debug tools. To launch DevTools in the Emulator, simply click on the "Open DevTools for this URL" link at the bottom of the url bar in the Control Panel.

There are loads of articles available on the internet that detail how to use Devtools. You can get started at the official Chrome DevTools documentation.

Tips & Tricks

1. translateZ Layer Creation

As mentioned at the beginning of this document, rendering in Chromium is done in 3 stages namely Style/Layout, Painting and Compositing. You should always strive to trigger as few of these operations as possible. This means you should try to avoid Layout, and Painting as much as you can. One way of doing this is to promote animating elements into their own layer, by adding thetransform: translateZ(0); CSS property into your element. This enables Chromium to cache the layer and just recomposite it if there was only simple changes to the page, like transform or opacity updates that didn't trigger layout or paint of the whole page.

Ideally, this would mean one layer per animatable entity. However, creating too many layers has its own overhead so you need to come up with a good compromise. A good rule-of-thumb is to put together all elements that have the same transform into the same layer. If you start noticing your performance going down due to too many layers, you should start merging layers or shaving off animations until your layer count is under control. For more information, see GPU accelerate your DOM elements.

Note: There are more ways to promote an element into its own layer so don't go using the translateZ transform on every element. For more information on what causes layer creation, refer to the section below on Compositing Reasons.

2. translate3d Animation

Similar to the translateZ CSS transform property, we can use the element.style.transform = "translate3d(x,y,0);" property in JavaScript to promote an element to its own layer when it is animated. You should not use the element.style.top and element.style.left properties to animate in JavaScript, since this will cause the element to be repainted in its new position instead of just simply composited. For more information, see On translate3d and layer creation hacks which goes into more detail on this subject.

3. Use CSS Animations

The easiest way to achieve high performance animations using the Opera Devices SDK is to use CSS transitions/animations instead of JavaScript animation. This is because CSS animations run on their own thread and most of the time just trigger compositing (if only the CSS transform or opacity property animated). Due to their asynchronous nature, even if other animations are running poorly, the elements that are being animated via CSS transitions/animations will still animate smoothly. For more information, see this CSS vs JavaScript animations article.

4. Avoid realtime DOM generation

If using CSS animations is the easiest way to increase your animation performance, using realtime DOM generation is the easiest way to kill it. Adding or removing an HTML element (div, img, etc) on the fly via JavaScript forces the internal DOM tree to be regenerated, the CSS styles are reapplied and the layout is done again. All the elements then need to be repainted and recomposited. It is as good as refreshing your webpage/portal so don't do it!

5. Avoid over-nesting DOM elements

An image-in-a-div is a good idea but an image-in-a-div-in-a-div-in-a-div-in-a-div-in-a-div might be overkill. A simple change in an element deep inside the nest might trigger unnecessary checking throughout the nest to see if reflow is needed, even if it isn't! For more information, see this Minimizing browser reflow article.

Opera TV Store Example

We look at a simple example here with an older version of the the Opera TV Store user interface. To see if it is well-designed and high performance, we turn on paint rects and layer borders turning on these options in the control panel. When the TV Store loads, this is the screen we see:

Figure 1: Analyzing the Opera TV Store.

Looking at the resulting image of the TV Store, you can observe the following:

  • There is a blue grid diving the screen into squares. This represents the tiles being rendered for each layer and the default size is 256x256 pixels. You do not need to worry about this tiling when analyzing and debugging your animations.
  • The page also has certain orange rectangles (4 in this case). Each rectangle represents a layer in the page. You can see we have a layer for the Opera TV Store logo, a layer for the main menu on top, a layer for the carousel of apps in the middle and a final layer for the background covering the entire screen.
  • The number of layers is quite low and it is close to the optimal. Each layer is a separate animatable entity, i.e. all the elements within a layer are transformed together. This is because all the menu items are translated left or right at the same time and speed, as well as all the carousel items in the main body. The Opera TV Store logo does not animate or move, so we can live without having it as a layer. The background is always its own layer so we can't avoid that.
  • We observe that the focus rectangle (blue above on the 'iG Arena' app) is not its own layer and we can immediately see that it will be a problem when it moves independently of the carousel items below it. In fact, we expect the focus rectangle to be repainted every time is it moved, so lets put this to the test. Below you have a screenshot of the focus rectangle animation. We see indeed a green rectangle overlay on both the old carousel item and the new one when the focus is moved.
  • There is a good reason for this in the case of the TV Store portal. It is because the whole background color of the carousel item div changes when it is selected so the thumbnail has to repainted. And this brings us to an important conclusion to this section: optimization is always a compromise between performance and quality. You should save as much painting as you can afford, but not more. It is simply a mattering of finding your comfort spot on the performance/quality curve.


Figure 2: Repaint caused when the focus rectangle moves.

Compositing Reasons

As promised, here is a (non-exhaustive) list of reasons why Chromium promotes an element to its own layer (taken directly from Gpu Accelerated Compositing in Chrome:

  • Layer has 3D or perspective transform CSS properties.
  • Layer is used by <video> element using accelerated video decoding.
  • Layer is used by a <canvas> element with a 3D context or accelerated 2D context.
  • Layer is used for a composited plugin.
  • Layer uses a CSS animation for its opacity or uses an animated CSS transform.
  • Layer uses accelerated CSS filters.
  • Layer has a descendant that is a compositing layer.
  • Layer has a sibling with a lower z-index which has a compositing layer (in other words the layer overlaps a composited layer and should be rendered on top of it).

Further Reading

The following links give more information about optimizing your web code:

Essential Websites to Bookmark

  • No labels