Procesamiento acelerado en Chrome

El modelo por capas

Tom Wiltzius
Tom Wiltzius

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, desconocido para convertir esta representación de una página en una imagen en pantalla. En los últimos años, los navegadores modernos cambiaron la forma en que funciona la renderización para aprovechar las tarjetas gráficas. Esto a menudo se denomina "aceleración de hardware". Cuando se habla de una página web normal (es decir, no Canvas2D o WebGL), ¿qué significa realmente ese término? En este artículo, se explica el modelo básico que respalda la renderización acelerada por hardware del contenido web en Chrome.

Advertencias

Aquí hablamos de WebKit y, más específicamente, del puerto de WebKit para Chromium. En este artículo, se abordan los detalles de implementación de Chrome, no de 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 este artículo se aplique a otros navegadores, pero el conocimiento de los componentes internos puede ser útil para la depuración avanzada y el ajuste del rendimiento.

Además, ten en cuenta que en este artículo completo se analiza una pieza central de la arquitectura de renderización de Chrome que está cambiando muy rápido. En este artículo, se intenta cubrir solo cuestiones que probablemente no cambien, pero no se garantiza que se aplicarán de todas formas en seis meses.

Es importante comprender que Chrome tiene dos rutas de renderización diferentes desde hace un tiempo: la ruta de acceso acelerada por hardware y la ruta de acceso del software más antigua. A partir de este momento, todas las páginas siguen la ruta acelerada por 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 (a continuación, puedes obtener más información sobre lo que requiere composición), pero pronto todas las páginas seguirán la ruta acelerada allí también.

Por último, examinaremos en detalle el motor de renderización y analizaremos 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 tomarte el pie: las capas son construcciones útiles, pero crear muchas de ellas puede generar sobrecarga en toda la pila de gráficos. ¡Presta atención!

Del DOM a la pantalla

Presentamos las Capas

Una vez que una página se carga y analiza, se representa en el navegador como una estructura con la que muchos desarrolladores web ya están familiarizados: el DOM. Sin embargo, cuando se renderiza una página, el navegador tiene una serie de representaciones intermedias que no están expuestas directamente a los desarrolladores. La más importante de estas estructuras es una capa.

En Chrome, hay varios tipos diferentes de capas: RenderLayers, que se encargan de los subárboles del DOM, y GraphicsLayers, que se encargan de los subárboles de RenderLayers. Esta última opción nos resulta más interesante, ya que las GraphicsLayers son las que se cargan a la GPU como texturas. De aquí en adelante, simplemente diré "capa" para referirse a GraphicsLayer.

Información breve sobre la terminología de las GPU: ¿qué es una textura? Considéralo como una imagen de mapa de bits que se traslada de la memoria principal (es decir, la RAM) a la memoria de video (es decir, la VRAM de tu GPU). Una vez que está en la GPU, puedes asignarla a una geometría de malla. En videojuegos o programas de CAD, esta técnica se utiliza para dar "máscara" a los modelos 3D esqueléticos. Chrome usa texturas para obtener fragmentos de contenido de páginas web en la GPU. Es más económico asignar texturas a diferentes posiciones y transformaciones aplicándolas en una malla rectangular muy simple. Así es como funciona 3D CSS, y también es excelente para un desplazamiento rápido, pero hablaremos sobre ambos casos más adelante.

Veamos algunos ejemplos para ilustrar el concepto de capa.

Una herramienta muy útil cuando se estudian capas en Chrome es la marca "mostrar bordes de capas compuestas" que se encuentra en la configuración (es decir, pequeño ícono de engranaje) en las Herramientas para desarrolladores, debajo del encabezado "Renderización". Simplemente resalta dónde están las capas en la pantalla. Vamos a activarlo. Estas capturas de pantalla y ejemplos corresponden a la versión más reciente de Chrome Canary, Chrome 27 al momento de la redacción de este documento.

Figura 1: Una página de una sola capa

<!doctype html>
<html>
<body>
  <div>I am a strange root.</div>
</body>
</html>
Captura de pantalla de los bordes de renderización de la capa compuesta alrededor de la capa base de la página
Captura de pantalla de los bordes de renderización de la capa compuesta alrededor de la capa base de la página.
.

Esta página tiene solo una capa. La cuadrícula azul representa mosaicos, que se entienden como subunidades de una capa que Chrome usa para subir partes de una capa grande a la vez a la GPU. Aquí no son muy importantes.

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>
Captura de pantalla de los bordes de renderización de la capa rotada
Captura de pantalla de los bordes de renderización de la capa rotada
.

Si colocas una propiedad 3D CSS en la <div> que la rota, podemos ver cómo se ve cuando un elemento obtiene su propia capa. Observa el borde naranja, que delinea una capa en esta vista.

Criterios de creación de capas

¿Qué más obtiene su propia capa? La heurística de Chrome ha evolucionado con el tiempo y continúa haciéndolo, pero, actualmente, cualquiera de las siguientes opciones de creación de capas de activador:

  • Propiedades de CSS de transformación de perspectiva o 3D
  • <video> elementos con la decodificación acelerada de video
  • Elementos <canvas> con un contexto 3D (WebGL) o contexto 2D acelerado
  • Complementos compuestos (p.ej., Flash)
  • Elementos con animación CSS para su opacidad o con una transformación animada
  • Elementos con filtros CSS acelerados
  • El elemento tiene un elemento subordinado con 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 del mismo nivel con un índice z más bajo que tiene una capa compuesta (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 realmente útiles para desplazarse por el 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 bueno: volver a pintar lleva tiempo que se puede dedicar a otras cosas, como ejecutar JavaScript, y si la pintura es larga, provoca enganches o retrasos en las animaciones.

Por ejemplo, puedes observar esta vista del cronograma de las Herramientas para desarrolladores: no hay operaciones de pintura mientras esta capa rota hacia adelante y hacia atrás.

Captura de pantalla del cronograma de Herramientas para desarrolladores durante la animación
Captura de pantalla del cronograma de las Herramientas para desarrolladores durante la animación

No válido Volver a pintar

Pero 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 hace 1 px más ancho. Esto provoca un rediseño y una nueva imagen de 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" de las Herramientas para desarrolladores, también debajo del encabezado "Renderización" de la configuración de las Herramientas para desarrolladores. Después de encenderlo, observa que el elemento animado y el botón parpadean en rojo al hacer clic en él.

Captura de pantalla de la casilla de verificación Mostrar rectángulos de pintura
Captura de pantalla de la casilla de verificación Mostrar rectángulos de pintura

Los eventos de pintura también aparecen en el cronograma de las Herramientas para desarrolladores. Los lectores sabrosos pueden notar que hay dos eventos de pintura allí: uno para la capa y otro para el botón en sí, que se vuelve a pintar cuando cambia hacia o desde el estado de presión.

Captura de pantalla de la línea de tiempo de Herramientas para desarrolladores que vuelve a pintar una capa
Captura de pantalla de la línea de tiempo de Herramientas para desarrolladores que vuelve a renderizar una capa.

Ten en cuenta que Chrome no siempre necesita volver a pintar toda la capa; 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. Pero 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 responderlo de forma exhaustiva porque hay muchos casos extremos que pueden forzar invalidaciones. La causa más común es ensuciar el DOM mediante la manipulación de estilos de CSS o la modificación del 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 abarca la pintura con más detalle (pero termina solo con pintura, no con este material de composición elegante).

La mejor manera de determinar si afecta a algo en lo que estás trabajando es usar las herramientas Cronograma de Herramientas para desarrolladores y Mostrar rectángulos de pintura para ver si vuelves a pintar cuando deseas que no lo hayas hecho. Luego, intenta identificar dónde ensuciaste el DOM justo antes de volver a diseñar o 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 Herramientas para desarrolladores.

Reunir todos los elementos: DOM a pantalla

Entonces, ¿cómo convierte Chrome el DOM en una imagen de pantalla? De forma conceptual, sucede lo siguiente:

  1. Toma el DOM y lo divide en capas.
  2. Pinta cada una de estas capas de forma independiente en mapas de bits de software.
  3. Las sube a la GPU como texturas.
  4. Combina las diversas capas en la imagen de pantalla final.

Todo debe suceder la primera vez que Chrome genera un marco de una página web. Sin embargo, puede tomar algunos atajos para los marcos futuros:

  1. Si cambian ciertas propiedades de la CSS, no es necesario volver a pintar nada. Chrome solo puede componer las capas existentes que ya se encuentran en la GPU como texturas, pero con diferentes propiedades de composición (p. ej., en diferentes posiciones, con diferentes opacidades, etc.).
  2. Si parte de una capa se invalida, esta se vuelve a pintar y a subir. Si su contenido sigue siendo el mismo, pero cambian los atributos compuestos (p.ej., se traduce o cambia su opacidad), Chrome puede dejarlo en la GPU y recomponerlo para crear un nuevo fotograma.

Como ahora debería quedar 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 renderización. Los desarrolladores experimentados verán la lista de activadores de composición que aparece más arriba y se darán cuenta de que es posible forzar fácilmente la creación de capas. Pero ten cuidado de crearlas a ciegas, ya que no son libres: consumen memoria en la RAM del sistema y en la GPU (particularmente limitada en los dispositivos móviles) y tener muchas de ellas puede generar otras sobrecargas en la lógica que hagan un seguimiento de las que son visibles. En realidad, muchas capas también pueden aumentar el tiempo dedicado a la rasterización si son grandes y se superponen mucho donde antes no se mostraban, lo que genera lo que a veces se denomina "superposición". Así que usa tu conocimiento sabiamente.

Eso es todo por ahora. No te pierdas algunos artículos más sobre las implicaciones prácticas del modelo de capas.

Recursos adicionales