Aquí encontrarás cómo se configuran los componentes de RenderingNG y cómo fluye la canalización de renderización a través de ellos.
A partir del nivel más alto, las tareas de renderización son las siguientes:
- Renderiza el contenido en píxeles en la pantalla.
- Anima los efectos visuales en el contenido de un estado a otro.
- Desplázate en respuesta a la entrada.
- Enruta la entrada de manera eficiente a los lugares correctos para que las secuencias de comandos de los 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, además de 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, subprocesos y subcomponentes de la CPU dentro de esos subprocesos. Cada uno desempeña un papel importante para lograr confiabilidad, rendimiento escalable y extensibilidad para todo el contenido web.
Estructura de la canalización de renderización
La renderización se realiza en una canalización con varias etapas y artefactos que se crean a lo largo del proceso. Cada etapa representa un código que realiza una tarea bien definida dentro de la renderización. Los artefactos son estructuras de datos que son entradas o salidas de las etapas.
Las etapas son las siguientes:
- Animar: Cambia los diseños calculados y muta los árboles de propiedades con el tiempo en función de cronogramas declarativos.
- Estilo: Aplica CSS al DOM y crea estilos computados.
- 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.
- Pintura previa: Calcula árboles de propiedades e invalidate cualquier lista de visualización y mosaicos de textura de GPU existentes, según corresponda.
- Desplazamiento: Actualiza el desplazamiento de los documentos y elementos del DOM desplazables. Para ello, cambia los árboles de propiedades.
- Paint: Calcula una lista de visualización que describe cómo rasterizar mosaicos de textura de la GPU desde el DOM.
- Confirmación: Copia los árboles de propiedades y la lista de visualización en el subproceso del compositor.
- Layerize: Divide la lista de visualización en una lista de capas compuestas para la rasterización y animación independientes.
- Trabajos en mosaico de rasterización, decodificación y pintura: Convierte las listas de visualización, las imágenes codificadas y el código de trabajo en mosaico de pintura, respectivamente, en baldosas de textura de GPU.
- Activar: Crea un marco del compositor que represente cómo dibujar y posicionar los mosaicos de la GPU en la pantalla, junto con los efectos visuales.
- Agrupa: Combina los fotogramas del compositor de todos los fotogramas visibles en un solo fotograma del compositor global.
- Dibujo: Ejecuta el fotograma del compositor agregado en la GPU para crear píxeles en 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. Por eso, la animación y el desplazamiento se marcan con puntos amarillos y verdes en el diagrama. Si se pueden omitir el diseño, la pintura previa y la pintura para los efectos visuales, 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 muestra 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). El video (que tampoco se representa directamente) generalmente se renderiza con un código independiente que decodifica fotogramas en tarjetas de textura de la GPU que, luego, se conectan a los fotogramas del compositor y al paso de dibujo.
Estructura de procesos y subprocesos
Procesos de la CPU
El uso de varios procesos de CPU logra el aislamiento de rendimiento y seguridad entre los sitios y el estado del navegador, y la estabilidad y el aislamiento de seguridad del hardware de la GPU.
- El proceso de renderización renderiza, anima, desplaza y enruta la entrada para una combinación de pestaña y sitio únicos. Existen varios procesos de renderización.
- El proceso del navegador renderiza, anima y enruta la entrada 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 de 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 Viz.
Los diferentes sitios siempre terminan en diferentes procesos de renderización.
Por lo general, varias pestañas o ventanas del navegador del mismo sitio se incluyen en diferentes procesos de renderización, a menos que las pestañas estén relacionadas, por ejemplo, si una abre la otra. Cuando hay una gran presión de memoria en Chromium para computadoras de escritorio, es posible que se coloquen 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 diferentes procesos de renderización, 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 los múltiples procesos de renderización es que los iframes y las pestañas entre sitios logran el aislamiento de rendimiento entre sí. Además, los orígenes pueden habilitar aún más aislamiento.
Hay exactamente un proceso de Viz para todo Chromium, ya que, por lo general, solo hay 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 todos tienen píxeles de la IU del navegador para dibujar, es posible que te preguntes por qué hay exactamente un proceso del navegador. El motivo es que solo una de ellas está enfocada a la vez. De hecho, las pestañas no visibles del navegador están desactivadas 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 (conocidos como WebUI). Esto no se hace por motivos 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 el del navegador se comparten cuando se usan en un 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 hay un proceso de utilidad para decodificar contenido de video protegido. Este proceso no se muestra en los diagramas anteriores.
Subprocesos
Los subprocesos ayudan a lograr el aislamiento y la capacidad de respuesta del rendimiento a pesar de las tareas lentas, la paralelización de canalizaciones y el búfer múltiple.
- El subproceso principal ejecuta secuencias de comandos, el bucle de eventos de renderización, el ciclo de vida del documento, las pruebas de hit, el envío de eventos de secuencia de comandos y el análisis de HTML, CSS y otros formatos de datos.
- Los ayudantes de subprocesos principales realizan tareas como crear imágenes de mapas de bits y BLOB que requieren codificación o decodificación.
- Trabajadores web ejecutan la secuencia de comandos y un bucle de eventos de renderización para OffscreenCanvas.
- El subproceso de compositor procesa eventos de entrada, realiza animaciones y desplazamiento de contenido web, calcula la estratificación óptima del contenido web y coordina las decodificaciones de imágenes, las tareas de rasterización y las worklets de pintura.
- 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 renderización principal).
Separar los subprocesos principal y del compositor es de vital importancia para el aislamiento del 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 pueden terminar en el mismo proceso. Sin embargo, existe un aislamiento de rendimiento del trabajo que se realiza en varias APIs del navegador. Por ejemplo, la generación de mapas de bits y objetos blob de imagen en la API de Canvas se ejecuta en un subproceso auxiliar del subproceso principal.
Del mismo modo, solo hay un subproceso de compositor por proceso de renderización. Por lo general, no es un problema que haya solo uno, 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 el enrutamiento de entrada, el desplazamiento o la animación. Los subprocesos de trabajo del compositor coordinan las tareas que se ejecutan en el proceso de Viz, pero la aceleración de la 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 un 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 están menos limitados por la batería que los dispositivos móviles. Este es un ejemplo de escalamiento ascendente y descendente.
La arquitectura de subprocesos del proceso de renderización es una aplicación de tres patrones de optimización diferentes:
- Subprocesos auxiliares: Envía subtareas de larga duración a subprocesos adicionales para mantener el subproceso superior responsivo a otras solicitudes simultáneas. Los subprocesos auxiliares del subproceso principal y del compositor son buenos ejemplos de esta técnica.
- Búfer múltiple: Muestra contenido renderizado anteriormente mientras se renderiza contenido nuevo para ocultar la latencia de la renderización. El subproceso del compositor usa esta técnica.
- Paralelización de canalización: Ejecuta la canalización de renderización en varios lugares de forma simultánea. De esta manera, el desplazamiento y la animación pueden ser rápidos; incluso si se produce una actualización de renderización del subproceso principal, el desplazamiento y la animación pueden ejecutarse en paralelo.
Proceso del navegador
- 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 y organiza 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 rasterización o decodificación de resguardo.
El subproceso de renderización y 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 compositor se combinan en uno. En este caso, solo se necesita un subproceso porque no es necesario el aislamiento de rendimiento de las tareas de subproceso principal largas, ya que no hay ninguna de forma predeterminada.
Proceso de visualización
- El subproceso principal de la GPU rasteriza listas de visualización y fotogramas de video en tarjetas de textura de la GPU y dibuja fotogramas del compositor en la pantalla.
- El subproceso del compositor de pantalla agrega y optimiza la composición de cada proceso de renderización, además del proceso del navegador, en un solo fotograma del compositor para la presentación en la pantalla.
Por lo general, el procesamiento en ráster y el dibujo se realizan en el mismo subproceso, ya que ambos dependen de los recursos de la GPU, y es difícil usar la GPU de forma multiproceso de manera confiable (el acceso multiproceso más fácil a la GPU es una motivación para desarrollar el nuevo estándar Vulkan). En WebView de Android, 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 pantalla se encuentra en un subproceso diferente porque debe ser responsivo en todo momento y no bloquearse en ninguna fuente posible de ralentización en el subproceso principal de la GPU. Una causa de la ralentización del subproceso principal de la GPU son las llamadas a código que no es de Chrome, como los controladores de GPU específicos del proveedor, que pueden ser lentos de formas difíciles de predecir.
Estructura del componente
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
En el renderizador Blink, haz lo siguiente:
- El fragmento de árbol de marcos local representa el árbol de los marcos locales y el DOM dentro de los marcos.
- El componente APIs de DOM y Canvas contiene implementaciones de todas estas APIs.
- El ejecutor del ciclo de vida de los documentos ejecuta los pasos de la canalización de renderización hasta el paso de confirmación inclusive.
- El componente de envío y prueba de hits de eventos de entrada ejecuta pruebas de hits para averiguar a qué elemento DOM está dirigido un evento y ejecuta los algoritmos de envío de eventos de entrada y los comportamientos predeterminados.
El programador y ejecutor del bucle de eventos de renderización decide qué ejecutar en el bucle de eventos y cuándo. Programa la renderización para que se realice a una cadencia que coincida con la pantalla del dispositivo.
Los fragmentos del árbol de tramas locales son un poco complicados. Recuerda que un árbol de marcos es la página principal y sus iframes secundarios, de forma recursiva. Un fotograma es local para un proceso de renderización si se renderiza en ese proceso, y, de lo contrario, es remoto.
Puedes imaginarte cómo se colorean los fotogramas 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 trama local es un componente conectado del mismo color en un árbol de trama. Hay cuatro árboles de tramas locales en la imagen: dos para el sitio A, uno para el sitio B y uno para el sitio C. Cada árbol de marcos local obtiene su propio componente del renderizador Blink. El renderizador Blink de un árbol de marcos locales puede estar o no en el mismo proceso de renderización que otros árboles de marcos 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
Entre los componentes del compositor del proceso de renderización, se incluyen los siguientes:
- Un controlador de datos que mantiene una lista de capas compuestas, listas de visualización y árboles de propiedades.
- Un ejecutor de ciclo de vida que ejecuta los pasos de animación, desplazamiento, composición, rasterización, 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 pruebas de entrada y de acierto realiza el procesamiento de entradas y las pruebas de acierto en la resolución de capas compuestas para determinar si los gestos de desplazamiento se pueden ejecutar en el subproceso del compositor y a qué pruebas de acierto del proceso de renderización se deben orientar.
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>
pestaña 3: baz.com
html
<html>
…
</html>
La estructura del proceso, el subproceso y el componente de estas pestañas se ve de la siguiente manera:
Veamos un ejemplo de cada una de las cuatro tareas principales de renderización. Recordatorio:
- Renderiza el contenido en píxeles en la pantalla.
- Animate los efectos visuales en el contenido de un estado a otro.
- Desplázate en respuesta a la entrada.
- Enruta la entrada de manera eficiente a los lugares correctos para que las secuencias de comandos de los desarrolladores y otros subsistemas puedan responder.
Para renderizar el DOM modificado en la pestaña uno, haz lo siguiente:
- La secuencia de comandos de un desarrollador cambia el DOM en el proceso de renderización de foo.com.
- El renderizador de Blink le indica al compositor que necesita que se realice una renderización.
- El compositor le indica a Viz que necesita una renderización.
- Viz indica el inicio de la renderización al compositor.
- El compositor reenvía el indicador de inicio al renderizador de Blink.
- El ejecutor del bucle de eventos del subproceso principal ejecuta el ciclo de vida del documento.
- El subproceso principal envía el resultado al subproceso del compositor.
- El ejecutor de bucle de eventos del compositor ejecuta el ciclo de vida de la composición.
- Todas las tareas de ráster se envían a Viz para ráster (a menudo, hay más de una de estas tareas).
- Viz rasteriza el contenido en la GPU.
- Viz confirma que se completó la tarea de rasterización. Nota: A menudo, Chromium no espera a que se complete el raster y, en su lugar, usa un elemento llamado token de sincronización que las tareas de rasterización deben resolver antes de que se ejecute el paso 15.
- Se envía un fotograma del compositor a Viz.
- Viz agrega los fotogramas 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.
- Viz programa un sorteo.
- Viz dibuja el fotograma del compositor agregado en la pantalla.
Para animar una transición de transformación de CSS en la pestaña dos, haz lo siguiente:
- El subproceso del compositor para el proceso de renderización de bar.com marca una animación en su bucle de eventos de compositor mediante la mutación de los árboles de propiedades existentes. Luego, se vuelve a ejecutar el ciclo de vida del compositor. (Es posible que se produzcan tareas de rasterización y decodificación, pero no se muestran aquí).
- Se envía un fotograma del compositor a Viz.
- Viz agrega los fotogramas del compositor para el proceso de renderización de foo.com, el proceso de renderización de bar.com y la IU del navegador.
- Viz programa un sorteo.
- Viz dibuja el fotograma del compositor agregado en la pantalla.
Para desplazarte por la página web en la pestaña tres, haz lo siguiente:
- Llega una secuencia de eventos
input
(mouse, toque o teclado) al proceso del navegador. - Cada evento se enruta al subproceso del compositor del proceso de renderización de baz.com.
- El compositor determina si el subproceso principal necesita conocer el evento.
- Si es necesario, el evento se envía al subproceso principal.
- El subproceso principal activa objetos de escucha de eventos
input
(pointerdown
,touchstar
,pointermove
,touchmove
owheel
) para ver si los objetos de escucha llamarán apreventDefault
en el evento. - El subproceso principal muestra si se llamó a
preventDefault
al compositor. - De lo contrario, el evento de entrada se devuelve al proceso del navegador.
- El proceso del navegador lo convierte en un gesto de desplazamiento combinándolo con otros eventos recientes.
- El gesto de desplazamiento se vuelve a enviar al subproceso del compositor del proceso de renderización de baz.com.
- Allí se aplica el desplazamiento, 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, se muta el desplazamiento en los árboles de propiedades y se vuelve a ejecutar el ciclo de vida del compositor.
También le indica al subproceso principal que active un evento
scroll
(que no se muestra aquí). - Se envía un fotograma del compositor a Viz.
- 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.
- Viz programa un sorteo.
- Viz dibuja el fotograma del compositor agregado en la pantalla.
Para enrutar un evento click
en un hipervínculo en el iframe número dos de la pestaña uno, haz lo siguiente:
- Un evento
input
(mouse, táctil o teclado) llega 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í. - El subproceso de compositor para 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. - El procesador de eventos de entrada para las pruebas de hits del subproceso principal de bar.com determina en qué elemento del DOM se hizo clic en el iframe y activa un evento
click
para que las secuencias de comandos lo observen. Si no escuchapreventDefault
, navega al hipervínculo. - Cuando se carga la página de destino del hipervínculo, se renderiza el estado nuevo, con pasos similares al ejemplo anterior "renderiza el DOM modificado". (Estos cambios posteriores no se muestran aquí).
Conclusión
Puede llevar mucho tiempo recordar y, luego, interiorizar cómo funciona 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 papel fundamental en la habilitación del 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.