Обзор архитектуры RenderingNG

Крис Харрельсон
Chris Harrelson

В предыдущем посте я дал обзор целей и ключевых свойств архитектуры RenderingNG. В этом посте будет объяснено, как настроены его составные части и как через них проходит конвейер рендеринга.

Начиная с самого высокого уровня и далее вниз, задачи рендеринга заключаются в следующем:

  1. Преобразуйте содержимое в пиксели на экране.
  2. Анимируйте визуальные эффекты содержимого из одного состояния в другое.
  3. Прокрутка в ответ на ввод.
  4. Эффективно направляйте вводимые данные в нужные места, чтобы сценарии разработчика и другие подсистемы могли реагировать.

Содержимое для рендеринга — это дерево фреймов для каждой вкладки браузера, а также пользовательский интерфейс браузера. И поток необработанных событий ввода с сенсорных экранов, мышей, клавиатур и других аппаратных устройств.

Каждый кадр включает в себя:

  • Состояние DOM
  • CSS
  • Холсты
  • Внешние ресурсы, такие как изображения, видео, шрифты и SVG.

Фрейм — это HTML-документ плюс его URL-адрес. Веб-страница, загруженная на вкладку браузера, имеет фрейм верхнего уровня, дочерние фреймы для каждого iframe, включенного в документ верхнего уровня, и их рекурсивные потомки iframe.

Визуальный эффект — это графическая операция, применяемая к растровому изображению, например прокрутка, преобразование, обрезка, фильтрация, непрозрачность или смешивание.

Компоненты архитектуры

В RenderingNG эти задачи логически разделены на несколько этапов и компонентов кода. Компоненты попадают в различные процессы ЦП, потоки и подкомпоненты внутри этих потоков. Каждый из них играет важную роль в достижении надежности , масштабируемой производительности и расширяемости всего веб-контента.

Рендеринг структуры конвейера

Схема конвейера рендеринга, описанная в следующем тексте.

Рендеринг происходит в конвейере с множеством этапов и артефактов, создаваемых на этом пути. Каждый этап представляет собой код, выполняющий одну четко определенную задачу в рамках рендеринга. Артефакты — это структуры данных, которые являются входными или выходными данными этапов; на схеме входы или выходы обозначены стрелками.

В этом сообщении блога не будет подробностей об артефактах; об этом речь пойдет в следующем посте: Ключевые структуры данных и их роли в RenderingNG .

Этапы конвейера

На предыдущей диаграмме этапы обозначены цветами, указывающими, в каком потоке или процессе они выполняются:

  • Зеленый: основной поток процесса рендеринга.
  • Желтый: наборщик процесса рендеринга.
  • Оранжевый: а именно процесс

В некоторых случаях они могут выполняться в нескольких местах, в зависимости от обстоятельств, поэтому некоторые из них имеют два цвета.

Этапы:

  1. Анимация: изменяйте вычисленные стили и деревья свойств с течением времени на основе декларативных временных шкал.
  2. Стиль: примените CSS к DOM и создайте вычисляемые стили .
  3. Макет: определите размер и положение элементов DOM на экране и создайте неизменяемое дерево фрагментов .
  4. Предварительная раскраска: вычислите деревья свойств и при необходимости аннулируйте любые существующие списки отображения и плитки текстур графического процессора.
  5. Прокрутка: обновление смещения прокрутки документов и прокручиваемых элементов DOM путем изменения деревьев свойств.
  6. Paint: вычисляет список отображения, описывающий, как растрировать фрагменты текстур графического процессора из DOM.
  7. Зафиксировать: скопировать деревья свойств и список отображения в поток компоновщика.
  8. Layerize: разбейте список отображения на составной список слоев для независимой растеризации и анимации.
  9. Растровые, декодирующие и рисующие рабочие программы: преобразуйте списки отображения, закодированные изображения и код рабочей программы рисования соответственно в плитки текстур графического процессора .
  10. Активировать: создайте фрейм композитора , показывающий, как рисовать и размещать фрагменты графического процессора на экране, а также любые визуальные эффекты.
  11. Агрегировать: объединить кадры композитора из всех видимых кадров композитора в один глобальный кадр композитора.
  12. Draw: выполнение агрегированного кадра компоновщика на графическом процессоре для создания пикселей на экране.

Этапы конвейера рендеринга можно пропустить, если они не нужны. Например, анимация визуальных эффектов и прокрутки могут пропускать макет, предварительную прорисовку и прорисовку. Именно поэтому анимация и прокрутка отмечены на схеме желтыми и зелеными точками. Если макет, предварительную отрисовку и отрисовку можно пропустить для визуальных эффектов, их можно полностью запустить в потоке наборщика и пропустить основной поток.

Рендеринг пользовательского интерфейса браузера здесь не показан напрямую, но его можно рассматривать как упрощенную версию того же конвейера (и фактически его реализация использует большую часть кода). Видео (также не изображенное напрямую) обычно визуализируется с помощью независимого кода, который декодирует кадры в плитки текстур графического процессора, которые затем подключаются к кадрам компоновщика и на этапе рисования.

Структура процессов и потоков

Процессы ЦП

Использование нескольких процессов ЦП обеспечивает изоляцию производительности и безопасности между сайтами и от состояния браузера, а также изоляцию стабильности и безопасности от оборудования графического процессора.

Схема различных частей процессов ЦП

  • Процесс рендеринга визуализирует, анимирует, прокручивает и маршрутизирует ввод для одной комбинации сайта и вкладки. Существует множество процессов рендеринга.
  • Процесс браузера визуализирует, анимирует и маршрутизирует входные данные для пользовательского интерфейса браузера (включая строку URL-адреса, заголовки вкладок и значки), а также направляет все оставшиеся входные данные в соответствующий процесс рендеринга. Существует ровно один процесс браузера.
  • Процесс Viz объединяет композиции из нескольких процессов рендеринга, а также процесса браузера. Он растрирует и рисует с помощью графического процессора. Существует ровно один процесс Viz.

Разные сайты всегда подвергаются разным процессам рендеринга. (На самом деле всегда на рабочем столе; если возможно, на мобильном устройстве . Ниже я напишу «всегда», но это предостережение применимо повсюду.)

Несколько вкладок браузера или окон одного и того же сайта обычно выполняются в разных процессах рендеринга, если только вкладки не связаны друг с другом (одна открывает другую). При сильной нехватке памяти на настольном компьютере Chromium может помещать несколько вкладок с одного и того же сайта в один и тот же процесс рендеринга, даже если они не связаны друг с другом.

В пределах одной вкладки браузера кадры с разных сайтов всегда находятся в разных процессах рендеринга, но кадры с одного и того же сайта всегда находятся в одном и том же процессе рендеринга. С точки зрения рендеринга важным преимуществом нескольких процессов рендеринга является то, что межсайтовые iframe и вкладки обеспечивают изоляцию производительности друг от друга. Кроме того, страны происхождения могут пойти на еще большую изоляцию .

Для всего Chromium существует ровно один процесс Viz. В конце концов, обычно есть только один графический процессор и экран для рисования. Выделение Viz в отдельный процесс полезно для стабильности перед лицом ошибок в драйверах графического процессора или оборудовании. Это также хорошо для изоляции безопасности, что важно для API-интерфейсов графических процессоров, таких как Vulkan . Это также важно для безопасности в целом .

Поскольку в браузере может быть множество вкладок и окон, и во всех из них есть пиксели пользовательского интерфейса браузера, которые нужно отрисовывать, вы можете задаться вопросом: почему существует ровно один процесс браузера? Причина в том, что одновременно фокусируется только один из них; на самом деле, невидимые вкладки браузера в большинстве случаев деактивируются и освобождают всю память графического процессора. Однако сложные функции рендеринга пользовательского интерфейса браузера все чаще реализуются и в процессах рендеринга (известных как WebUI ). Это делается не из соображений изоляции производительности, а для того, чтобы воспользоваться простотой использования механизма веб-рендеринга Chromium.

На старых устройствах Android процессы рендеринга и браузера являются общими при использовании в WebView (в целом это не относится к Chromium на Android, только к WebView). В WebView процесс браузера также используется совместно с приложением для внедрения, а WebView имеет только один процесс рендеринга.

Иногда также существует служебный процесс для декодирования защищенного видеоконтента. Выше этот процесс не изображен.

Потоки

Потоки помогают добиться изоляции производительности и скорости реагирования, несмотря на медленные задачи, распараллеливание конвейеров и множественную буферизацию.

Схема процесса рендеринга, описанная в статье.

  • В основном потоке выполняются сценарии, цикл событий рендеринга, жизненный цикл документа, проверка попадания, диспетчеризация событий сценария и анализ HTML, CSS и других форматов данных.
    • Помощники основного потока выполняют такие задачи, как создание растровых изображений и больших двоичных объектов, требующих кодирования или декодирования.
    • Веб-работники запускают скрипт и цикл событий рендеринга для OffscreenCanvas.
  • Поток Compositor обрабатывает входные события, выполняет прокрутку и анимацию веб-контента, вычисляет оптимальную слоистость веб-контента и координирует декодирование изображений, рабочие программы рисования и растровые задачи.
    • Помощники потоков компоновщика координируют растровые задачи Viz, выполняют задачи декодирования изображений, рабочие программы рисования и резервный растр.
  • Потоки вывода мультимедиа, демультиплексора или звука декодируют, обрабатывают и синхронизируют видео- и аудиопотоки. (Помните, что видео выполняется параллельно с основным конвейером рендеринга.)

Разделение основного потока и потока композитора критически важно для изоляции производительности анимации и прокрутки от работы основного потока.

Для каждого процесса рендеринга существует только один основной поток, хотя несколько вкладок или фреймов с одного и того же сайта могут оказаться в одном процессе. Однако производительность изолирована от работы, выполняемой в различных API браузера. Например, создание растровых изображений и больших двоичных объектов в Canvas API выполняется во вспомогательном потоке основного потока.

Аналогично, для каждого процесса рендеринга существует только один поток композитора. Обычно не проблема, что он только один, потому что все действительно дорогостоящие операции в потоке компоновщика делегируются либо рабочим потокам компоновщика, либо процессу Viz, и эта работа может выполняться параллельно с маршрутизацией ввода, прокруткой или анимацией. . Рабочие потоки Compositor координируют задачи, выполняемые в процессе Viz, но ускорение графического процессора повсюду может не работать по причинам, не зависящим от Chromium, например, из-за ошибок драйверов. В таких ситуациях рабочий поток будет выполнять работу в резервном режиме на ЦП.

Количество рабочих потоков композитора зависит от возможностей устройства. Например, настольные компьютеры обычно используют больше потоков, поскольку они имеют больше ядер ЦП и менее ограничены по заряду батареи, чем мобильные устройства. Это пример увеличения и уменьшения масштаба .

Также интересно отметить, что архитектура потоков процесса рендеринга представляет собой применение трёх различных шаблонов оптимизации:

  • Вспомогательные потоки: отправка долго выполняющихся подзадач в дополнительные потоки, чтобы родительский поток реагировал на другие запросы, происходящие одновременно. Хорошими примерами этого метода являются вспомогательные потоки основного потока и вспомогательные потоки композитора.
  • Множественная буферизация : отображение ранее визуализированного контента при рендеринге нового контента, чтобы скрыть задержку рендеринга. Поток наборщика использует эту технику.
  • Распараллеливание конвейеров: запуск конвейера рендеринга в нескольких местах одновременно. Вот почему прокрутка и анимация могут быть быстрыми, даже если происходит обновление рендеринга основного потока, поскольку прокрутка и анимация могут выполняться параллельно.

Процесс браузера

Диаграмма процесса браузера, показывающая взаимосвязь между потоками рендеринга и компоновки, а также вспомогательным потоком рендеринга и компоновки.

  • Поток рендеринга и композитинга реагирует на вводимые данные в пользовательском интерфейсе браузера, направляет другие входные данные в правильный процесс рендеринга; макетирует и рисует пользовательский интерфейс браузера.
  • Помощники потоков рендеринга и компоновки выполняют задачи декодирования изображений и резервного растра или декодирования.

Процесс рендеринга браузера и поток композиции аналогичны коду и функциональности процесса рендеринга, за исключением того, что основной поток и поток компоновщика объединены в один. В этом случае необходим только один поток, поскольку нет необходимости в изоляции производительности от длинных задач основного потока, поскольку их нет по замыслу.

а именно процесс

Диаграмма, показывающая, что процесс Viz включает в себя основной поток графического процессора и поток компоновщика дисплея.

  • Растровые изображения основного потока графического процессора отображают списки и видеокадры в виде фрагментов текстур графического процессора и рисуют кадры компоновщика на экране.
  • Поток компоновщика дисплея объединяет и оптимизирует компоновку каждого процесса рендеринга, а также процесса браузера, в единый кадр компоновщика для представления на экране.

Растр и рисование обычно выполняются в одном потоке, поскольку оба они полагаются на ресурсы графического процессора, и трудно надежно обеспечить многопоточное использование графического процессора (более простой многопоточный доступ к графическому процессору является одной из причин разработки нового стандарта Vulkan) . ). В Android WebView существует отдельный поток рендеринга на уровне ОС для рисования, поскольку WebViews встроены в собственное приложение. На других платформах, вероятно, появится такая тема в будущем.

Компоновщик дисплея находится в другом потоке, поскольку он должен всегда реагировать и не блокировать любой возможный источник замедления в основном потоке графического процессора. Одной из причин замедления основного потока графического процессора являются вызовы кода, отличного от Chromium, например драйверов графического процессора конкретного поставщика, которые могут быть медленными в труднопредсказуемых случаях.

Структура компонента

Внутри каждого основного потока или потока композитора процесса рендеринга имеются логические программные компоненты, которые структурированным образом взаимодействуют друг с другом.

Компоненты основного потока процесса рендеринга

Схема рендерера Blink.

  • Рендерер мигания:
    • Фрагмент дерева локальных кадров представляет дерево локальных кадров и DOM внутри кадров.
    • Компонент API DOM и Canvas содержит реализации всех этих API.
    • Средство выполнения жизненного цикла документа выполняет этапы конвейера рендеринга вплоть до этапа фиксации.
    • Компонент тестирования и диспетчеризации входных событий выполняет тесты на попадание, чтобы определить, на какой элемент DOM нацелено событие, а также запускает алгоритмы диспетчеризации входных событий и поведение по умолчанию.
  • Планировщик и исполнитель цикла событий рендеринга решают, что и когда запускать в цикле событий. Он планирует рендеринг с частотой, соответствующей дисплею устройства.

Схема дерева фреймов.

Фрагменты локального дерева фреймов немного сложны для понимания. Напомним, что дерево фреймов — это главная страница и ее дочерние iframe, рекурсивно. Кадр является локальным по отношению к процессу рендеринга, если он визуализируется в этом процессе, в противном случае он является удаленным .

Вы можете представить раскраску кадров в соответствии с процессом их рендеринга. На предыдущем изображении зеленые кружки — это все кадры одного процесса рендеринга; оранжевые — через секунду, а синие — через треть.

Фрагмент локального дерева фреймов — это связный компонент одного цвета в дереве фреймов. На изображении имеется четыре дерева локальных фреймов: два для сайта A, один для сайта B и один для сайта C. Каждое дерево локальных фреймов получает свой собственный компонент рендеринга Blink. Средство рендеринга Blink локального дерева кадров может находиться или не находиться в том же процессе рендеринга, что и другие локальные деревья кадров (это определяется способом выбора процессов рендеринга, как описано ранее).

Структура потока компоновщика процесса рендеринга

Диаграмма, показывающая компоненты композитора процесса рендеринга.

Компоненты композитора процесса рендеринга включают в себя:

  • Обработчик данных , который поддерживает составной список слоев, списки отображения и деревья свойств.
  • Средство выполнения жизненного цикла , которое выполняет этапы анимации, прокрутки, компоновки, растра, а также декодирования и активации конвейера рендеринга. (Помните, что анимация и прокрутка могут выполняться как в основном потоке, так и в композиторе.)
  • Обработчик ввода и проверки попадания выполняет обработку ввода и проверку попадания с разрешением составных слоев, чтобы определить, можно ли запускать жесты прокрутки в потоке компоновщика и на какой процесс рендеринга следует ориентироваться при проверке попадания.

Пример на практике

Давайте теперь конкретизируем архитектуру на примере. В этом примере есть три вкладки:

Вкладка 1: foo.com

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

Вкладка 2: bar.com

<html>
 …
</html>

Вкладка 3: baz.com html <html> … </html>

Структура процессов, потоков и компонентов для этих вкладок будет выглядеть следующим образом:

Схема процесса для вкладок.

Теперь давайте рассмотрим по одному примеру каждой из четырех основных задач рендеринга, которыми, как вы помните, являются:

  1. Преобразуйте содержимое в пиксели на экране.
  2. Анимируйте визуальные эффекты содержимого из одного состояния в другое.
  3. Прокрутка в ответ на ввод.
  4. Эффективно направляйте вводимые данные в нужные места, чтобы сценарии разработчика и другие подсистемы могли реагировать.

Чтобы отобразить измененный DOM для первой вкладки:

  1. Сценарий разработчика изменяет DOM в процессе рендеринга для foo.com.
  2. Средство рендеринга Blink сообщает композитору, что ему необходим рендеринг.
  3. Компоновщик сообщает Viz, что для этого требуется рендеринг.
  4. Viz сигнализирует композитору о начале рендеринга.
  5. Компоновщик пересылает стартовый сигнал на рендерер Blink.
  6. Обработчик цикла событий основного потока запускает жизненный цикл документа.
  7. Основной поток отправляет результат в поток компоновщика.
  8. Обработчик цикла событий компоновщика запускает жизненный цикл компоновки.
  9. Любые растровые задачи отправляются в Виз для растра (часто таких задач несколько).
  10. Визуализация растрового содержимого на графическом процессоре.
  11. Виз подтверждает завершение растровой задачи. Примечание. Chromium часто не ждет завершения растра, а вместо этого использует так называемый токен синхронизации , который должен быть разрешен растровыми задачами перед выполнением шага 15.
  12. Кадр наборщика отправляется в Viz.
  13. Viz объединяет кадры композитора для процесса рендеринга foo.com, процесса рендеринга iframe bar.com и пользовательского интерфейса браузера.
  14. Виз планирует ничью.
  15. Viz рисует агрегированный кадр композитора на экране.

Чтобы анимировать переход преобразования CSS на второй вкладке:

  1. Поток композитора для процесса рендеринга bar.com отмечает анимацию в своем цикле событий композитора, изменяя существующие деревья свойств. Затем повторно запускается жизненный цикл композитора. (Могут возникнуть задачи растра и декодирования, но они здесь не описаны.)
  2. Кадр наборщика отправляется в Viz.
  3. Viz объединяет кадры композитора для процесса рендеринга foo.com, процесса рендеринга bar.com и пользовательского интерфейса браузера.
  4. Виз планирует ничью.
  5. Viz рисует агрегированный кадр композитора на экране.

Чтобы прокрутить веб-страницу на третьей вкладке:

  1. В процесс браузера поступает последовательность событий input (мышь, касание или клавиатура).
  2. Каждое событие направляется в поток композитора процесса рендеринга baz.com.
  3. Компоновщик определяет, нужно ли основному потоку знать о событии.
  4. Событие при необходимости отправляется в основной поток.
  5. Основной поток запускает прослушиватели input событий ( pointerdown , touchstar , pointermove , touchmove wheel ), чтобы узнать, будут ли прослушиватели вызывать preventDefault для этого события.
  6. Основной поток возвращает информацию о том, вызывался ли preventDefault для компоновщика.
  7. Если нет, событие ввода отправляется обратно в процесс браузера.
  8. Процесс браузера преобразует его в жест прокрутки, комбинируя его с другими недавними событиями.
  9. Жест прокрутки снова отправляется в поток композитора процесса рендеринга baz.com.
  10. Там применяется прокрутка, и поток композитора для процесса рендеринга bar.com отмечает анимацию в своем цикле событий композитора. Затем это изменяет смещение прокрутки в деревьях свойств и повторно запускает жизненный цикл компоновщика. Он также сообщает основному потоку запустить событие scroll (здесь не показано).
  11. Кадр наборщика отправляется в Viz.
  12. Viz объединяет кадры композитора для процесса рендеринга foo.com, процесса рендеринга bar.com и пользовательского интерфейса браузера.
  13. Виз планирует ничью.
  14. Viz рисует агрегированный кадр композитора на экране.

Чтобы перенаправить событие click по гиперссылке в iframe #two на первой вкладке:

  1. В процесс браузера поступает событие input (мышь, касание или клавиатура). Он выполняет приблизительную проверку попадания, чтобы определить, что процесс рендеринга iframe bar.com должен получить щелчок, и отправляет его туда.
  2. Поток композитора bar.com направляет событие click в основной поток bar.com и планирует задачу цикла событий рендеринга для его обработки.
  3. Обработчик входных событий для основного потока bar.com проверяет попадание, чтобы определить, какой элемент DOM в iframe был нажат, и запускает событие click для наблюдения скриптами. Услышав preventDefault , он переходит к гиперссылке.
  4. При загрузке целевой страницы гиперссылки новое состояние отображается с помощью шагов, аналогичных приведенному выше примеру «рендеринга измененного DOM». (Эти последующие изменения здесь не показаны.)

Заключение

Уф, как много подробностей. Как видите, рендеринг в Chromium довольно сложен! Чтобы запомнить и усвоить все детали, может потребоваться много времени, поэтому не волнуйтесь, если это покажется вам утомительным.

Самый важный вывод заключается в том, что существует концептуально простой конвейер рендеринга, который благодаря тщательной модульности и вниманию к деталям был разделен на ряд автономных компонентов. Затем эти компоненты были разделены по параллельным процессам и потокам, чтобы максимизировать масштабируемую производительность и возможности расширения .

Каждый из этих компонентов играет решающую роль в обеспечении всей производительности и функций, необходимых современным веб-приложениям. Вскоре мы опубликуем более подробную информацию о каждом из них и о важной роли, которую они играют.

Но перед этим я также объясню, почему ключевые структуры данных, упомянутые в этом посте (те, что обозначены синим цветом по бокам диаграммы конвейера рендеринга), так же важны для RenderingNG, как и компоненты кода.

Спасибо за чтение и следите за обновлениями!

Иллюстрации Уны Кравец.