This article has participated in the third “topic writing” track of the Denver Creators Training Camp. For details, check out: Digg Project | Creators Training Camp third is ongoing, “write” to make a personal impact

preface

You can view the chrome rendering flowchart in the Performance panel

It is divided into the following steps:

  1. Parse HTML: Builds a DOM Tree by parsing HTML text
  2. Recalc Styles: Style calculation to generate the CSSOM Tree
  3. Layout: Calculates the geometry of visible elements (position, size) to generate a Layout Tree, also known as reflow
  4. Update layer Tree: Create the layer tree by creating the context and element hierarchy
  5. Paint: This step records the method that needs to be drawndraw calls, in the rasterizationRasterizationWhen,draw callsWill be executed.
  6. Composite Layers: Some special rendering Layers are considered Compositing Layers, and Composite handles them
  7. And then give that information tocompositor threadProcess and print to the screen for the GPU

Build a DOM tree

This part is taken from article 4 of the big Three: what happens from entering the URL to rendering the page? — Parsing algorithms

Since the browser can’t understand HTML strings directly, you need to turn this series of byte streams into a meaningful and easily manipulated data structure called the DOM Tree. DOM Tree is essentially a multi-fork Tree with document as the root node.

The HTML parsing algorithm is divided into two stages:

  1. Mark,
  2. done

The two corresponding processes are lexical analysis and grammatical analysis

Tokenization algorithm

This algorithm, also known as the tag generator, takes HTML text as input and HTML tags as output. The finite automatic state machine is used to complete it. That is, when one or more characters are received in the current state, it will be updated to the next state.

<html>
  <body>
    Hello world
  </body>
</html>
Copy the code

A simple example illustrates the tokenization process.

When < is encountered, the status is open for the flag

Receives the character [a-z] and enters the token name state.

This state remains until > is encountered, indicating that the tag name recording is complete, at which point it becomes data state.

Next, do the same for the body tag.

At this point the HTML and body tags are recorded.

Go to > in , enter the data state, and receive the following character Hello World

After receiving the < in , the token is returned to the open state. After receiving the next /, an end tag token is created

Then enter the tag name state and return to the data state when > is encountered.

Then process in the same style

Tree-building algorithm

As mentioned earlier, the DOM tree is a multi-fork tree with document as the root node. So the parser first creates a Document object. The tag generator sends information about each tag to the tree builder. When the tree builder receives the corresponding tag, it creates the corresponding DOM object. After creating this DOM object, you do two things:

  1. willDOM objectAdd to the DOM tree.
  2. Press the corresponding mark into store open (withClosing tagsMeaning corresponding to) element on the stack.

Take the following example:

<html>
  <body>
    Hello world
  </body>
</html>
Copy the code

First, the state is initialized.

An HTML tag from the tag generator is received, and the status changes to **before HTML state **. Create an HTMLHtmlElement DOM element, add it to the document root object, and stack it.

Then the status automatically changes to **before head, and the body from the tag generator indicates that there is no head, and the tree builder automatically creates an HTMLHeadElement and adds it to the DOM tree.

Now enter the **in head state and jump directly to after head**.

Now the tag generator passes in the body tag, creates HTMLBodyElement, inserts it into the DOM tree, and pushes it onto the open tag stack.

The state then changes to **in body, which receives the following series of characters: Hello world. When the first character is received, a Text node is created and inserted into it, and then the Text node is inserted under the body in the DOM tree. As successive characters are received, they are attached to the Text** node.

Fault-tolerant mechanism

Speaking of the HTML5 specification, I have to say that it has a strong tolerance strategy and very strong fault tolerance. Although people have different opinions, I think as a veteran front-end engineer, it is necessary to know what HTML Parser does in terms of fault tolerance.

Here are some classic examples of fault tolerance in WebKit, and others are welcome to fill in.

  1. use</br>Rather than<br>
if (t->isCloseTag(brTag) && m_document->inCompatMode()) {
  reportError(MalformedBRError);
  t->beginTag = true;
}
Copy the code

All in

form.

  1. The form of discrete
<table>
  <table>
    <tr><td>inner table</td></tr>
  </table>
  <tr><td>outer table</td></tr>
</table>
Copy the code

WebKit automatically converts to:

<table>
  <tr><td>outer table</td></tr>
</table>
<table>
  <tr><td>inner table</td></tr>
</table>
Copy the code
  1. Form elements are nested

Ignore the form.

Build CSSOM tree

The process of building a CSSOM Tree is very similar to that of building a DOM Tree. When a browser receives a piece of CSS, the first thing the browser does is recognize the Token, then build the node and generate the CSSOM Tree

Let’s go to the last example

body {
  font-size: 16px;
}
p {
  font-weight: bold;
}
span {
  color: red;
}
p span {
  display: none;
}
img {
  float: right;
}
Copy the code

The resulting tree structure looks like this

Layout tree construction, layout and drawing

We now have a complete DOM tree and CSSOM tree. The next step is to use the browser’s Layout system to determine the location of the elements, which is to generate a Layout Tree.

  1. Layout tree generation generally works as follows:
    1. Iterate over the generated DOM tree nodes and add them toThe layout in the tree.
    2. Computes the coordinate positions of nodes in the layout tree.

Note that visibility: hidden is not the same as display: None. The former hides the element, but the element still occupies layout space (rendering it as an empty box), while the latter (display: None) completely removes the element from the rendering tree, and the element is neither visible nor part of the layout.

Now that we have a Layout Tree, can we go through the render Tree and draw the contents of each LayoutObject onto the page?

No, browsers also have a cascading context, which determines how elements overlay each other (like z-index). This makes it possible for elements located earlier in the document flow to overwrite elements located later. The DFS process described above can only mindlessly overwrite the previous element with the last element in the document flow.

So, PaintLayer.

From LayoutObjects to PaintLayers

Generally, LayoutObjects, which have the same coordinate space, belong to the same PaintLayer. PaintLayer was originally used to implement a cascading context to ensure that the elements on the page are composable in the correct order so that overlapping elements, translucent elements, and so on can be shown correctly. So LayoutObjects that meet the conditions for forming a cascading context must create a new rendering layer for them, as well as other special cases where a new rendering layer is created for special LayoutObjects, such as Overflow! = visible element. Depending on why PaintLayer is created, there are three common categories:

  • NormalPaintLayer

    • Root element (HTML)
    • Have clear positioning attributes (relative, fixed, sticky, absolute)
    • Opacity < 1
    • I have a CSS fliter
    • The CSS mask property is available
    • Has CSS mix-blending-mode property (not normal)
    • CSS transform property (not None)
    • Backface -visibility property is hidden
    • CSS Reflection property
    • Has CSS column-count property (not auto) or CSS column-width property (not auto)
    • Currently, animations are applied to opacity, Transform, fliter, and backdrop filter
  • OverflowClipPaintLayer

    • The overflow is not visible
  • NoPaintLayer

    • A PaintLayer that does not require paint, such as an empty div with no visual attributes (background, color, shadow, etc.).

LayoutObject that meets these conditions will have a separate rendering layer, while other LayoutObject objects will share one with their first parent element that has a rendering layer.

From PaintLayers to GraphicsLayers

Some particular render Layers are thought of as Compositing Layers, which have a separate GraphicsLayer, and other render Layers that are not Compositing Layers share one with their first parent that has a GraphicsLayer.

Each GraphicsLayer has a GraphicsContext. The GraphicsContext is responsible for the output of bitmaps of this layer. Bitmaps are stored in shared memory and uploaded to GPU as textures. Then draw to the screen, at which point our page is displayed on the screen.

The render layer is promoted to the compositing layer for several reasons:

Note: There is a prerequisite for the rendering layer to be promoted to the compositing layer. This rendering layer must be the SelfPaintingLayer. All the situations discussed below when a rendering layer is promoted to a composition layer are under the premise that the rendering layer is SelfPaintingLayer.

  • 3D or Perspective transform CSS properties
  • Uses elements that accelerate video decoding
  • Elements that have either a 3D (WebGL) context or an accelerated 2D context
  • Hybrid plug-ins (e.g. Flash)
  • Animation or transition is applied to opacity, Transform, fliter, and backdropfilter (animation or transition must be active, The animation or Transition layer fails before the animation or Transition effect starts or ends.
  • Will-change is set to opacity, transform, top, left, bottom, and right (top, left, etc., should be set with clear positioning attributes, such as relative, etc.)
  • An element that has accelerated CSS filters
  • Element has a sibling element with a lower Z-index that contains a composite layer (in other words, the element is rendered above the composite layer)
  • … . For a detailed list of all cases, see taobao Fed article: Wireless Performance Optimization: Composite

3D Transform, will-change set to opacity, transform, CSS transitions and animations including opacity and transform are three common cases of raising compositing layers.

In addition to the above directly causing PaintLayers to be promoted to GraphicsLayer, there is also the following situation where A is promoted implicitly because B is promoted. See CSS GPU Animation: Doing It Right

Each composite GraphicsLayer has a GraphicsContext, and the GraphicsContext creates a bitmap for that Layer, which means that each GraphicsLayer has a bitmap. GraphicsLayer is responsible for drawing the LayoutObject contained in its Own PaintLayer and its descendants into place maps. The bitmap is then handed to the GPU as a texture. So now the GPU receives the texture of the HTML element’s GraphicsLayer, and it might also receive the texture of some element that has been promoted to a GraphicsLayer because of attributes like a 3D Transform.

Now, THE GPU needs to composite multi-layer textures. Meanwhile, the GPU can specify different synthesis parameters for each texture layer during texture synthesis, so that the texture can be synthesized after transform, mask, opacity and other operations. And the GPU for this process is the underlying hardware acceleration, performance is very good. Finally, the texture is synthesized into one piece of content and drawn onto the screen.

Therefore, animation processing will be very efficient when elements have CSS animation or CSS transition with properties such as transform and opacity. These properties do not need to be redrawn in animation, but only need to be recomposed.

By the way: How do I view compositing layers on a page?

You can use the Chrome DevTools tool to see the composition layer on the page

One way is to select the Layer Borders option on the Rendering panel, and the composite Layer on the page will have a yellow border

The second way is to directly open the Layers panel to check, which also shows the reason for the composition

draw

The rendering engine then breaks down the layer rendering into instructions, such as drawing the background first and then drawing the borders…… These instructions are then sequentially assembled into a list to draw, which is equivalent to a wave of planning for subsequent drawing operations.

Again in the Layers panel, we can see the detailed drawing list

Generate graph blocks and generate bitmaps

Now we start drawing, which is actually done in the renderer process by a special thread called the composite thread.

When the drawing list is ready, the main thread of the renderer process sends a COMMIT message to the composition thread, which submits the drawing list to the composition thread. Now it’s time for synthetic threads to show off.

First of all, considering that the viewport is only so big, when the page is very large, it takes a long time to slide to the bottom, and it is quite a waste of performance to draw it all in one go. Therefore, the first thing the composition thread needs to do is block the layer. The size of these blocks is usually not particularly large, usually 256 _ 256 or 512 _ 512. This will greatly speed up the first screen of the page.

Since the data of the later image blocks will enter the GPU memory, it takes a long time to upload the data from the browser memory to the GPU memory, even if part of the image blocks are drawn, it may take a lot of time. Aiming at this problem, Chrome has adopted a strategy: in the first composite block using a low resolution images, so that the first screen to display the time just show low resolution images, this time to continue synthesis operation, when the normal block content mapping to end, will replace the current low resolution image block. This is also a way for Chrome to optimize the first screen loading speed.

As a side note, a rasterized thread pool is maintained in the render process to convert blocks into bitmap data.

The composite thread then selects the block near the viewport and passes it to the rasterized thread pool to generate the bitmap.

The bitmap generation process is actually accelerated using the GPU, and the generated bitmap is eventually sent to the composite thread.

Display content

Once the rasterization operation is complete, the composite thread generates a draw command, called “DrawQuad”, and sends it to the browser process.

The VIz component in the browser process receives this command, draws the page content into memory, that is, generates the page, and then sends the memory to the graphics card. Why send video cards? I think it’s worth talking about how a monitor displays images first.

Whether PC monitor or mobile phone screen, there is a fixed refresh frequency, generally 60 HZ, that is, 60 frames, that is, update 60 pictures a second, a picture stays about 16.7ms. Each update comes from the graphics card’s front buffer. The graphics card receives the browser process from the page, will synthesize the corresponding image, and the image saved to the back buffer, and then the system will automatically swap the front buffer and the back buffer, so the cycle update.

As you can see from this, when an animation takes up a lot of memory, the browser is slow to generate images, the images are not delivered to the graphics card in a timely manner, and the monitor is still refreshing at the same rate, so there will be a lag, which is a noticeable drop of frames.

Reflow & Redraw & Composition

Backflow and redraw are old and familiar, but it’s worth mentioning here in conjunction with browser rendering

csstriggers.com/

backflow

First, reflux. Backflow is also called rearrangement.

The trigger condition

Simply put, when changes to the DOM structure cause the DOM geometry to change, the browser marks the current Layout as dirty, causing the browser to perform all of the above steps in the next frame.

Specifically, the following actions trigger backflow:

  1. The geometry of a DOM element changes. Common geometry properties arewidth,height,padding,margin,left,top,borderWait, that’s easy to understand.
  2. Make the DOM node happenIncrease or decreaseormobile.
  3. Read and writeoffsetThe family,scrollandclientIn order to retrieve these values, the browser needs to perform a backflow operation.
  4. callwindow.getComputedStyleMethods.

Note that the browser does not rearrange it until the next frame, the next render. Instead of rearranging the CSS immediately after executing the line change, the browser will do a batch reflux, so you can write 100 lines of CSS change in the JS statement, but only rearrange it once on the next frame.

If you access properties such as offsetTop and scrollHeight while the current Layout is marked dirty, the browser will immediately rearrange the Layout to calculate the correct position of the elements. To ensure that you get the correct offsetTop, scrollHeight, etc. in JS.

//Layout not dirty access doma.offsetwidth does not Force Layout
domA.style.width = domA.offsetWidth + 1 + 'px'
//Layout is already dirty, Force Layout
domB.style.width = domB.offsetWidth + 1 + 'px'
//Layout is already dirty, Force Layout
domC.style.width = domC.offsetWidth + 1 + 'px'
Copy the code

In addition, the current Layout is no longer dirty after each rearrangement or forcible rearrangement. So if you access a property like offsetWidth, it doesn’t trigger a rearrangement.

// Layout will not be rearranged for any number of times it is not dirty.
console.log(domA.offsetWidth)
console.log(domB.offsetWidth)

//Layout not dirty access doma.offsetwidth does not Force Layout
domA.style.width = domA.offsetWidth + 1 + 'px'
//Layout is already dirty, Force Layout
console.log(domC.offsetWidth)

//Layout is no longer dirty and does not trigger rearrangements
console.log(domA.offsetWidth)
//Layout is no longer dirty and does not trigger rearrangements
console.log(domB.offsetWidth)
Copy the code

redraw

The trigger condition

Repaint occurs when DOM changes result in style changes that do not affect geometry properties.

The redraw process skips the layout tree and layer tree stages and goes straight to the drawing list, then continues with the following operations, such as partitioning and bitmap generation.

As you can see, redrawing does not necessarily cause backflow, but backflow must have redrawn.

synthetic

And then there’s direct synthesis. For example, CSS3’s transform, opacity and filter properties can be used to achieve the synthetic effect, which is commonly referred to as GPU acceleration.

Ascension to the composite layer has the following benefits in brief:

  • The bitmap of the composite layer will be synthesized by the GPU faster than the CPU

  • When repaint is required, only repaint itself is required and no other layers are affected

  • Layout and Paint are not triggered for Transform and opacity effects

We can use composition layers to optimize page performance

Elements that enhance the animation

The advantage of the composition layer is that it does not affect the drawing of other elements, so in order to reduce the influence of animation elements on other elements and thus reduce the paint, we need to promote the elements in the animation effect to the composition layer.

The best way to improve the composition layer is to use the WILL-change property of CSS. From the previous section, it can be seen that setting will-change to opacity, transform, top, left, bottom and right can promote an element to the composition layer.

#target {
  will-change: transform;
}
Copy the code

The compatibility is as follows:

For those browsers that do not currently support will-change, it is currently common to use a 3D Transform property to enforce promotion to the composition layer:

#target {
  transform: translateZ(0);
}
Copy the code

Use transform or opacity to animate

This is well known, but it is important to note that transform and opacity do not trigger paint after an element is promoted to a composition layer, or if it is not a composition layer, it will still trigger paint.

Refer to the article

(1.6W word) browser soul ask, how many can you catch?

Render pages: How browsers work

Building an object Model

Detailed process for browser rendering: redraw, rearrange, and composite are just the tip of the iceberg

Wireless performance optimization: Composite