Hasta hace poco, trabajé en el equipo de Yandex.Browser e hice una presentación en la conferencia de YaTalks a raíz de esta experiencia. El informe trataba sobre lo que el navegador tiene debajo del capó y cómo sus páginas se convierten en píxeles en la pantalla. Frontend mínimo, solo el interior del navegador, solo hardcore.

- Hola a todos, mi nombre es Kostya. Sorprendentemente, ahora trabajo en el equipo de la red virtual Yandex.Cloud. Antes de eso, había trabajado en el equipo del navegador durante más de cinco años, por lo que hoy hablaré sobre cosas que son comunes para nosotros y para usted.
Como puede suponer, no entiendo muy bien en la parte delantera. Si me hablas de React o de algo así, probablemente no te entienda. Pero hice muchas cosas en el navegador: decodificación de video, lógica de negocios. Incluyendo que pasé mucho tiempo haciendo diferentes cosas al renderizar el navegador. Hoy tendremos, por ejemplo, un programa educativo sobre el dispositivo interno del navegador. Intentaré pasar por las cosas más interesantes que hicimos en Yandex.Browser o Google en Chromium.

Si hablamos de renderizar en el navegador, entonces esto es algo muy complicado, que consiste en una gran cantidad de componentes. En primer lugar, debe descargar recursos para mostrarlos. Luego, debe analizarlos, crear un árbol DOM, estilos, diseños, etc. Es probable que los primeros tres puntos le sean familiares. Mi informe se dedicará más a las otras tres partes: pintura, rasterización y composición: lo que sucede debajo del capó cuando ya ha escrito el diseño. Solo en palabras puede parecer que se trata de lo mismo; de hecho, estos son componentes completamente diferentes.


Comencemos con la pintura y la composición. ¿De qué se trata todo esto? Volvamos hace muchos, muchos años atrás, cuando la web no era tan compleja como lo es ahora, cuando no había todo tipo de animaciones 3D, CSS y otras cosas. ¿Cómo dibujó entonces el navegador? Imagina que tienes tu página, algunos elementos maravillosos, imágenes, etc. El navegador pintó todo esto en una textura fuerte, en un gran bloque de memoria. Sabía cómo dibujar cada elemento, y si tuvimos algún cambio, se resaltan en amarillo, sucedió algo como lo siguiente.
El navegador los agregó en áreas que se indican aquí en azul. Ha habido un cambio en esta área, volvamos a dibujarlo. Todo en esta área fue simplemente rediseñado y copiado a la textura.
Funcionó por sí mismo. Luego, a las personas inteligentes se les ocurrió la animación CSS 3D, otras cosas. Podríamos tener muchas representaciones en diferentes lugares. Si giramos una ruleta, volver a dibujar todo el pastel de elementos que se encuentran debajo no es muy eficaz.

Luego, otras personas inteligentes decidieron rehacer todo un poco. Tenemos algún tipo de árbol DOM, lo construimos en la memoria. Estos son objetos más, se comparan con el diseño que escribió.

Y entonces la magia comienza a suceder. El navegador convierte todo el árbol DOM en un árbol Render Object. Esta cosa sabe cómo dibujar cada elemento DOM específico. Es decir, ella sabe lo que hay que hacer para que algo aparezca en la pantalla en lugar de su árbol o elemento P.

El siguiente árbol es el árbol de capas. Que es esto Cada uno de nuestros elementos puede asociarse con cualquier capa, y una capa de renderizado puede contener varios objetos a la vez. ¿Por qué se hace esto? Esto se muestra muy bien aquí. Generamos un conjunto de capas, cada una de ellas tiene ciertos elementos. Ahora, supongamos que algo cambia en una de las capas: se produce una animación, un elemento de sello vuela. Luego redibujamos solo una capa, y el resto, por ejemplo el fondo, permanece sin cambios. Luego, simplemente los pegamos en compuestos y en la salida obtenemos la imagen final: el cuadro actual de la animación.

Hay un conjunto fijo de razones para crear nuevas capas. Por ejemplo, se crea una capa para representar la animación CSS 3D, un lienzo, un elemento de video en él; en general, algo relacionado con la animación pesada, adecuada para volver a dibujar por separado de todo el otro contenido.

Pero este enfoque tiene varios problemas. Ahora necesita encender el combustible. ¿Piensa en lo que se representará aquí? Solo hay dos elementos.

Aquí Aunque, al parecer, los elementos se ubican uno por uno. ¿Por qué vuela el lienzo? En nuestro país, están ordenados secuencialmente; no puse ningún orden.

Vamos a complicarnos Aquí tenemos otro div, como este.

En general, el comportamiento esperado. Tenemos un div encima del div, pero el lienzo está por alguna razón encima. La magia! Ok, vamos a complicar este ejemplo nuevamente.

Exactamente una línea ha cambiado, agregué transform.

Y ahora tenemos todo ubicado correctamente, al menos en términos de lienzo y div. Pero este div todavía se encuentra debajo, aunque fue el siguiente elemento en nuestro diseño.
Este es el llamado error de composición fundamental. Si busca el rastreador de cromo, verá un montón de errores que están vinculados a uno antiguo. Él se llama así.

Entonces que paso? Como dije, algunos elementos se representan en la Capa de renderizado, otros no. Algunos se unen con otros. Aquí sucedió lo siguiente: los elementos div permanecen en la misma capa que el fondo. El lienzo se estrella en una capa separada. Y el orden z se lleva a cabo solo entre capas. Debido al hecho de que tenemos un fondo y un div en una capa y un lienzo en otra, obtenemos un error: el lienzo se superpone al div.
Pero tan pronto como movemos este elemento div a una capa separada y comienza a usar el orden z normalmente, también comienza a comprender quién está detrás de quién. Y aquí todo se vuelve "normal".

Y una de las últimas iniciativas que se ha desarrollado durante varios años es la llamada pintura adelgazante, que debería solucionarlo. Su significado es que necesitamos separar la pintura del dibujo en capas, es decir, entender lo que se debe hacer para dibujar estos elementos, de cómo componerlos entre sí. Si tenemos un diseño tan simple, se convierte en algo así. Hay una lista simple de comandos que debe hacer para obtener el contenido de la página. Y si volvemos a este ejemplo, se verá más o menos así.


Es decir, dijimos: aquí está Paint para ti, aquí está el contenido para ti, por favor dame algo. Proporciona una lista para la representación, que va a Compositor, y Compositor comprende cómo dividir todo el contenido en capas para que normalmente estén ubicadas una en relación con la otra.
Y si no lo has notado, esta es una captura de pantalla de Chrome. Lo logré hace aproximadamente dos semanas, es decir, el error aún está vivo. El proyecto aún no está terminado; ahora está en proceso de desarrollo.


Es decir, el Compositor de esta lista y algunos conocimientos secretos que los pases de plink pueden comprender cómo colocarlo correctamente en capas.


Además del hecho de que este enfoque básicamente solucionará este error, también obtenemos cambios bastante baratos en la representación. Supongamos que tenemos una lista de comandos de dibujo y se producen cambios; digamos que el elemento B se va, el elemento E se agrega. Entonces podemos simplemente mantener las dos listas juntas sin preocuparnos por los árboles, etc. En la salida obtenemos una nueva lista de elementos para renderizar y, posiblemente, una nueva lista de capas que se compilará en el futuro.
Esta fue una historia corta sobre lo que sucede cuando el navegador implementa Paint, y lo que sucede después de que intenta componer capas.

Pasemos a otro tema: la rasterización. Solo en Rasterización en Yandex.Browser, se hicieron muchas cosas, y también hice esto. ¿Cuál es el significado de la rasterización? En la salida de la etapa anterior, cuando hicimos Paint, hay una lista de comandos que debemos implementar para obtener algún tipo de imagen. La rasterización es la transformación de una lista de comandos en píxeles reales.


Si abre la pestaña Más herramientas → Representación en el inspector del navegador, entonces hay una marca de verificación Bordes de capa. Y ves exactamente esa grilla. ¿Qué está pasando aquí? Cuando el navegador dibuja la página, en realidad no hace todo ahora. El navegador toma y divide cada capa en un cierto número de cuadrados tan pequeños. Históricamente, tienen un tamaño de 256 por 256 píxeles. Es decir, cada capa se divide en tantas texturas separadas. El contenido del mosaico actual se dibuja sobre cada textura, y luego se pegan en una textura grande.

Ayuda mucho En primer lugar, podemos volver a dibujar solo las fichas que han cambiado. Pero también nos permite priorizar el renderizado. Es decir, en primer lugar, se deben dibujar los mosaicos que ve el usuario, la llamada ventana gráfica. Luego tenemos que dibujar pronto el borde, esto es lo que está alrededor de la ventana gráfica. La siguiente es la dirección del desplazamiento: si se desplaza hacia abajo, dibujamos tanto como sea posible. Si está arriba, dibuja tanto como sea posible. Si todavía tenemos una cuota de memoria, sacaremos algo más, pero no el hecho de que permanecerá en este punto.

Entonces obtenemos una actualización de contenido bastante barata en la página. Supongamos que tomamos el marco actual y el usuario deshabilitó algo, por ejemplo, texto resaltado. Luego dibujamos solo las fichas que han cambiado.

Es decir, los mosaicos verdes son los que quedan de la representación anterior, los rojos que redibujamos. Pero este enfoque tiene otras ventajas.

Podemos hacer, y Chrome lo hizo, la llamada optimización de pequeños redibujos. Suponga que tiene algún tipo de throbber, cursor o algo así haciendo un pequeño rediseño en un pequeño rectángulo. Entonces no necesitamos volver a dibujar todo el cuadrado. Esto es lógico. Por ejemplo, si nuestro cursor parpadea, solo se vuelve a dibujar. Esto ahorra mucho la CPU.

La próxima optimización que han hecho. ¿Dónde puede haber ineficiencia? ¿Han cambiado las fichas? Buena idea, pero tiendo a otra. Aquí hay solo un rectángulo blanco. Este es un mosaico blanco en el que no se dibuja un solo píxel. Pero es una textura. Ocupa 256 por 256 por cuatro bytes de memoria.

Otra optimización que se inventó en Chrome: y tomemos estos mosaicos, de un color, y codifíquelos no con un montón de píxeles, sino, de hecho, con los coordinadores, el tamaño y el color. Internet ahora está lleno de páginas con muchas áreas monocromáticas para monitores grandes. Y tales páginas se optimizan en consecuencia, obtenemos muchos ahorros de memoria.
Nosotros en Yandex fuimos un poco más lejos y decidimos hacer un experimento más específico. ¿Dónde crees que puedes ahorrar más?
Tenemos un azulejo El contenido se encuentra en un área pequeña: una tira, la palabra Yandex. ¿Por qué necesitamos dibujar en su conjunto, si el contenido es muy pequeño y todo lo demás es monocromo?

Que hemos hecho Esto es lo que hice específicamente. Dividimos cada mosaico en cinco mosaicos. Si el contenido está solo en el medio, entonces seleccionamos la textura para este contenido solo para lo que se hace en el medio. Aquí está el área roja. Todo lo demás lo codificamos de la misma manera: tamaño, coordenadas y color.

Es decir, específicamente en esta página, todas estas áreas se han convertido en no textura. No se utilizan bytes en la memoria, sino simplemente comandos sobre lo que necesitamos dibujar aquí, rellenar con un color. Esto nos dio un ahorro promedio de alrededor del 40% en la memoria de la GPU por usuario.

En páginas más complejas, se ve así. Dado que las páginas más complejas usan más capas, y cada capa es un mosaico separado, puede ahorrar un poco en cualquier capa.
Si habilita esta marca de verificación ahora, no verá una cuadrícula de rectángulos, sino esta.

¿Qué es? ¿Por qué las baldosas son tan anchas aquí y por qué hay pocas? El significado aquí es el siguiente. En Chrome, pensaron: ¿por qué no hacemos no solo la composición de hardware, sino también la representación de hardware? Que estan haciendo Tenemos una lista de comandos sobre lo que hay que hacer: dibujar un rectángulo, rellenar con color, etc. Todo esto va a la GPU, y la GPU dibuja dicha textura. El redibujado es muy rápido, por lo que los mosaicos también se pueden hacer grandes. Aquí hay un pequeño
video de chacal, pero muestra muy bien la ventaja que sucedió en los teléfonos debido al hecho de que el renderizado se aceleró por hardware. Creo que la diferencia aquí es muy, muy notable.
La comunicación entre los desarrolladores de navegadores y los proveedores frontales me parece muy útil. No sucede con mucha frecuencia, pero brinda muchos beneficios. Por lo tanto, cuando nuestros colegas de otros departamentos acuden a nosotros y nos preguntan cómo hacer que el diseño sea más rápido y mejor, intentamos ayudarlos y hablar sobre lugares donde algo no es óptimo y usted puede acelerar.
Y no me cansaré de repetir mi consejo. (No voy a citar aquí, hubo un
gran informe por separado sobre este tema. - Comentario del autor).
Aquí he compilado un conjunto de enlaces útiles, sobre renderizado y no solo, sino también un poco sobre Yandex.Browser. Gracias