Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Chrome not need to repaint the entire layer on a change?

I am trying to understand, in practice, how the layout → paint → composite pipeline of Chrome works. During my testing, I got confused about Chrome's behavior in the following situation. (Codepen)

var button = document.querySelector('button');
var red = document.querySelector('.red');
var blue = document.querySelector('.blue');
button.addEventListener('click', function() {
  red.classList.toggle('test');
})
.wrapper {
  height: 100%;
  width: 100%;
  background-color: teal;
  position: static;
}

.square {
  height: 200px;
  width: 200px;
  position: static;
  transition: transform 0.3s ease,
    opacity 0.3s ease,
    width 0.3s ease,
    height 0.3s ease,
    box-shadow 0.3s ease;
}

.red {
  /* position: absolute; */
  background-color: red;
  top: 100px;
  left: 100px;
  /* will-change: transform; */
  opacity: 1;
}

.blue {
  background-color: blue;
  z-index: 3;
}

.test {
  /* transform: translate3d(50px, 50px, 0); */
  /* opacity: 0; */
  width: 60px;
  height: 60px;
  /* box-shadow: 0px 0px 0px 10px rgba(0,0,0,.5) */
}

button {
  position: fixed;
  right: 100px;
  top: 50px;
  z-index: 10000;
  font-weight: bold;
  background-color: yellow;
  padding: 5px 10px;
  /* will-change: transform; */
}
<div class="wrapper">
  <div class="red square"></div>
  <div class="blue square"></div>
</div>
<button>Click</button>

To reproduce.

  • Use Chrome;
  • turn Highlight repaints on, in the dev tools;
  • open the Layers panel;
  • click the yellow button to toggle the size of the red square;
  • see what is highlighted as "repainted", as well as the current layers;
  • try doing it after changing the position of the red square too.

We have two squares, a red one and a blue one, inside a wrapper element. In Chrome's Layers panel this whole thing shows as one layer.

  • If both squares are static positioned, when I transition the width and height of the red one, I can see that the whole layer gets repainted, which, to me, makes sense, since, if all 3 elements are in the same layer, changing the dimensions of one, changes the whole layer's end result, so the whole layer has to be repainted.

  • If I set the red square to absolute positioning (you can do that by uncommenting the line in the .red style rules), when I transition its width and height, even though the dev tools still show one layer, only the red square, inside that layer, is shown as repainted.

Question.
The second scenario does not make sense to me.

If the two squares and the wrapper element are all in the same layer, shouldn't changing one element affect the whole layer and cause the layer as a whole to repaint, instead of just the red square?

Additional questions.
Does Chrome, during the layout phase (or whatever phase it is that determines the layers) separate some elements into their own layers (due to position properties for example)? Is that why it is able to repaint them separately? Does it, after the compositing phase, dump them, so the developer only sees one layer in the dev tools?

Related background.
My rough understanding of the painting process of modern browsers is as follows:

  • During the layout phase, the browser combines CSS with HTML information in order to figure out positions and dimensions of visual elements on screen. I also believe this is the time where it determines how many render layers are there (due to will-change for example).
  • During the paint phase, the browser makes a framebuffer for each layer, containing information about what color each pixel should display, based on the layout data.
  • During the compositing phase, the GPU composites all the layers together, based on the data generated during the paint phase, and sends the final result to the screen.
like image 417
Dimitris Karagiannis Avatar asked Mar 05 '18 00:03

Dimitris Karagiannis


People also ask

How to turn on paint flashing in Chrome?

Chrome DevTools Paint flashingPress Command+Shift+P (Mac) or Control+Shift+P (Windows, Linux) to open the Command Menu. Start typing Rendering in the Command Menu and select Show Rendering. In the Rendering tab enable Paint flashing.

How to see paint in Chrome?

Open the Rendering tab and then enable Paint Flashing. With this option switched on Chrome will flash the screen green whenever painting happens. If you're seeing the whole screen flash green, or areas of the screen that you didn't think should be painted, then you should dig in a little further.

How to see layers in Chrome?

Fortunately, there is a tool that makes repaints spotting easier — Layers panel in Chrome Dev Tools. To reveal the panel you need to open a customisation menu in Chrome Dev Tools and in “More Tools” choose “Layers” option.


1 Answers

So, the confusing bit here was the fact that the Paint flashing of the dev tools, only flashes the part of the layer that gets invalidated from the previous frame (so if one absolute positioned square starts getting smaller, the invalidated area between frames is an area with the dimensions and coordinates that the square had in the previous frame)

However, internally the whole layer gets repainted, no matter how big or small the invalidated parts are between frames.

So, for example, a blinking cursor will look small on Paint Flashing, but in reality the entire layer needs to be repainted.

Indeed, if we open up the Performance panel and enable the Advanced Painting Instrumentation option, we can see that between the square transitions the whole layer gets painted in both scenarios described in the question.

Chrome Paint analysis 1

Chrome Paint analysis 2

Sources

https://twitter.com/paul_irish/status/971196975013027840
https://twitter.com/paul_irish/status/971196996924030977
https://twitter.com/paul_irish/status/971197842713800704

Some observations

If we were to minimise the Layout and Painting steps to make as few operations as possible we should separate the red square and the yellow button to their own render layers.

This way interacting with the button and resizing the square will only affect their respective layers and will not cause a repaint to the background layer (which includes the background and the blue square).

like image 104
Dimitris Karagiannis Avatar answered Nov 29 '22 12:11

Dimitris Karagiannis