Arquitectura de RenderingNG

Chris Harrelson
Chris Harrelson

Aquí aprenderás cómo se configuran las partes de componentes de RenderingNG y cómo la canalización de renderización fluye a través de ellas.

A partir del nivel más alto, las tareas de renderización son las siguientes:

  1. Renderiza el contenido en píxeles en la pantalla.
  2. Anima efectos visuales en el contenido de un estado a otro.
  3. Desplazamiento en respuesta a la entrada.
  4. Enruta las entradas de manera eficiente a los lugares correctos para que las secuencias de comandos para desarrolladores y otros subsistemas puedan responder.

El contenido que se renderizará es un árbol de marcos para cada pestaña del navegador, además de la interfaz del navegador. así como un flujo de eventos de entrada sin procesar de pantallas táctiles, mouses, teclados y otros dispositivos de hardware.

Cada marco incluye lo siguiente:

  • Estado del DOM
  • CSS
  • Lienzos
  • Recursos externos, como imágenes, videos, fuentes y SVG

Un marco es un documento HTML, más su URL. Una página web cargada en una pestaña del navegador tiene un marco de nivel superior, marcos secundarios para cada iframe incluido en el documento de nivel superior y sus elementos subordinados de iframe recurrentes.

Un efecto visual es una operación gráfica aplicada a un mapa de bits, como desplazamiento, transformación, recorte, filtro, opacidad o combinación.

Componentes de la arquitectura

En RenderingNG, estas tareas se dividen de forma lógica en varias etapas y componentes de código. Los componentes terminan en varios procesos de CPU, subprocesos y subcomponentes dentro de esos subprocesos. Cada uno cumple una función importante para lograr la confiabilidad, el rendimiento escalable y la extensibilidad para todo el contenido web.

Estructura de la canalización de renderización

Diagrama de la canalización de renderización.
Las flechas indican las entradas y salidas de cada etapa. Las etapas se notan por color para demostrar qué subproceso o proceso ejecutan. En algunos casos, las etapas pueden ejecutarse en varios lugares, según las circunstancias, por lo que algunas tienen dos colores. Las etapas verdes representan el subproceso principal del proceso de renderización; las etapas verdes son los compositores del proceso de renderización; las etapas naranjas son el proceso de visualización.

El procesamiento continúa en una canalización con una serie de etapas y artefactos creados a lo largo del camino. Cada etapa representa el código que realiza una tarea bien definida dentro del procesamiento. Los artefactos son estructuras de datos, que son entradas o salidas de las etapas.

Las etapas son las siguientes:

  1. Animar: Cambia los estilos calculados y muta los árboles de propiedades a lo largo del tiempo en función de cronogramas declarativos.
  2. Estilo: Aplica CSS al DOM y crea estilos computados.
  3. Diseño: Determina el tamaño y la posición de los elementos del DOM en la pantalla y crea el árbol de fragmentos inmutable.
  4. Pintura previa: Calcula árboles de propiedades e invalida cualquier lista de visualización y mosaicos de textura de GPU existentes, según corresponda.
  5. Desplazamiento: Actualiza el desplazamiento de los documentos y elementos del DOM desplazables. Para ello, cambia los árboles de propiedades.
  6. Paint: calcula una lista de visualización en la que se describe cómo generar tramas de mosaicos de texturas de GPU desde el DOM.
  7. Confirmación: Copia los árboles de propiedades y la lista de visualización en el subproceso del compositor.
  8. Layerize: Divide la lista de visualización en una lista de capas compuestas para lograr una rasterización y animación independientes.
  9. Worklets de trama, decodificación y pintura: Convierte las listas de visualización, las imágenes codificadas y el código del worklet de pintura, respectivamente, en mosaicos de textura de GPU.
  10. Activar: Crea un marco del compositor que represente cómo dibujar y posicionar mosaicos de GPU en la pantalla, junto con cualquier efecto visual.
  11. Aggregate: Combina los fotogramas del compositor de todos los fotogramas visibles del compositor en un solo fotograma global.
  12. Draw: Ejecuta el marco agregado del compositor en la GPU para crear píxeles en la pantalla.

Se pueden omitir las etapas de la canalización de renderización si no son necesarias. Por ejemplo, las animaciones de efectos visuales y el desplazamiento pueden omitir el diseño, la pintura previa y la pintura. Es por eso que la animación y el desplazamiento están marcados con puntos amarillos y verdes en el diagrama. Si se pueden omitir el diseño, el proceso de pintura previa y la pintura para los efectos visuales, estos se pueden ejecutar por completo en el subproceso del compositor y omitir el subproceso principal.

La renderización de la IU del navegador no se representa directamente aquí, pero se puede considerar como una versión simplificada de esta misma canalización (y, de hecho, su implementación comparte gran parte del código). Por lo general, los videos (tampoco se representan directamente) se renderizan con código independiente que decodifica fotogramas en mosaicos de texturas de GPU que, luego, se conectan a los marcos del compositor y al paso de dibujo.

Estructura de subprocesos y procesos

Procesos de la CPU

El uso de varios procesos de CPU logra un aislamiento de rendimiento y seguridad entre sitios y del estado del navegador, y de aislamiento de estabilidad y seguridad del hardware de la GPU.

Diagrama de las distintas partes de los procesos de la CPU

  • El proceso de renderización renderiza, anima, desplaza y enruta las entradas para una sola combinación de sitio y pestaña. Existen varios procesos de renderización.
  • El proceso del navegador renderiza, anima y enruta las entradas para la IU del navegador (incluida la barra de direcciones, los títulos de las pestañas y los íconos), y enruta todas las entradas restantes al proceso de renderización adecuado. Hay un proceso del navegador.
  • El proceso de visualización agrega la composición de varios procesos de renderización, además del proceso del navegador. Traza y dibuja con la GPU. Hay un proceso de visualización.

Los diferentes sitios siempre terminan en procesos de renderización diferentes.

Varias pestañas o ventanas del navegador de un mismo sitio suelen pasar por procesos de renderización diferentes, a menos que las pestañas estén relacionadas, por ejemplo, una que abre la otra. Con una gran presión de memoria en Chromium para computadoras de escritorio, es posible que se ubiquen varias pestañas del mismo sitio en el mismo proceso de renderización, incluso si no están relacionadas.

Dentro de una sola pestaña del navegador, los fotogramas de diferentes sitios siempre están en procesos de renderización diferentes entre sí, pero los fotogramas del mismo sitio siempre están en el mismo proceso de renderización. Desde la perspectiva de la renderización, la ventaja importante de varios procesos de renderización es que los iframes y las pestañas entre sitios logran un aislamiento de rendimiento entre sí. Además, los orígenes pueden habilitar aún más aislamiento.

Existe exactamente un proceso de Viz para todo Chromium, ya que, por lo general, hay solo una GPU y una pantalla para dibujar.

Separar Viz en su propio proceso es bueno para lograr estabilidad ante errores en los controladores o el hardware de las GPU. También es útil para el aislamiento de seguridad, que es importante para las APIs de GPU como Vulkan y la seguridad en general.

Dado que el navegador puede tener muchas pestañas y ventanas, y todas tienen píxeles de la IU del navegador para dibujar, es posible que te preguntes por qué hay exactamente un proceso del navegador. Esto se debe a que solo una de ellas está enfocada a la vez. De hecho, las pestañas no visibles del navegador se desactivan en su mayoría y descartan toda su memoria de GPU. Sin embargo, las funciones complejas de renderización de la IU del navegador también se implementan cada vez más en los procesos de renderización (lo que se conoce como WebUI). Esto no se debe a razones de aislamiento de rendimiento, sino para aprovechar la facilidad de uso del motor de renderización web de Chromium.

En dispositivos Android más antiguos, el proceso de renderización y de navegador se comparte cuando se usa en una WebView (esto no se aplica a Chromium en Android en general, solo a WebView). En WebView, el proceso del navegador también se comparte con la app de incorporación, y WebView solo tiene un proceso de renderización.

A veces, también existe un proceso de utilidad para decodificar contenido de video protegido. Este proceso no se representa en los diagramas anteriores.

Conversaciones

Los subprocesos ayudan a lograr el aislamiento del rendimiento y la capacidad de respuesta a pesar de las tareas lentas, la paralelización de la canalización y el almacenamiento en búfer múltiple.

Diagrama del proceso de renderización.

  • El subproceso principal ejecuta secuencias de comandos, el bucle de evento de renderización, el ciclo de vida del documento, las pruebas de hits, el envío de eventos de secuencias de comandos y el análisis de HTML, CSS y otros formatos de datos.
    • Los ayudantes de subprocesos principales realizan tareas como crear mapas de bits y BLOB de imagen que requieren codificación o decodificación.
    • Los trabajadores web ejecutan una secuencia de comandos y un bucle de eventos de renderización para OffscreenCanvas.
  • El subproceso del compositor procesa los eventos de entrada, realiza el desplazamiento y las animaciones del contenido web, calcula la asignación de capas óptima del contenido web y coordina las decodificación de imágenes, los worklets de pintura y las tareas de trama.
    • Los asistentes de subprocesos del compositor coordinan las tareas de trama de Viz y ejecutan tareas de decodificación de imágenes, Worklets de pintura y trama de resguardo.
  • Los subprocesos de salida de audio, demuxer o multimedia decodifican, procesan y sincronizan transmisiones de audio y video. (Recuerda que el video se ejecuta en paralelo con la canalización de procesamiento principal).

Separar los subprocesos principal y del compositor es muy importante para el aislamiento de rendimiento de la animación y el desplazamiento del trabajo del subproceso principal.

Solo hay un subproceso principal por proceso de renderización, aunque varias pestañas o marcos del mismo sitio puedan terminar en el mismo proceso. Sin embargo, existe aislamiento de rendimiento del trabajo realizado en varias APIs del navegador. Por ejemplo, la generación de mapas de bits y BLOB de imágenes en la API de Canvas se ejecuta en un subproceso auxiliar del subproceso principal.

Del mismo modo, solo hay un subproceso del compositor por proceso de renderización. En general, no es un problema que solo haya una, ya que todas las operaciones realmente costosas en el subproceso del compositor se delegan a los subprocesos de trabajo del compositor o al proceso de Viz, y este trabajo se puede realizar en paralelo con enrutamiento de entrada, desplazamiento o animación. Los subprocesos de trabajo del compositor coordinan las tareas que se ejecutan en el proceso Viz, pero la aceleración de GPU en todas partes puede fallar por razones fuera del control de Chromium, como errores del controlador. En estas situaciones, el subproceso de trabajo realizará el trabajo en modo de resguardo en la CPU.

La cantidad de subprocesos trabajadores del compositor depende de las capacidades del dispositivo. Por ejemplo, las computadoras de escritorio suelen usar más subprocesos, ya que tienen más núcleos de CPU y tienen menos restricciones de batería que los dispositivos móviles. Este es un ejemplo de escalamiento vertical.

La arquitectura de subprocesos del proceso de renderización es una aplicación de tres patrones de optimización diferentes:

  • Subtareas de ayuda: Envía subtareas de larga duración a subprocesos adicionales para mantener el subproceso superior responsivo a otras solicitudes simultáneas. El asistente de subprocesos principal y los subprocesos auxiliares del compositor son buenos ejemplos de esta técnica.
  • Almacenamiento en búfer múltiple: Muestra contenido renderizado anteriormente mientras renderizas contenido nuevo para ocultar la latencia de la renderización. El subproceso compositor usa esta técnica.
  • Paralelización de canalización: Ejecuta la canalización de renderización en varios lugares de forma simultánea. Así es como el desplazamiento y la animación pueden ser rápidos. Incluso si se está realizando una actualización de renderización del subproceso principal, el desplazamiento y la animación pueden ejecutarse en paralelo.

Proceso del navegador

Un diagrama del proceso del navegador que muestra la relación entre la renderización y el subproceso de composición, y el asistente de renderización y del subproceso de composición.

  • El subproceso de renderización y composición responde a la entrada en la IU del navegador, enruta otras entradas al proceso de renderización correcto, establece y pinta la IU del navegador.
  • Los ayudantes de subprocesos de renderización y composición ejecutan tareas de decodificación de imágenes y decodificación o trama de resguardo.

La renderización y el subproceso de composición del proceso del navegador son similares al código y la funcionalidad de un proceso de renderización, excepto que el subproceso principal y el subproceso compositor se combinan en uno. En este caso, solo se necesita un subproceso porque no es necesario aislar el rendimiento de las tareas largas del subproceso principal, ya que no hay ninguno por diseño.

Proceso de visualización

El proceso Viz incluye el subproceso principal de la GPU y el subproceso compositor de pantalla.

  • El subproceso principal de la GPU muestra listas y fotogramas de video en mosaicos de texturas de GPU y dibuja marcos del compositor en la pantalla.
  • El subproceso del compositor de visualización agrega y optimiza la composición de cada proceso de renderización, además del proceso del navegador, en un solo marco del compositor para presentarlo en la pantalla.

La trama y el dibujo generalmente ocurren en el mismo subproceso, debido a que ambos dependen de recursos de GPU, y es difícil usar varios subprocesos de manera confiable a la GPU (un acceso más sencillo a varios subprocesos a la GPU es una de las motivaciones para desarrollar el nuevo estándar de Vulkan). En Android WebView, hay un subproceso de renderización independiente a nivel del SO para dibujar debido a la forma en que los componentes WebView se incorporan en una app nativa. Es probable que otras plataformas tengan ese subproceso en el futuro.

El compositor de pantallas está en un subproceso diferente porque debe ser responsivo en todo momento y no bloquear ninguna posible fuente de lentitud en el subproceso principal de la GPU. Una de las causas de la demora en el subproceso principal de la GPU son las llamadas a código que no es de Chromium, como los controladores de GPU específicos del proveedor, que pueden ser lentas en formas difíciles de predecir.

Estructura de los componentes

Dentro de cada subproceso principal o compositor del proceso de renderización, hay componentes de software lógicos que interactúan entre sí de maneras estructuradas.

Componentes del subproceso principal del proceso de renderización

Diagrama del procesador Blink.

En el procesador Blink:

  • El fragmento de árbol de marcos local representa el árbol de los marcos locales y el DOM dentro de los marcos.
  • El componente de las APIs de DOM y Canvas contiene implementaciones de todas estas APIs.
  • El ejecutor del ciclo de vida del documento ejecuta los pasos de la canalización de renderización, incluido el paso de confirmación.
  • El componente de prueba y envío de eventos de entrada ejecuta pruebas de posicionamiento para averiguar a qué elemento del DOM se orienta un evento, y ejecuta los algoritmos de envío y los comportamientos predeterminados del evento de entrada.

El programador y ejecutor del bucle de eventos de renderización decide qué ejecutar en el bucle de eventos y cuándo. Programa que el procesamiento se realice a una cadencia que coincida con la pantalla del dispositivo.

Diagrama del árbol de marcos.

Los fragmentos del árbol de marcos locales son un poco complicados. Recuerda que un árbol de marcos es la página principal y sus iframes secundarios, de manera recursiva. Un fotograma es local para un proceso de renderización si se renderiza en ese proceso; de lo contrario, es remoto.

Puedes imaginar para colorear marcos según su proceso de renderización. En la imagen anterior, los círculos verdes son todos marcos en un proceso de renderización; los naranjas están en un segundo y el azul está en un tercero.

Un fragmento de árbol de marcos local es un componente conectado del mismo color en un árbol de marcos. Hay cuatro árboles de marco locales en la imagen: dos para el sitio A, uno para el sitio B y uno para el sitio C. Cada árbol de fotogramas local recibe su propio componente del procesador Blink. El procesador Blink de un árbol de fotogramas local puede o no estar en el mismo proceso de renderización que otros árboles de fotogramas locales. Se determina según la forma en que se seleccionan los procesos de renderización, como se describió antes.

Estructura de subprocesos del compositor del proceso de renderización

Diagrama que muestra los componentes del compositor del proceso de renderización.

Entre los componentes del compositor del proceso de renderización, se incluyen los siguientes:

  • Un controlador de datos que mantiene una lista de capas compuesta, listas de visualización y árboles de propiedades.
  • Un ejecutor de ciclo de vida que ejecuta los pasos de animación, desplazamiento, compuesto, trama y decodificación, y activación de la canalización de renderización. (Recuerda que la animación y el desplazamiento pueden ocurrir tanto en el subproceso principal como en el compositor).
  • Un controlador de prueba de entrada y de posicionamiento realiza el procesamiento de entrada y las pruebas de posicionamiento en la resolución de las capas compuestas para determinar si los gestos de desplazamiento se pueden ejecutar en el subproceso del compositor y a qué procesos de renderización se deben orientar las pruebas de posicionamiento.

Arquitectura de ejemplo en la práctica

En este ejemplo, hay tres pestañas:

Pestaña 1: foo.com

<html>
  <iframe id=one src="foo.com/other-url"></iframe>
  <iframe  id=two src="bar.com"></iframe>
</html>

Pestaña 2: bar.com

<html>
 …
</html>

Tab 3: baz.com html <html> … </html>

El proceso, el subproceso y la estructura de los componentes de estas pestañas se ven de la siguiente manera:

Diagrama del proceso para las pestañas.

Analicemos un ejemplo de cada una de las cuatro tareas principales de renderización. Recuerda lo siguiente:

  1. Renderiza el contenido en píxeles en la pantalla.
  2. Anima efectos visuales en el contenido de un estado a otro.
  3. Desplazamiento en respuesta a la entrada.
  4. Enruta las entradas de manera eficiente a los lugares correctos para que las secuencias de comandos para desarrolladores y otros subsistemas puedan responder.

Para renderizar el DOM modificado en la pestaña uno, haz lo siguiente:

  1. La secuencia de comandos de un desarrollador cambia el DOM en el proceso de renderización de foo.com.
  2. El renderizador Blink le indica al compositor que debe renderizarse.
  3. El compositor le indica a Viz que necesita una renderización.
  4. La visualización le indica al compositor el inicio de la renderización.
  5. El compositor envía la señal de inicio al renderizador Blink.
  6. El ejecutor de bucles de eventos del subproceso principal ejecuta el ciclo de vida del documento.
  7. El subproceso principal envía el resultado al subproceso del compositor.
  8. El ejecutor de bucle de eventos del compositor ejecuta el ciclo de vida de la composición.
  9. Todas las tareas de trama se envían a Viz para tramas (a menudo, hay más de una de estas tareas).
  10. Visualiza contenido de tramas en la GPU.
  11. Viz confirma la finalización de la tarea de trama. Nota: A menudo, Chromium no espera a que se complete la trama y, en su lugar, usa algo llamado token de sincronización que debe resolverse con tareas de trama antes de que se ejecute el paso 15.
  12. Se envía un marco del compositor a Viz.
  13. Viz agrega los marcos del compositor para el proceso de renderización de foo.com, el proceso de renderización de iframe de bar.com y la IU del navegador.
  14. Viz programa un sorteo.
  15. La visualización dibuja el marco agregado del compositor en la pantalla.

Para animar una transición de transformación de CSS en la pestaña dos, haz lo siguiente:

  1. El subproceso compositor para el proceso de renderización de bar.com marca una animación en su bucle de eventos del compositor mediante la mutación de los árboles de propiedades existentes. Luego, se vuelve a ejecutar el ciclo de vida del compositor. (Las tareas de trama y decodificación pueden ocurrir, pero no se muestran aquí).
  2. Se envía un marco del compositor a Viz.
  3. Viz agrega los marcos del compositor para el proceso de representación de foo.com, el proceso de representación de bar.com y la IU del navegador.
  4. Viz programa un sorteo.
  5. La visualización dibuja el marco agregado del compositor en la pantalla.

Para desplazarte por la página web en la pestaña tres, haz lo siguiente:

  1. Llega una secuencia de eventos input (mouse, toque o teclado) al proceso del navegador.
  2. Cada evento se enruta al subproceso compositor del proceso de renderización de baz.com.
  3. El compositor determina si el subproceso principal debe conocer el evento.
  4. El evento se envía, de ser necesario, al subproceso principal.
  5. El subproceso principal activa los objetos de escucha de eventos input (pointerdown, touchstar, pointermove, touchmove o wheel) para ver si los objetos de escucha llamarán a preventDefault en el evento.
  6. El subproceso principal muestra si se llamó a preventDefault al compositor.
  7. De lo contrario, el evento de entrada se devuelve al proceso del navegador.
  8. El proceso del navegador lo convierte en un gesto de desplazamiento al combinarlo con otros eventos recientes.
  9. El gesto de desplazamiento se envía una vez más al subproceso compositor del proceso de renderización de baz.com.
  10. El desplazamiento se aplica allí, y el subproceso del compositor para el proceso de renderización de bar.com marca una animación en su bucle de eventos del compositor. Luego, cambia el desplazamiento de desplazamiento en los árboles de propiedades y vuelve a ejecutar el ciclo de vida del compositor. También le indica al subproceso principal que active un evento scroll (no se muestra aquí).
  11. Se envía un marco del compositor a Viz.
  12. Viz agrega los marcos del compositor para el proceso de renderización de foo.com, el proceso de renderización de bar.com y la IU del navegador.
  13. Viz programa un sorteo.
  14. La visualización dibuja el marco agregado del compositor en la pantalla.

Para enrutar un evento click en un hipervínculo en el iframe núm. dos en la pestaña uno:

  1. Se activa un evento input (mouse, panel táctil o teclado) al proceso del navegador. Realiza una prueba de posicionamiento aproximada para determinar si el proceso de renderización de iframe de bar.com debería recibir el clic y lo envía allí.
  2. El subproceso compositor de bar.com enruta el evento click al subproceso principal de bar.com y programa una tarea de bucle de eventos de renderización para procesarlo.
  3. El procesador de eventos de entrada para las pruebas de hits del subproceso principal de bar.com a fin de determinar en qué elemento del DOM en el iframe se hizo clic y activa un evento click para que las secuencias de comandos lo observen. Al escuchar que no hay preventDefault, se navega al hipervínculo.
  4. Cuando se carga la página de destino del hipervínculo, se renderiza el estado nuevo, con pasos similares al ejemplo anterior "render changed DOM". (Los cambios posteriores no se muestran aquí).

Comida para llevar

Puede llevar mucho tiempo recordar e internalizar el funcionamiento de la renderización.

La conclusión más importante es que la canalización de renderización, mediante una modularización cuidadosa y atención al detalle, se dividió en varios componentes independientes. Luego, estos componentes se dividieron en procesos y subprocesos paralelos para maximizar las oportunidades de rendimiento escalable y extensibilidad.

Cada componente desempeña un rol fundamental para habilitar el rendimiento y las funciones de las apps web modernas.

Sigue leyendo sobre las estructuras de datos clave, que son tan importantes para RenderingNG como los componentes de código.


Ilustraciones de Una Kravets.