Author: Huang Haoqun

A CSS property caused a murder

Web page performance is the focus of front-end development, there are many indicators to judge the performance of the front-end Web page, the smoothness of the page is one of them, how to make the page become “smooth and silky”, to discuss can be quite a material topic. When I was developing the H5 page for mobile, I had an interesting performance problem — a store page had a serious delay on IOS phones, but it worked perfectly on Android models. To summarize the performance of the iPhoneX test:

  • There is a significant delay in page loading, but the network request caught through the proxy is not higher than that of Android;

  • When the page scrolls, there will be a brief partial white screen, that is, frame loss.

Based on these representations, it’s not hard to infer that something is frantically consuming CPU and blocking the rendering process.

But I don’t know what it is, if you ask me. The only way to overcome this problem is to use the “code dichotomy”. After a lot of hard work, the root of the problem was finally focused on the following line of CSS code:

  filter: blur(100px);
Copy the code

This line of CSS code is used to implement a Gaussian blur to construct the bottom shadow of a coupon module. Because the event is configured with multiple coupons, there are multiple div elements in the page with this attribute set, and the IOS browser seems to have a hard time rendering this attribute (for unknown reasons), which leads to high CPU usage in the rendering process, resulting in delays.

Oh? Busy CPU? Easy! I added one more line of code like this to the coupon module, and the problem was solved……

  will-change: transform;
Copy the code

You read that right, and I didn’t write less, it was a single line of code.

Those of you who are familiar with it may have noticed that the basic principle is actually very simple. This line of code can enable the GPU to accelerate page rendering, thus greatly reducing the load on the CPU, and achieving the purpose of optimizing page rendering performance. Increase Your Site’s Performance with Hardware-accelerated CSS.

Problem solved, but is it really over? In keeping with the great principle of “uprooting the tree to find the root”, I took a good look at this and realized that GPU acceleration is not that simple.

Browser Rendering Process

Before we get into specifics, we need to look at some basic concepts of the browser rendering process. The browser rendering process is an old topic, for “how the browser renders the content of a page” this kind of question, many people can tell a relatively complete process, from the network request to the browser parsing, can be specific to a lot of details. In addition to the steps of obtaining network resources, the display of Web pages we understand can be generally divided into several steps: building DOM tree, building rendering tree, layout, drawing and rendering layer synthesis.

  • Building a DOM tree: The browser parses HTML into a DOM tree with a tree structure, typically when the page first loads or when the page’S JavaScript modifies the node structure.

  • Build the render tree: The browser parses CSS into a CSSOM tree, which is combined with the DOM tree to form a render tree.

  • Layout: The browser calculates the position of each node in the screen based on the nodes represented in the render tree, the CSS definition of each node, and their affiliation. The layout of elements in a Web page is relative. Changes in the location and size of elements on the page often lead to linkage of other nodes, and the layout needs to be recalcitrated. In this case, the layout process is generally called Reflow.

  • Paint: Walk through the render tree, calling the renderer’s Paint () method to draw the node content on the screen, essentially a pixel-filling process. This process can also occur when a part of the screen is redrawn due to reflux or some CSS change that does not affect the layout. This is called Repaint. In fact, the drawing process is done on multiple layers called renderlayers.

  • Composite: Multiple rendered layers are combined in the appropriate overlapping order to produce a bitmap that is displayed on the screen via the graphics card.

This is a basic browser process from parsing to drawing a Web page, related to the solution to the page lag problem above, mainly the final step – rendering layer composition.

Render layer composition

What is render layer composition

Each node in the DOM tree corresponds to a RenderObject. When each node’s RenderObject is in the same coordinate space (z-space), a renderLayer is created. The render layer ensures that the page elements are stacked in the correct order, resulting in a composite that handles the display of transparent and overlapping elements correctly.

This model is similar to the layer model in Photoshop, where each design element is a separate layer, and multiple layers are superimposed on the z-axis space in the right order to finally form a complete design drawing.

This process is especially important for pages with overlapping elements, because if the layers are merged in the wrong order, the elements will appear abnormally.

Second, the browser rendering principle

From the browser rendering process, we know that the page HTML is parsed into a DOM tree, and each HTML element corresponds to a node in the tree structure. In fact, there are some transitional data structures in the process of transforming from DOM tree to each rendering layer, and finally performing merge and drawing, which record the transformation principle of DOM tree to screen graphics, and its essence is the evolution of tree structure to layer structure.

1. RenderObject

A DOM node corresponds to a render object, which still maintains the tree structure of the DOM tree. A render object knows how to draw the contents of a DOM node by making the necessary draw calls to a GraphicsContext.

2. RenderLayer

This is the first layer model built during browser rendering. Render objects in the same coordinate space (z-space) are merged into the same render layer, so depending on the cascading context, render objects in different coordinate Spaces will form multiple render layers to reflect their cascading relationship. Therefore, the browser automatically creates a new render layer for a render object that meets the conditions to form a cascading context. Several common situations that can cause the browser to create a new rendering layer for it include:

  • The root document

  • Have clear positioning attributes (relative, fixed, sticky, absolute)

  • opacity < 1

  • There is a CSS fliter property

  • There is a CSS mask attribute

  • The CSS mix-blast-mode attribute is displayed and the value is not normal

  • Has a CSS Transform property that is not None

  • The backface-visibility property is hidden

  • There is the CSS Reflection property

  • There is a CSS column-count attribute with the value not auto or a CSS column-width attribute with the value not auto

  • Currently, animations are applied for opacity, Transform, Fliter, and Context-filter

  • The overflow is not visible

DOM nodes and render objects are one to one, and render objects that meet the above conditions can have their own render layer. Of course, independence is not entirely accurate. It does not mean that they share the render layer entirely, since rendering objects that do not meet the above conditions will share the render layer with their first parent element that has a render layer, so in effect, these render objects will share the render layer with their part element.

3. GraphicsLayer

The GraphicsLayer is actually a layer model that is responsible for generating the graph of the content that is finally ready to be rendered. It has a GraphicsContext that outputs the bitmap of that layer. The bitmaps stored in the shared memory will be uploaded to the GPU as textures. Finally, the GPU will synthesize multiple bitmaps and then draw them to the screen. At this time, our page will be displayed on the screen.

So the GraphicsLayer is an important rendering vehicle and tool, but it doesn’t deal directly with the rendering layer, it deals with the composition layer.

4. CompositingLayer

Rendering layers that meet certain special conditions are automatically promoted by the browser to the composition layer. The compositing layer has a separate GraphicsLayer, while any rendering layer that is not a compositing layer shares one with its first parent layer that has a GraphicsLayer.

What special conditions must be met for a render layer to be promoted to a composition layer? Here are some common ones:

  • 3D Transforms: Translate3D, translateZ, etc

  • Video, Canvas, iframe and other elements

  • The opacity animation is converted using element.animate ()

  • The OPACITY animation is converted using the с SS animation

  • position: fixed

  • Has the will-change attribute

  • Animation or Transition is applied to opacity, Transform, Fliter, and BackdropFilter

Therefore, the solution in the first example is to use the will change attribute to elevate the CPU-heavy render element to a new composited layer to enable GPU acceleration, so you can also use Transform: translateZ(0) to solve this problem.

It is important to note that many people confuse the conditions of the compositing layer with the conditions of the rendering layer. These two conditions occur in two different layers and are completely different.

In addition, some articles will also list CSS Filter as one of the factors affecting Composite, but I found no effect after verification.

3. Implicit synthesis

As mentioned above, the render layer is promoted to the composition layer by the browser when certain explicit special conditions are met. In addition, there is an implicit composition in the Composite phase of the browser, where some render layers are promoted to the Composite layer by default for certain scenarios.

For implicit composition, the CSS GPU Animation is described as follows:

This is called implicit compositing: One or more non-composited elements that should appear above a composited one in the stacking order are promoted to One or more non-composite elements should appear on top of the composite elements in the stack order and be promoted to the

This sentence may not be easy to understand, but it actually describes a problem overlap. Here’s an example:

  • The two Absolute positioned divs overlap on the screen, according toz-indexOne div will be “overlaid” on top of the other.

  • At this point, if the div below has a CSS property:transform: translateZ(0), will be promoted to the composition layer by the browser. The improved composition layer is located above the Document. If there is no implicit composition, the div that should have been on the top will still share a GraphicsLayer with the Document, but the level will be lowered, resulting in the problem of confusion of overlapping relations between elements.

  • So in order to correct the wrong overlap order, the browser has to make the render layer that is supposed to “cover” it also become the compositing layer.

Layer explosion and layer compression

1, layer explosion

As we can see from the above studies, some of the causes of synthesis layers are too subtle, especially implicit synthesis. In the normal development process, we rarely pay attention to the problem of layer synthesis, it is easy to produce some synthetic layer is not expected range, when these do not meet the expected synthesis layer reaches a certain magnitude, it will become layer explosion.

Layer explosion consumes the GPU and a large amount of memory resources, which seriously consumes page performance, so blindly using GPU acceleration can be counterproductive. This article provides an interesting DEMO. The DEMO page contains an H1 header that applies the animation to Transform, resulting in the transform being rendered in the composition layer. Due to the particularity of Animation Transform (dynamic overlapping is uncertain), implicit composition can occur without overlapping, resulting in all the rendering layers corresponding to nodes whose Z-index is higher than it in the page being promoted to the composition layer, and finally creating thousands of composition layers on the page.

To eliminate implicit composition is to eliminate overlapping elements. In this DEMO, we only need to set the z-index attribute of the header h1 to a higher value than the rest of the elements on the page, so there is no need to improve the composition layer. Click on the check button in the DEMO to add a large Z-index to the h1 header, and the contrast is obvious.

2. Layer compression

Of course, browsers have a strategy for dealing with this problem. If multiple render layers overlap with a composition layer, these render layers are compressed into a Single GraphicsLayer to prevent a possible “layer explosion” due to overlapping. This sentence is not easy to understand. Here is an example:

  • The model is the same as before, except this time, there are four absolute positioned divs overlapping in the screen. The div at the bottom has a CSS property attached to ittransform: translateZ(0)If you follow the principle of implicit composition, the div on top of it will be promoted to a new composition layer. The third div on top of the second div will also be promoted to the composition layer. The fourth div will also be promoted to the composition layer. Wouldn’t that make four synthetic layers?

  • However, this is not the case. The browser’s layer-compression mechanism compresses multiple implicitly compositing layers into the same GraphicsLayer for rendering, that is, the top three divs end up in the same compositing layer, which is the browser’s layer-compression.

Of course, automatic browser layering is not a universal solution, and there are many specific situations in which a browser may not be able to perform layering. This article, Composite, lists a number of detailed scenarios.

Page rendering optimization based on layer composition

First, the gains and losses of layer synthesis

Layer composition is a relatively complex browser feature, so why should we care about something so low-level and hard to understand? That’s because when the render layer is upgraded to the composite layer, there are a number of benefits:

  • The bitmap of synthesis layer will be synthesized by GPU, which is much faster than CPU processing.

  • When repaint is needed, only repaint is needed and no other layers are affected.

  • Transform and Opacity do not trigger Repaint if an element is elevated to the synthesis layer. If the element is not in the synthesis layer, repaint is still triggered.

Of course, the pros and cons are relative and co-existing, and there are some drawbacks to layer composition that are often the root cause of our web performance problems:

  • The drawn layers must be transferred to the GPU, and when the number and size of these layers reach a certain level, the transfer may be very slow, resulting in flickering on some low – and mid-range devices;

  • Implicit compositing tends to result in an excess of compositing layers, each of which takes up extra memory, and memory is a valuable resource on mobile devices. Using too much memory can crash the browser and make performance optimizations counterproductive.

How to view the composition layer in Chrome Devtools

The feature of layer composition provides us with a way to optimize the performance of the page by using the terminal hardware capability. For some pages with heavy interaction and animation, reasonable use of layer composition can greatly improve the rendering efficiency of the page and improve the interactive experience. What we need to pay attention to is how to avoid the negative impact of layer synthesis on the page, or to put it another way, more often is how to weigh the interests, reasonable organization of the page synthesis layer, which requires us to have a detailed understanding of the page layer synthesis situation in advance. Chrome Devtools gives you the tools to easily view the composition layer of a page.

The first step is to look at the Rendering of the page. For a column page, go to More Tools -> Rendering and select Layer Borders. You can see that all the layers on the page have yellow borders.

That’s not enough, we need More detail on layer composition. Go to More Tools -> Layers and you can see a view like this:

On the left is a list of all elements promoted to individual composition layers. On the right is a view of the overall composition layer boundaries and details of the selected composition layers, including the following key information:

  • Size: the Size of the composition layer, which is the Size of the corresponding element;
  • Compositing Reasons: The reason for forming the composite layer, which is the most critical and the breakthrough point for us to analyze the problem. For example, the Compositing layer in the figure is caused by overlapping problem;
  • Memory estimate: Memory usage estimate.
  • Paint count: Number of times to draw;
  • Slow scroll Regions: indicates the Slow scroll region.

You can see that we have inadvertently created a lot of unexpected synthetic layers, which are meaningless synthetic layers that can be optimized.

Three, some optimization suggestions

1. Animation is implemented using Transform

For key animations that require a high level of experience, such as interactive and complex gameplay pages, where animation elements are constantly changing position, it is best to use Transform rather than changing left/top. The reason for this is that if you use left/top to change the position, the animation node and the Document will be rendered in the same GraphicsLayer, and the continuous animation will cause the entire Document to be constantly redrawn. Using Transform allows the animation node to be placed in a separate composited layer for rendering, without affecting the other layers when the animation occurs. On the other hand, animations run entirely on the GPU, which tends to be smoother than processing layers by the CPU and then sending them to the graphics card for display and drawing.

2. Reduce implicit composition

While implicit composits are basically about making sure that the layers overlap in the right order, they can easily lead to meaningless composits in real life, and ultimately require us to restrict our layout habits during development.

For example, the column page mentioned above, because of the usual development of the page generated too much synthesis layer, I tried to view the synthesis layer of the page, on the PC can obviously feel the time. Using Chrome Devtools analysis, it is not difficult to find that there is a transform button in the page, which is promoted to the composition layer. The uncertainty of animation overlap causes all other nodes in the page whose Z-index is larger than it but which do not actually overlap to be promoted to the composited layer (this is really a bad reason).

In this case, we just need to set the z-index value of the animation node to a higher order than the other unrelated nodes on the page. Of course, it is not necessary to blindly set z-index to avoid this. Sometimes z-index can also lead to implicit composition. In this case, you can try to adjust the order of nodes in the document so that the later nodes overwrite the front nodes, instead of z-index to adjust the overlap. There is not only one method, but the specific method depends on the specific page.

The improved page looks like this. You can see that we eliminated a lot of meaningless composition layers compared to before optimization.

3. Reduce the size of the synthetic layer

For a simple example, draw two div’s of the same size, but the implementation is a little different: one is set to size 100×100, the other is set to size 10×10, and then scale them up by 10 times, and we make both of them into the composition layer:

<style>
  .bottom..top {
    position: absolute;
    will-change: transform;
  }
  .bottom {
    width: 100px;
    height: 100px;
    top: 20px;
    left: 20px;
    z-index: 3;
    background: rosybrown;
  }
  .top {
    width: 10px;
    height: 10px;
    transform: scale(10);
    top: 200px;
    left: 200px;
    z-index: 5;
    background: indianred;
  }
</style>
<body>
  <div class="bottom"></div>
  <div class="top"></div>
</body>
Copy the code

Using Chrome Devtools to look at the memory usage of the two composition layers, it is found that the memory usage of the.bottom layer is 39.1 KB, while the memory usage of the.top layer is 400 B. This is because.top is the Composite layer and transform is in the Composite phase and is now executed entirely on the GPU. So for some solid color layers, we can reduce the physical size of the composition layer by using the width and height properties, and then transform: scale(…) Zoom in, which can greatly reduce the memory consumption caused by layer composition.

Refer to the article

CSS3 hardware acceleration also has the pit of wireless performance optimization: Composite CSS GPU Animation


If you think this post is valuable to you, please like it and follow us on our website and our WecTeam account, where we will post quality articles every week: