El modelo de capas
Introducción
Para la mayoría de los desarrolladores web, el modelo fundamental de una página web es el DOM. La renderización es el proceso, a menudo poco claro, de convertir esta representación de una página en una imagen en la pantalla. En los últimos años, los navegadores modernos cambiaron la forma en que funciona el renderizado para aprovechar las tarjetas gráficas. Esto suele denominarse de forma vaga como “aceleración de hardware”. Cuando se habla de una página web normal (es decir, que no es Canvas2D ni WebGL), ¿qué significa realmente ese término? En este artículo, se explica el modelo básico que sustenta la renderización acelerada por hardware del contenido web en Chrome.
Advertencias importantes
Aquí nos referimos a WebKit y, más específicamente, al puerto de Chromium de WebKit. En este artículo, se abordan los detalles de implementación de Chrome, no las funciones de la plataforma web. La plataforma web y los estándares no codifican este nivel de detalle de implementación, por lo que no hay garantías de que lo que se menciona en este artículo se aplique a otros navegadores. Sin embargo, el conocimiento de los aspectos internos puede ser útil para la depuración avanzada y el ajuste del rendimiento.
Además, ten en cuenta que todo este artículo analiza una parte fundamental de la arquitectura de renderización de Chrome que está cambiando muy rápido. En este artículo, se intenta abarcar solo los aspectos que es poco probable que cambien, pero no se garantiza que todo siga aplicándose en seis meses.
Es importante comprender que, desde hace un tiempo, Chrome tiene dos rutas de renderización diferentes: la ruta acelerada por hardware y la ruta de software más antigua. En el momento de escribir este artículo, todas las páginas siguen la ruta de aceleración de hardware en Windows, ChromeOS y Chrome para Android. En Mac y Linux, solo las páginas que necesitan composición para parte de su contenido siguen la ruta acelerada (consulta más abajo para obtener más información sobre lo que requeriría composición), pero pronto todas las páginas también seguirán la ruta acelerada allí.
Por último, analizaremos el motor de renderización y sus funciones que tienen un gran impacto en el rendimiento. Cuando intentas mejorar el rendimiento de tu propio sitio, puede ser útil comprender el modelo de capas, pero también es fácil cometer errores: las capas son construcciones útiles, pero crear muchas de ellas puede generar sobrecarga en toda la pila de gráficos. ¡Ya te lo advertimos!
Del DOM a la pantalla
Presentación de las capas
Una vez que se carga y analiza una página, se representa en el navegador como una estructura con la que muchos desarrolladores web están familiarizados: el DOM. Sin embargo, cuando renderiza una página, el navegador tiene una serie de representaciones intermedias que no se exponen directamente a los desarrolladores. La más importante de estas estructuras es una capa.
En Chrome, hay varios tipos de capas: RenderLayers, que son responsables de los subárboles del DOM, y GraphicsLayers, que son responsables de los subárboles de RenderLayers. Este último es el más interesante para nosotros, ya que GraphicsLayers son los que se suben a la GPU como texturas. De aquí en adelante, solo diré “capa” para referirme a GraphicsLayer.
Un breve comentario sobre la terminología de la GPU: ¿Qué es una textura? Piensa en ella como una imagen de mapa de bits que se mueve de la memoria principal (es decir, la RAM) a la memoria de video (es decir, la VRAM, en la GPU). Una vez que está en la GPU, puedes asignarla a una geometría de malla. En los videojuegos o los programas de CAD, esta técnica se usa para darles "piel" a los modelos 3D esqueléticos. Chrome usa texturas para llevar fragmentos de contenido de páginas web a la GPU. Las texturas se pueden asignar de forma económica a diferentes posiciones y transformaciones aplicándolas a una malla rectangular muy simple. Así es como funciona el CSS 3D, y también es excelente para el desplazamiento rápido, pero hablaremos más sobre ambos temas más adelante.
Veamos un par de ejemplos para ilustrar el concepto de capa.
Una herramienta muy útil para estudiar las capas en Chrome es la marca “show composited layer borders” en la configuración (es decir, el ícono de engranaje pequeño) de las Herramientas para desarrolladores, en el encabezado “rendering”. Destaca de forma muy sencilla dónde se encuentran las capas en la pantalla. Vamos a activarla. Estas capturas de pantalla y ejemplos se tomaron de la versión más reciente de Chrome Canary, Chrome 27, en el momento de escribir este artículo.
Figura 1: Una página de una sola capa
<!doctype html>
<html>
<body>
<div>I am a strange root.</div>
</body>
</html>
Esta página tiene solo una capa. La cuadrícula azul representa las tarjetas, que puedes considerar como subunidades de una capa que Chrome usa para subir partes de una capa grande a la vez a la GPU. No son muy importantes aquí.
Figura 2: Un elemento en su propia capa
<!doctype html>
<html>
<body>
<div style="transform: rotateY(30deg) rotateX(-30deg); width: 200px;">
I am a strange root.
</div>
</body>
</html>
Si colocamos una propiedad de CSS en 3D en el elemento <div> que lo rota, podemos ver cómo se ve cuando un elemento obtiene su propia capa: observa el borde naranja, que delimita una capa en esta vista.
Criterios de creación de capas
¿Qué más tiene su propia capa? La heurística de Chrome aquí ha evolucionado con el tiempo y continúa haciéndolo, pero actualmente cualquiera de los siguientes factores activa la creación de capas:
- Propiedades de CSS de transformación de perspectiva o 3D
- Elementos
<video>que usan la decodificación de video acelerada - Elementos
<canvas>con un contexto 3D (WebGL) o un contexto 2D acelerado - Complementos compuestos (p.ej., Flash)
- Elementos con animación CSS para su opacidad o que usan una transformación animada
- Elementos con filtros CSS acelerados
- El elemento tiene un descendiente que tiene una capa de composición (en otras palabras, si el elemento tiene un elemento secundario que está en su propia capa).
- El elemento tiene un elemento hermano con un índice z más bajo que tiene una capa de composición (en otras palabras, se renderiza sobre una capa compuesta).
Implicaciones prácticas: Animación
También podemos mover las capas, lo que las hace muy útiles para la animación.
Figura 3: Capas animadas
<!doctype html>
<html>
<head>
<style>
div {
animation-duration: 5s;
animation-name: slide;
animation-iteration-count: infinite;
animation-direction: alternate;
width: 200px;
height: 200px;
margin: 100px;
background-color: gray;
}
@keyframes slide {
from {
transform: rotate(0deg);
}
to {
transform: rotate(120deg);
}
}
</style>
</head>
<body>
<div>I am a strange root.</div>
</body>
</html>
Como se mencionó antes, las capas son muy útiles para mover contenido web estático. En el caso básico, Chrome pinta el contenido de una capa en un mapa de bits de software antes de subirlo a la GPU como una textura. Si ese contenido no cambia en el futuro, no es necesario volver a pintarlo. Esto es algo bueno: volver a pintar lleva tiempo que se puede dedicar a otras cosas, como ejecutar JavaScript, y si la pintura es larga, provoca problemas o retrasos en las animaciones.
Por ejemplo, observa esta vista de la línea de tiempo de DevTools: no hay operaciones de pintura mientras esta capa rota hacia adelante y hacia atrás.
No válido Repintado
Sin embargo, si el contenido de la capa cambia, se debe volver a pintar.
Figura 4: Volver a pintar capas
<!doctype html>
<html>
<head>
<style>
div {
animation-duration: 5s;
animation-name: slide;
animation-iteration-count: infinite;
animation-direction: alternate;
width: 200px;
height: 200px;
margin: 100px;
background-color: gray;
}
@keyframes slide {
from {
transform: rotate(0deg);
}
to {
transform: rotate(120deg);
}
}
</style>
</head>
<body>
<div id="foo">I am a strange root.</div>
<input id="paint" type="button" value="repaint">
<script>
var w = 200;
document.getElementById('paint').onclick = function() {
document.getElementById('foo').style.width = (w++) + 'px';
}
</script>
</body>
</html>
Cada vez que se hace clic en el elemento de entrada, el elemento giratorio se ensancha 1 px. Esto provoca que se vuelva a diseñar y pintar todo el elemento, que en este caso es una capa completa.
Una buena manera de ver qué se está pintando es con la herramienta “Mostrar rectángulos de pintura” en Herramientas para desarrolladores, también en el encabezado “Renderización” de la configuración de Herramientas para desarrolladores. Después de activarla, observa que el elemento animado y el botón parpadean en rojo cuando se hace clic en el botón.
Los eventos de pintura también aparecen en el cronograma de Herramientas para desarrolladores. Los lectores atentos pueden notar que hay dos eventos de pintura: uno para la capa y otro para el botón, que se vuelve a pintar cuando cambia de estado.
Ten en cuenta que Chrome no siempre necesita volver a pintar toda la capa, sino que intenta volver a pintar de forma inteligente solo la parte del DOM que se invalidó. En este caso, el elemento del DOM que modificamos es el tamaño de toda la capa. Sin embargo, en muchos otros casos, habrá muchos elementos del DOM en una capa.
La siguiente pregunta obvia es qué causa una invalidación y fuerza un nuevo dibujo. Es difícil responder esto de forma exhaustiva porque hay muchos casos extremos que pueden forzar invalidaciones. La causa más común es que se ensucie el DOM manipulando los estilos CSS o provocando un cambio de diseño. Tony Gentilcore tiene una excelente entrada de blog sobre las causas del cambio de diseño, y Stoyan Stefanov tiene un artículo que aborda la pintura con más detalle (pero termina solo con la pintura, no con esta elegante composición).
La mejor manera de determinar si afecta algo en lo que estás trabajando es usar las herramientas Timeline y Show Paint Rects de DevTools para ver si estás volviendo a pintar cuando no lo deseas y, luego, intentar identificar dónde modificaste el DOM justo antes de ese cambio de diseño o de volver a pintar. Si la pintura es inevitable, pero parece tardar demasiado, consulta el artículo de Eberhard Gräther sobre el modo de pintura continua en las Herramientas para desarrolladores.
Unión de todo: del DOM a la pantalla
¿Cómo convierte Chrome el DOM en una imagen de pantalla? Conceptualmente, hace lo siguiente:
- Toma el DOM y lo divide en capas.
- Pinta cada una de estas capas de forma independiente en mapas de bits de software.
- Las sube a la GPU como texturas.
- Compone las distintas capas en la imagen final de la pantalla.
Todo eso debe suceder la primera vez que Chrome produce un fotograma de una página web. Pero, luego, puede tomar algunos atajos para los fotogramas futuros:
- Si cambian ciertas propiedades de CSS, no es necesario volver a pintar nada. Chrome puede simplemente volver a componer las capas existentes que ya están en la GPU como texturas, pero con diferentes propiedades de composición (p. ej., en diferentes posiciones, con diferentes opacidades, etc.).
- Si se invalida parte de una capa, se vuelve a pintar y a subir. Si su contenido sigue siendo el mismo, pero cambian sus atributos compuestos (p.ej., se traduce o cambia su opacidad), Chrome puede dejarlo en la GPU y volver a componerlo para crear un fotograma nuevo.
Como ya debería estar claro, el modelo de composición basado en capas tiene implicaciones profundas en el rendimiento de la renderización. La composición es comparativamente económica cuando no se necesita pintar nada, por lo que evitar volver a pintar las capas es un buen objetivo general cuando se intenta depurar el rendimiento de la renderización. Los desarrolladores inteligentes consultarán la lista de activadores de composición que se muestra más arriba y se darán cuenta de que es posible forzar fácilmente la creación de capas. Sin embargo, ten cuidado de no crearlas a ciegas, ya que no son gratuitas: ocupan memoria en la RAM del sistema y en la GPU (que es particularmente limitada en los dispositivos móviles), y tener muchas de ellas puede generar otra sobrecarga en la lógica que hace un seguimiento de cuáles son visibles. Muchas capas también pueden aumentar el tiempo dedicado a la rasterización si son grandes y se superponen mucho donde antes no lo hacían, lo que genera lo que a veces se denomina “sobregiro”. Por lo tanto, usa tus conocimientos con prudencia.
Eso es todo por ahora. No te pierdas los próximos artículos sobre las implicaciones prácticas del modelo de capas.