Procesamiento acelerado en Chrome

El modelo de 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, con frecuencia, sombrío para convertir esta representación de una página en una imagen en la pantalla. Los navegadores modernos han cambiado la forma en que funciona la renderización en los últimos años para aprovechar las tarjetas gráficas: esto a menudo se conoce como “aceleración de hardware”. Cuando se habla de una página web normal (es decir, no de Canvas2D o WebGL), ¿qué significa realmente ese término? En este artículo, se explica el modelo básico que respalda el procesamiento acelerado por hardware de contenido web en Chrome.

Limitaciones grandes y grasas

Aquí estamos hablando de WebKit y, más específicamente, nos referimos 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 nada en este artículo se aplique a otros navegadores. Sin embargo, el conocimiento de aspectos internos puede ser útil para la depuración y el ajuste de rendimiento avanzados.

Además, ten en cuenta que todo este artículo trata sobre una pieza fundamental de la arquitectura de procesamiento de Chrome que está cambiando muy rápido. En este artículo, se trata solo de temas que probablemente no se modificarán, pero no hay garantía de que siga vigente en seis meses.

Es importante comprender que Chrome tiene dos rutas de procesamiento diferentes desde hace un tiempo: la ruta de acceso acelerada por hardware y la ruta de software anterior. En el momento en que se escribe este documento, todas las páginas recorren 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 (consulta a continuación lo que requeriría composición), pero pronto todas las páginas también pasarán por la ruta acelerada.

Por último, estamos ahondando en el motor de renderización y analizando las funciones que tienen un gran impacto en el rendimiento. Al tratar de mejorar el rendimiento de tu propio sitio, puede resultar útil entender el modelo de capas, pero también es fácil grabarte con los pies: las capas son construcciones útiles, pero crear muchas de ellas puede generar sobrecarga en toda la pila de gráficos. ¡Preséntate!

Del DOM a la pantalla

Presentamos las capas

Una vez que se carga y analiza una página, se representa en el navegador como una estructura que muchos desarrolladores web conocen: DOM. Sin embargo, cuando se 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 realidad, en Chrome existen diferentes tipos de capas: RenderLayers, que son responsables de los subárboles del DOM, y GraphicsLayers, que son responsables de los subárboles de RenderLayers. Esto último nos resulta más interesante en este caso, ya que los GraphicsLayers son los que se suben a la GPU como texturas. De aquí en adelante, solo diré “capa” para referirse a GraphicsLayer.

Una aclaración breve sobre la terminología de la GPU: ¿Qué es una textura? Considéralo una imagen de mapa de bits que se traslada de la memoria principal (es decir, la RAM) a la memoria de video (es decir, VRAM, en tu GPU). Una vez que está en la GPU, puedes asignarla a una geometría de malla: en los videojuegos o programas de CAD, esta técnica se usa para dar "piel" a los modelos 3D esqueléticos. Chrome usa texturas para colocar fragmentos de contenido de la página web en la GPU. Las texturas se pueden asignar de forma económica a diferentes posiciones y transformaciones si se las aplica 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 sobre ambos temas más adelante.

Veamos algunos ejemplos para ilustrar el concepto de capas.

Una herramienta muy útil al estudiar capas en Chrome es la función experimental "mostrar bordes de capa compuesta" en la configuración (es decir, ícono de engranaje pequeño) en Herramientas para desarrolladores, debajo del encabezado "Renderización". Simplemente destaca dónde se encuentran las capas en la pantalla. Vamos a activarlo. Todas estas capturas de pantalla y ejemplos se toman de la versión más reciente de Chrome Canary, Chrome 27 en el momento en que se redactó este documento.

Figura 1: 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 solo tiene una capa. La cuadrícula azul representa las tarjetas, que se pueden considerar como subunidades de una capa que Chrome utiliza para subir partes de una capa grande por vez a la GPU. No son muy importantes en este caso.

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 de CSS en el elemento <div> que la rota, podemos ver cómo se ve cuando un elemento tiene 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? En este caso, la heurística de Chrome evolucionó con el tiempo y sigue haciéndolo, pero actualmente se puede crear cualquiera de las siguientes capas de activación:

  • Propiedades de CSS de transformación 3D o de perspectiva
  • Elementos <video> con decodificación de video acelerada
  • Elementos <canvas> con un contexto 3D (WebGL) o un contexto 2D acelerado
  • Complementos compuestos (es decir, Flash)
  • Elementos con animación CSS para su opacidad o con una transformación animada
  • Elementos con filtros de CSS acelerados
  • El elemento tiene un elemento subordinado que tiene una capa compuesta (es decir, si el elemento tiene un elemento secundario que se encuentra 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 muy ú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 un tiempo que se puede dedicar a otras tareas, como ejecutar JavaScript. Si la pintura es larga, provoca problemas o retrasos en las animaciones.

Por ejemplo, consulta esta vista de la línea de tiempo de Herramientas para desarrolladores: no hay operaciones de pintura mientras esta capa rota hacia adelante y 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 las 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 vuelve más ancho en 1 px. De esta manera, se rediseña y se vuelve a pintar todo el elemento, que en este caso es una capa completa.

Una buena manera de ver lo que se está pintando es con la herramienta “mostrar rectángulos de pintura” en Herramientas para desarrolladores, también bajo el encabezado “Renderización” de la configuración de Herramientas para desarrolladores. Después de activarlo, observa que el elemento animado y el botón parpadean en rojo cuando se hace clic en el botón.

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 Herramientas para desarrolladores. Los lectores de ojos agudos podrían 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 a su estado deprimido.

Captura de pantalla del cronograma de herramientas para desarrolladores en el que se vuelve a pintar una capa
Captura de pantalla de la línea de tiempo de las herramientas para desarrolladores en la que se pinta una capa nuevamente

Ten en cuenta que Chrome no siempre necesita volver a pintar toda la capa, sino que intenta aplicar el cuidado necesario para volver a pintar solamente la parte del DOM invalidada. En este caso, el elemento del DOM que modificamos tiene el tamaño de toda la capa. Sin embargo, en muchos otros casos, habrá muchos elementos del DOM en una capa.

Una pregunta obvia es qué causa una invalidación y fuerza una nueva pintura. 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 CSS o la modificación del diseño. Tony Gentilcore tiene una excelente entrada de blog sobre lo que causa el rediseño, y Stoyan Stefanov tiene un artículo que abarca la pintura con más detalle (pero termina con la pintura, no con esta composición sofisticada).

La mejor manera de saber si afecta a algo en lo que estás trabajando es usar las herramientas de cronograma de Herramientas para desarrolladores y Mostrar rectángulos de pintura para ver si estás volviendo a pintar cuando quisieras que no fuera así. Luego intenta identificar en qué parte del DOM ensuciaste el DOM justo antes de rediseñar o volver a pintar. Si la pintura es inevitable, pero parece llevar mucho tiempo, consulta el artículo de Eberhard Gräther sobre el modo de pintura continua en las Herramientas para desarrolladores.

Revisión general: DOM a pantalla

Entonces, ¿cómo convierte Chrome el DOM en una imagen en pantalla? De forma conceptual, hace 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 la pantalla final.

Todo esto tiene que ocurrir la primera vez que Chrome genera un marco de una página web. Sin embargo, puede usar algunos atajos para fotogramas futuros:

  1. Si determinadas propiedades de CSS cambian, no es necesario volver a pintar nada. Chrome puede simplemente recomponer 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 se invalida parte de una capa, se vuelve a pintar y se vuelve a subir. Si su contenido permanece igual, pero sus atributos compuestos cambian (p.ej., se traduce o cambia su opacidad), Chrome puede dejarlo en la GPU y recomponerlo para crear un nuevo fotograma.

Como debería quedar claro, el modelo compuesto basado en capas tiene grandes implicaciones en el rendimiento de la renderización. La composición es comparativamente económica cuando no se necesita pintar nada, por lo que evitar repetir el procesamiento de las capas es un buen objetivo general cuando se intenta depurar el rendimiento de la renderización. Los desarrolladores experimentados revisarán la lista de activadores de composición anterior y se darán cuenta de que es posible forzar la creación de capas con facilidad. Pero ten cuidado con crearlos a ciegas, ya que no son libres: ocupan memoria en la RAM del sistema y en la GPU (especialmente limitado en los dispositivos móviles) y tener muchos de ellos puede generar otra sobrecarga en la lógica y hacer un seguimiento de cuáles son visibles. Muchas capas también pueden aumentar el tiempo dedicado a la rasterización, si las capas son grandes y se superponen mucho donde antes no lo hacían, lo que a veces se conoce como “superposición”. ¡Usa tus conocimientos con prudencia!

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

Recursos adicionales