To understand the symptoms and potential causes of jank, it's important to have a basic understanding of the browser's rendering pipeline. This article will briefly describe that pipeline assuming knowledge of the web platform but no knowledge of how the rendering engine works, with links to relevant design docs for more info.
This article is adapted from a similar section in Using Frame Viewer to Bust Jank. It goes into greater detail, but doesn't cover any examples. See that document for a more illustrated version of this process, which annotates a trace of an example style modification.
Much more detail about the specifics of how rendering inside of Blink works (i.e. the style, layout, and Blink paint systems), as well as a (somewhat dated) discussion of how it might evolve, can be found in the Rendering Pipeline doc. This document is intended to be more descriptive, higher-level, and include the related input and compositing systems.
The main subsystems are:
As an example, updating the innerHTML of a DOM element that’s visible on-screen will trigger work in every one of the systems above. In bad cases any one of them can cost tens of milliseconds (or more), meaning every one of them can be considered a liability for staying within frame budget -- that is, any one of these stages can be responsible for dropping frames and causing jank.
The ordering of these events is often unfortunate, since currently Blink has a simple FIFO queue for all event types. The Blink Scheduler project is seeking to add a better notion of priorities to this event queue. The scheduler will never be able to actually shorten non-yielding events, though, so is only part of the solution.
It's also worth noting that the pipeline above isn’t strictly hierarchically ordered -- that is, sometimes you can update style without needing to re-lay-out anything and without needing to repaint anything. A key example is CSS transforms, which don’t alter layout and hence can skip directly from style resolution (e.g. changing the transform offset) to compositing if the necessary content is already painted. The below sections note which stages are optional. Different types of screen updates require exercising different stages in the pipeline. One way to keep animations inexpensive is to avoid stages of this pipeline altogether. The following sections cover a few common examples.
Lastly, note that user interactions that the browser handles, rather than the application, are largely unexceptional: clicking a button changes its style (adding pseudo-classes) and those style changes need to be resolved and later stages of the pipeline run (e.g. the depressed button state needs to be painted, and then the page needs to be re-composited with that new painted content).
Updating the viewport's scroll position has been designed to be as cheap as possible, relying heavily on the compositing step and its associated GPU machinery.
In the simple case, when the user tries to scroll a page events flow through the browser as follows:
This flow skips a lot of detail, which is covered in the Accelerated Compositing design doc if you're curious.
Here's an outline of how modifying style propagates through the browser:
This flow summarizes a lot of detail, again covered in the Accelerated Compositing design doc.
Adding new DOM or replacing a DOM subtree requires parsing the new DOM. Whether this parsing happens synchronously on the main thread or asynchronously off-thread depends on how the DOM is inserted. An incomplete listing:
After parsing, rendering proceeds roughly as above in "modifying style of existing DOM"
For Developers >