Процесс рендеринга в браузере Подробный анализ

JavaScript браузер анимация CSS

В предыдущей статье:Исходный код Vue, подробное объяснение nextTick: MutationObserver — это просто облако, микрозадача — это ядро!, как я уже сказал, случайно в пареВ обсуждении задачи и микрозадачи, я изучил механизм рендеринга исполняемый браузером после обработки задач и микрозадач.Я был очень взволнован, когда увидел этот контент, потому что я никогда не знал, какой стиль я изменил в js, когда и каким был браузер. интерфейс, так что я написал эту статью взволнованно.

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

спецификация и обработка событийного цикла

Вот что я сказал в началеПодробное объяснение nexttickЭту часть я упомянул, но только сам смотрел, и не стал вводить ее подробно как основную тему статьи, на тот момент речь шла в основном о задачах и микрозадачах. По сути, эта часть является ключом ко всему процессу рендеринга, включающемуКогда браузер отображает, так серьезно:

Пожалуйста, нажмите на ссылку, чтобы увидеть официальную спецификацию html5: html5 event loop processing model

  1. На первых шагах от 1 до 5 выберите очередь задач из нескольких очередей задач (браузер часто имеет несколько очередей задач, чтобы различать приоритеты разных задач), возьмите самую старую задачу из очереди задач и выполните ее, и затем удалите его из очереди.
  2. Шестой шаг — выполнение контрольной точки микрозадачи.Выполнение контрольной точки микрозадачи, этот шаг фактически содержит несколько подшагов. Пока очередь микрозадач не пуста, этот шаг всегда извлекает микрозадачу из очереди микрозадач и выполняет ее. Если во время выполнения микрозадачи добавляется еще одна микрозадача, вновь добавленная микрозадача все равно будет выполняться. ( Шаг 7 по ссылкеreturn to the microtask queue handling stepИмеет решающее значение)
  3. Шаг седьмой Обновите рендеринг, обновите рендеринг. Пришло время обновить интерфейс!
    1. Шаги 7.1–7.4 определяют, нужно ли визуализировать текущий документ.В официальной спецификации браузер определяет, выиграет ли документ от визуализации пользовательского интерфейса, поскольку необходимо поддерживать только частоту обновления 60 Гц, а каждый раунд события Циклы выполняются очень быстро, поэтому нет необходимости отображать пользовательский интерфейс в каждом раунде цикла, а только примерно через 16 мс. В то же время для некоторых страниц, которые относительно застряли и больше не могут гарантировать 60 Гц, если в это время будет выполняться отрисовка интерфейса, то будет хуже, поэтому браузер может снизить частоту, от которой может выиграть документ (например ) 30 Гц.
    2. run the resize steps, если размер браузера был изменен, то в окне будет запущено событие «изменить размер».
    3. run the scroll steps. Во-первых, всякий раз, когда мы прокручиваем цель (цель может быть прокручиваемым элементом или документом), браузер будет прокручивать документ, которому принадлежит цель.pending scroll event targetsСохраните цель, где происходит прокрутка. Сейчас,run the scroll stepsЭтот шаг начнется сpending scroll event targetsВыньте цель, а затем вызовите событие прокрутки на цели.
    4. Рассчитать, следует ли запускать медиа-запрос
    5. 7.8 и 7.9 Выполнение CSS-анимации и запуск событий, связанных с анимацией, таких как 'animationstart'. Выполнение шагов полноэкранного рендеринга: Если в предыдущих задачах или микрозадачах выполнялись полноэкранные API, такие как requestFullscreen(), полноэкранные операции будут выступал здесь.
    6. run the animation frame callbacks, выполните обратный вызов requestAnimationFrame, здесь выполняется requestAnimationFrame!
    7. воплощать в жизньIntersectionObserverОбратный вызов, возможно, вы использовали этот API в логике ленивой загрузки изображения.
    8. Обновление, визуализация пользовательского интерфейса
  4. Вернуться к первому шагу

Ну, весь процесс представлен. Первые два этапа обработки задачи и микрозадачи повторяться не будут. Есть 3 вещи, на которые следует обратить внимание на важном третьем шаге:

  1. Не каждый раунд цикла событий будет обновлять рендеринг, только когда браузер решит, что документ нуждается в обновлении, интерфейс будет обновлен. Это означает, что минимальный интервал между двумя рендерами пользовательского интерфейса также составляет 16 мс.Если вы установите Interval для обновления каждые 1 мс, он все равно будет обновляться каждые 16 мс.
  2. События изменения размера и прокрутки запускаются в процессе рендеринга. Разве это не удивительно? Это означает, что если вы хотите в событии прокруткиПривязать обратные вызовы для выполнения анимации, то вам вообще не нужно использовать requestAnimationFrame для дросселирования, само событие прокрутки выполняется до фактического рендеринга каждого кадра со своим собственным эффектом дросселирования! Конечно, бизнес-логика, такая как отложенная загрузка прокручиваемых изображений и бесконечная загрузка прокручиваемого контента, а не логика анимации, по-прежнему требует дросселирования.
  3. Как введено mdn, w3c и т. д.:Обратный вызов requestAnimationFrame выполняется перед перерисовкой, Шаг 7.9 является гарантией этой логики.
  4. Перерисовка пользовательского интерфейса выполняется в конце цикла событий.
    Перерисовка страницы на самом деле тесно связана с циклом событий и точно определена в цикле событий, что неожиданно. Я никогда не понимал, когда мой JS изменяет стиль DOM, когда стиль отображается.

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

оказывать

Эту очень классическую картинку видели многие, и концепция в ней должна быть всем знакома, то есть эти несколько шагов: js изменяет структуру или стиль dom -> вычисляет стиль -> layout (перестановка) -> paint (перестановка ) картина) -> композит

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


На фото выше изGPU Accelerated Compositing in Chrome

На фото выше изThe Anatomy of a Frame


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


Давайте сначала поговорим о некоторых понятиях:


1. битовая карта

Именно растровое изображение часто упоминается в структуре данных. Когда вы хотите нарисовать картинку, что вы должны сделать?Очевидно, что первое, что нужно сделать, это представить картинку как структуру данных, понятную компьютеру: использовать двумерный массив, и каждый элемент массива записывает значение каждый пиксель на картинке определенный цвет. Таким образом, браузер может использовать растровое изображение для записи того, что он хочет нарисовать в определенной области, а процесс рисования просто заполняет пиксели в определенных индексах в массиве.

2. текстура
Текстуры на самом деле представляют собой растровые изображения в графическом процессе, хранятся в видеорамке GPU. Вы можете определить, какие элементы в упомянутом выше элементах хранятся, используют ли 3 байта для хранения 256-битных RGB или 1 бита для хранения черно-белых, вы можете определить его самостоятельно, но текстура посвящена грастру И GPU и CPU разделены., необходимо иметь фиксированный формат для легкой совместимости и обработки. Следовательно, с одной стороны, формат текстуры относительно фиксирован, например, форматы пикселей, такие как R5G6B5, A4R4G4B4 и т. Д. С другой стороны, GPU имеет ограничения на размер текстуры, например, длина / Ширина должна быть мощностью 2, а максимум не может превышать 2048 или 4096.

3. Растрировать


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



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

### Конкретный процесс рисования

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

Давайте посмотрим на эту классическую картину:

Названия некоторых существительных на картинке изменены, подробнее читайте в статье taobaofed:Оптимизация производительности беспроводной сети: Композитный

Render Object

Во-первых, у нас есть дерево DOM, но DOM в дереве DOM предназначен для JS/HTML/CSS и не может быть отрисован непосредственно на странице или в растровом изображении. Таким образом, браузер внутренне реализуетRender Object:

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

Теперь, когда реализован метод отрисовки каждого узла DOM, можно ли открыть пространство растрового изображения, а затем DFS обходит это новое дерево объектов рендеринга и выполняет метод рисования каждого объекта рендеринга, чтобы отобразить DOM в растровое изображение? Точно так же, как «штамповка», покрывайте содержимое каждого объекта визуализации на бумаге один за другим (аналогично растровому изображению в настоящее время), чтобы завершить рисунок.

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

Следовательно, есть Render Layer.

Render Layer

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

Короче говоря, это элемент с каскадным, полупрозрачным и т. д. (подробнее см.Оптимизация производительности беспроводной сети: Композитный) будет переведен из Render Object в Render Layer. Объект рендеринга, не переведенный в слой рендеринга, принадлежит слою рендеринга, ближайшему к его родительскому элементу. Конечно, сам корневой элемент HTML должен быть повышен до слоя рендеринга.

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

Кроме того:

The children of each RenderLayer are kept into two sorted lists both sorted in ascending order, the negZOrderList containing child layers with negative z-indices (and hence layers that go below the current layer) and the posZOrderList contain child layers with positive z-indices (layers that go above the current layer).
Подслои рендеринга каждого слоя рендеринга хранятся в двух упорядоченных списках в порядке возрастания: negZOrderList хранит подслои с отрицательными z-индексами, а posZOrderList хранит подслои с положительными z-индексами.
- отКомпозитинг с GPU-ускорениемодна статья

Теперь механизм рендеринга браузера просматривает дерево слоев, посещает каждый RenderLayer, затем рекурсивно просматривает слои в negZOrderList, свой собственный RenderObject и рекурсивно просматривает слои в posZOrderList. Вы можете нарисовать дерево слоев.

Дерево слоев определяет иерархический порядок отрисовки веб-страницы, а RenderObject, подчиненный RenderLayer, определяет содержимое слоя.Все RenderLayer и RenderObject вместе определяют конечное содержимое веб-страницы, отображаемое на экране.

Проблему ламинирования контекста, полупрозрачности, MASK и т.п. решает Render Layer. Тогда теперь: открыть растровое пространство -> Постоянно рисовать слой рендера, покрывать нижний слой-> Взять GPU для его отображения?

Не делайте. Также GraphicsLayers и Graphics Context

Графический слой (также известный как составной слой) и графический контекст

Вышеупомянутый процесс может обрабатывать процесс рисования. Но в браузере часто есть анимация, видео, холст, 3d css и прочее. Это означает, что когда на странице есть эти элементы, отображение страницы будет часто меняться, а это означает, что растровое изображение будет часто меняться. При 60 кадрах в секунду перерисовка всего растрового изображения при каждом изменении — это ужасное снижение производительности.

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

Некоторые элементы с CSS3 3D-преобразованиями, элементами с анимацией на непрозрачности, свойства преобразования, аппаратные ускоренные холста и видео и т. Д. Эти элементы были повышены для рендеринга слоя на предыдущем шаге, и теперь они продвигаются к графическому уровню композиционного уровня (если вы Проверьте предыдущую статью, которую я далСвязь, вы можете задаться вопросом, почему эти случаи также могут быть повышены до Render Layer, теперь вы должны понимать, что они повышены до Graphics Слой подготовлен). Каждый слой визуализации принадлежит ближайшему графическому слою своего предка. Конечно, сам корневой элемент HTML необходимо повысить до уровня графики.

Когда слой рендеринга повышается до графического слоя:

  • 3D или перспективное преобразование(перспектива, преобразование) свойства CSS
  • Элементы, использующие ускоренное декодирование видео
  • Элементы с контекстом 3D (WebGL) или ускоренным контекстом 2D
  • Гибридные плагины (например, Flash)
  • Применить анимацию или переход к непрозрачности, преобразованию, отражению и фоновому фильтру (это должна быть активная анимация или переход. Если эффект анимации или перехода не начался или не закончился, продвижение составного слоя также не удастся)
  • will-change устанавливается на непрозрачность, преобразование, верх, левый, нижний, правый (где верхний, левый и т. д. необходимо установить четкие свойства позиционирования, такие как относительное и т. д.)
  • Элементы с ускоренными фильтрами CSS
  • У элемента есть родственный элемент с более низким z-индексом, который содержит составной слой (другими словами, элемент отображается поверх составного слоя).
  • ..... Подробный список всех дел можно найти в статье Taobao fed:Оптимизация производительности беспроводной сети: Композитный

3D-преобразование, изменение набора непрозрачности, преобразование и т. д., а также содержит непрозрачность, преобразование CSS-переходов и анимацию для улучшения синтеза этих трех слоев случаев, часто встречающихся в центре внимания Пожалуйста, помните.

В дополнение к упомянутому выше прямому повышению уровня рендеринга до уровня графики, существует также следующее, поскольку B повышается, в результате чего A также повышается до уровня графики.Неявное продвижение, подробнее см. в этой статье:GPU Animation: Doing It Right

Каждый графический слой слоя композиции имеет графический контекст, а графический контекст открывает растровое изображение для слоя, что означает, что каждый графический слой имеет растровое изображение. Графический слой отвечает за отрисовку объекта рендеринга, содержащегося в его собственном слое рендеринга и его потомках, в растровое изображение. Затем растровое изображение передается графическому процессору в виде текстуры. Так что теперь GPU получает текстуру графического слоя HTML-элемента, а также может получать некоторые атрибуты, которые повышаются до Graphics из-за 3D-преобразования. Текстура элемента слоя.

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

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

Описанный выше процесс слияния после наслоения можно описать картинкой:

Бетонная реализация рисунка

структура системы

обработать

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

  • Процесс визуализации
    Основной процесс, по одному на вкладку. Отвечает за выполнение JS и рендеринга страниц. Содержит 3 потока: Compositor Thread, Tile Worker, Main thread, эти три потока будут представлены позже.
  • Процесс графического процессора
    Один на весь браузер. Он в основном отвечает за загрузку растрового изображения тайла, нарисованного в процессе рендеринга, в графический процессор в виде текстуры и вызов соответствующих методов графического процессора для отрисовки текстуры на экран (как правило, в статьях, посвященных механизмам рендеринга браузера, используется слово paint для описания растеризация контента и отрисовка в битмап, а слово отрисовка означает, что GPU в итоге выводит текстуру на экран), так что процесс в этом CPU надо называть "процессом, отвечающим за работу с GPU", а не так, как я понял GPU и думаю, что это процесс в GPU, mdzz. В процессе GPU есть только один поток: GPU Thread.

Три потока процесса визуализации

  • Compositor Thread
    Этот поток отвечает не только за получение сигнала вертикальной синхронизации (Vsync, горизонтальная синхронизация означает отрисовку строки строк экрана, вертикальная синхронизация означает, что отрисовка сверху вниз экрана завершена, указывая на конец предыдущего кадр и начало нового кадра), а также отвечает за получение действий пользователя от ОС, таких как прокрутка, ввод, щелчки, движения мыши и т. д.
    Если возможно, поток компоновщика будет непосредственно отвечать за обработку этих входных данных, затем преобразовывать их в смещение и обработку слоя и передавать новый кадр непосредственно в поток графического процессора, тем самым напрямую выводя новую страницу. В противном случае, например, если вы прописываете обратные вызовы на прокрутку, события ввода и т.д., или на текущей странице есть анимация, то в это время Композитор Поток разбудит основной поток, позволит последнему выполнить JS, завершить процесс перерисовки, перестановки и т. д. и сгенерировать новые текстуры, а затем поток компоновщика передаст соответствующие текстуры потоку графического процессора для завершения вывода.
  • Main Thread

    Здесь все очень хорошо знакомы Содержимое, отображаемое в столбце «Основной» временной шкалы chrome devtools, — это связанные задачи, выполненные основным потоком: выполнение определенного JS, пересчет Стиль, обновление дерева слоев, рисование, составные слои и многое другое.
  • Compositor Tile Worker(s)
    Может быть один или несколько потоков, например 2 или 4 для Chrome на стороне ПК и 1 или 2 для Android и Safari. Он создается потоком компоновщика и специально используется для работы с растеризацией тайлов (растеризация, упомянутая выше).

Видно, что Compositor Thread — это очень коренная вещь, а последние два потока в основном управляются им.
В то же время пользовательский ввод напрямую вводится в поток компоновщика.С одной стороны, в сценариях, которые не требуют выполнения анимации JS или CSS, перерисовки и т. д., пользовательский ввод может обрабатываться напрямую и реагировать на него, в то время как Основной поток очень сложный поток задач. Это позволяет браузеру быстро реагировать на пользовательский ввод при прокрутке, наборе текста и т. д., вообще не входя в основной поток. Здесь также есть очень важный момент, о котором пойдет речь далее.
Кроме того, даже если вы зарегистрируете обратный вызов для взаимодействия с пользовательским интерфейсом, войдете в основной поток или основной поток сильно застрянет, но поскольку Composer Поток блокируется вне его, поэтому Поток-композитор все еще может нести прямую ответственность за вывод следующего кадра на страницу, поэтому даже если ваш основной поток может выполнять высокоинтенсивные задачи более 16 мс, браузер все равно может это делать, когда вы прокручивать страницу.Отзывчивый (за исключением специальных задач, таких как синхронный AJAX), так например у вас есть относительно застрявшая анимация (процесс предварительного расчета или процесс перерисовки анимации превышает 16 мс на кадр), но вы очень плавно прокручиваете страницу , то есть карта анимации при прокрутке не залипает(Просто дайте вам демо и попробуйте сами).

конкретный процесс

Как правило, мы, вероятно, увидим следующий процесс на временной шкале devtools:

Выполнение JS запускается после операции перераспределения перерисовки. Здесь мы сосредоточимся на процессе, стоящем за операцией, то есть на этой паре карт:

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

Общий процесс:

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

    Все обработчики событий ввода (touchmove, scroll, click) должны срабатывать первыми, один раз за кадр, но это не обязательно так: планировщик делает все возможное, успех которого зависит от операционной системы.

    Это означает, что хотя поток композитора может получать несколько входных данных от ОС в течение 16 мс, соответствующие события запускаются и передаются в основной поток для обработки JS один раз за кадр или даже реже, чем один раз за кадр. Другими словами, такие события, как touchmove и mousemove, выполняются один раз за кадр с максимальной скоростью, поэтому они имеют эффект дросселирования по сравнению с анимацией! Если ваш основной поток немного застрял с анимацией или чем-то подобным, частота событий, скорее всего, будет ниже 16 мс. В содержании о времени рендеринга в начале я сказал, что прокрутка и изменение размера находятся в том же раунде, что и рендеринг, поэтому они будут выполняться один раз за кадр с максимальной скоростью.Теперь это не просто прокрутка и изменение размера! Даже touchmove, mousemove и другие события остаются теми же из-за механизма Compositor Thread.!
    См. этот jsfiddle для деталей, вы можете попробовать, вы можете обнаружить, что частота вызовов обратного вызова mousemove и обратного вызова requestAnimationFrame точно такая же, а количество выполнений mousemove точно такое же, как и у raf.навсегдаНикогда не было случая, чтобы mousemove выполнялся дважды, а rAF не выполнялся ни разу. Два других интервала выполнения составляют от 14 до 20 мс, в основном потому, что интервал кадра не будет точным до 16,666 мс, что в основном колеблется между 14 мс и 20 мс.Вы можете открыть временную шкалу для наблюдения.Еще одно странное явление заключается в том, что каждый раз, когда мышь перемещается из devtool обратно в область страницы, mousemove будет запускаться дважды очень быстро (интервал иногда меньше 5 мс), хотя за ним по-прежнему следует raf после каждого перемещения мыши, что означает, что два кадра запускаются очень быстро.

  3. requestAnimationFrame
    Красная линия на картинке означает, что вы можете быть в JSForce Layout, то есть доступ к scrollWidth, clientHeight, ComputedStyle и т. д. вызывает принудительное перекомпоновку, что приводит к тому, что Recalc Styles и Layout продвигаются вперед в процессе выполнения кода.
  4. parse HTML
    Если есть изменение DOM, то будет этот процесс разбора DOM.
  5. Recalc Styles
    Если вы измените стили или измените DOM во время выполнения JS, то этот шаг будет выполнен для пересчета стилей указанного элемента и его дочерних элементов.
  6. Layout
    Мы часто говорим перестановка оплавления. Если есть изменения DOM или изменения стиля, связанные с информацией о положении элемента, браузер пересчитает информацию о положении и размере всех элементов. Простое изменение цвета, фона и т. д. не приведет к перестановке. смотрите подробностиcss-triggers.
  7. update layer tree
    Этот шаг фактически предназначен для обновления отношения каскадного порядка слоя рендеринга, о чем мы говорили ранее, чтобы получить каскадный контекст.Поскольку соответствующая информация о стиле и перестановке были обновлены ранее, каскадная ситуация также может измениться.
  8. На самом деле Paint состоит из двух шагов: первый шаг — записать, какие вызовы рисования должны быть выполнены, а второй шаг — выполнить эти вызовы рисования. Первый шаг — просто сериализовать требуемую запись операции в структуру данных с именем SkPicture:

    The SkPicture is a serializable data structure that can capture and then later replay commands, similar to a display list.

    Этот SkPicture на самом деле является списком, в котором записаны ваши команды. На следующем втором шаге будут воспроизведены операции в SkPicture, а вот собственно выполнение этих операций: растеризация и заливка в битмап. Paint в основном потоке и то, что мы видим на временной шкале, на самом деле является первой операцией Paint. Второй шаг — это последующий шаг растеризации (см. далее).

  9. Composite
    Этот шаг в основном потоке будет вычислять данные, необходимые для синтеза каждого графического слоя, включая параметры для таких операций, как перемещение, масштабирование, вращение и альфа-смешивание, и передавать их в поток компоновщика, а затем первую фиксацию, которую мы см. на картинке: Основной поток говорит Композиторному потоку: я закончил, вы берете на себя. Тогда основной поток выполнит requestIdleCallback в это время. Этот шаг ничего не делает для графики. Слои завершают композицию растрового изображения.
  10. На этом этапе выполняются записи SkPicture, созданные на шаге 8 Raster Scheduled и Rasterize.

    Записи SkPicture в потоке компоновщика превращаются в растровые изображения на графическом процессоре одним из двух способов: либо растровые изображения растеризатора Skia и загружаются в графический процессор в виде текстуры, либо рисуются бэкэндом Skia OpenGL. (Ганеш) прямо в текстуры на графическом процессоре.

    Видно, что растеризация на самом деле имеет две формы:

    • Один основан на процессоре, с использованием программной растеризации библиотеки Skia, сначала нарисованной в растровое изображение, а затем снова в виде текстуры, загруженной на графический процессор. Таким образом, поток компоновщика порождает один или несколько рабочих потоков компоновщика плиток, а затем несколько потоков выполняют параллельную операцию рисования записей SkPicture, чтобы графический слой перед введением блока рисовал графический слой в объекте рендеринга. И процесс разбивается на множество меньших слоев тайла, записывающих в тайл соответствующий растрированному растровому изображению.
    • Другой — аппаратная растеризация на основе графического процессора, которая также основана на рабочем потоке композитных плиток, который также делится на плитки, но этот процесс не отображается в растровое изображение в ЦП, как программная растеризация, а затем загружается в ГП как текстура. Вместо этого используйте серверную часть Skia OpenGL (Ganesh) для рисования и растеризации непосредственно в текстуру в графическом процессоре, заполняя пиксели. Также известен как GPU Raster.

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

    Преимущество использования аппаратной растеризации заключается в том, что предыдущий метод программной растеризации ограничен пропускной способностью загрузки перед ЦП и ГП, а процесс загрузки растрового изображения из ОЗУ в видеопамять ГП имеет незначительную нагрузку на производительность. Если область растеризации большая, то использование программной растеризации, скорее всего, застрянет здесь. В следующем примере сравниваются Chrome32 и Chrome41, последняя версия реализует Hardware Растеризация.

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

  11. commit
    Если это программная растеризация, поток компоновщика обязуется уведомить поток графического процессора после завершения растеризации всех тайлов, поэтому все растровые изображения тайлов будут загружены в графический процессор потоком графического процессора в виде текстур. Если вы используете аппаратную растеризацию графического процессора, то в это время текстуры уже находятся в графическом процессоре. Затем поток GPU вызовет 3D API, соответствующий платформе (под окнамиD3D, другие платформы — GL), рисуем все текстуры в финальное растровое изображение, тем самым завершая слияние текстур.
    В то же время очень важный момент: при слиянии текстур с помощью соответствующих параметров композитинга 3D API можно выполнять преобразования текстур (то есть вышеупомянутые смещения, повороты, масштабирование, изменения альфа-канала и т. д.). выполняется перед слиянием, сначала преобразуйте, а затем слейте. После завершения слияния содержимое может быть представлено на экране.

Не каждый рендеринг будет выполнять все вышеперечисленные 11 шагов, таких как Layout, Paint, Rasterize, commit не может быть выполнен один раз, но Layout может выполняться более одного раза. Также существует принцип использования бустинга слоя композитинга для получения анимации с ускорением на GPU и других сопутствующих технологий. Ниже приводится более подробный анализ вышеперечисленных шагов.

Макет перекомпоновки, Принудительная перекомпоновка Принудительная компоновка

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

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

Следует отметить, что браузер не переформатируется до следующего кадра, следующего рендеринга. Дело не в том, что JS перекомпоновывается сразу после выполнения этой строки оператора, изменяющего стиль, поэтому вы можете написать 100 строк оператора, изменяющего CSS, в операторе JS, но он будет перекомпоновываться только один раз в следующем кадре.

Если вы обращаетесь к таким атрибутам, как offsetTop и scrollHeight, когда текущий макет помечен как грязный, браузер немедленно переформатирует макет и рассчитает правильную информацию о позиции элемента в это время, чтобы убедиться, что offsetTop, полученный вами в JS, scrollHeight и т. д., верен. .

Свойства и методы, запускающие перекомпоновку:

Этот процесс называется принудительной перекомпоновкой Force Layout, который заставляет браузер выполнять процесс Layout, который изначально выполнялся в описанном выше процессе рендеринга.помещениек процессу выполнения JS. Предпосылка не является проблемой, проблема в том, что каждый раз, когда вы обращаетесь к свойству, которое запускает перекомпоновку, когда макет грязный, он принудительно запускает макет, что значительно снижает эффективность выполнения JS.

//Layout未dirty 访问domA.offsetWidth不会Force Layout
domA.style.width = (domA.offsetWidth + 1) + 'px' 
//Layout已经dirty, Force Layout
domB.style.width = (domB.offsetWidth + 1) + 'px' 
//Layout已经dirty, Force Layout
domC.style.width = (domC.offsetWidth + 1) + 'px'

Последние две строки из этих трех строк кода ведут к Force Layout. Время для одного макета варьируется от десятков микросекунд до более десяти миллисекунд в зависимости от порядка величины DOM. По сравнению со временем выполнения строки JS в менее 1 микросекунды, эти накладные расходы трудно принять. Таким образом, существуют способы избежать Force Layout, такие как разделение операций чтения и записи и чистое хранение переменных. В противном случае вы увидите этот вид пересчета стиля и макета более 10 раз на временной шкале.

Кроме того,После каждого перекомпоновки или принудительной перекомпоновки текущий макет больше не загрязняется.. Поэтому, если вы снова получите доступ к таким свойствам, как offsetWidth, это больше не будет вызывать перекомпоновку.

// Layout未dirty 访问多少次都不会触发重排
console.log(domA.offsetWidth) 
console.log(domB.offsetWidth) 

//Layout未dirty 访问domA.offsetWidth不会Force Layout
domA.style.width = (domA.offsetWidth + 1) + 'px' 
//Layout已经dirty, Force Layout
console.log(domC.offsetWidth) 

//Layout不再dirty,不会触发重排
console.log(domA.offsetWidth) 
//Layout不再dirty,不会触发重排
console.log(domB.offsetWidth)

Перерисовать краску

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

Однако есть очень критическое поведение, а именно:Перерисовка производится в единицах композитных слоев.. то естьНедействителен не весь документ и не отдельный элемент, а слой композиции, на котором расположен элемент.. Конечно, это также одно из первоначальных намерений разделить процесс рендеринга на рисование и композитинг:

Since painting of the layers is decoupled from compositing, invalidating one of these layers only results in repainting the contents of that layer alone and recompositing.

Вот две демонстрации:demo1а такжеdemo2

Две демонстрации почти идентичны, за исключением стиля .ab-right второй демонстрации с дополнительной строкой,will-change:transform;.Мы подчеркнули это, когда ввели составной слой ранее.will-change: transformПринудительно переместит элемент в составной слой.

.ab-right {
        will-change: transform; //多了这行
        position: absolute;
        right: 0;
}

Таким образом, во второй демонстрации есть два слоя композиции: слой композиции корневого элемента HTML и слой композиции, где находится .ab-right.

Затем мы изменили стиль элемента #target в js, поэтому слой композиции, в котором находится элемент #target (то есть слой композиции корневого элемента HTML), перерисовывается. В demo1 элемент .ab-right не был перемещен в составной слой, поэтому .ab-right также был перерисован. В demo2 элемент .ab-right не перерисовывается. Сначала посмотрите на demo1:


Очевидно, что .ab-right перерисовывается.


Судя по всему, demo2 перерисовывает только содержимое слоя композиции корневого элемента HTML.

Кстати, вы также можете щелкнуть столбец «Растер», чтобы увидеть конкретный процесс растеризации. Как было сказано выше, здесь операция в Paint действительно завершается, и содержимое отрисовывается в растровое изображение или текстуру, и выполняется оно тайлами.

Перекомпоновка, перерисовка и композитинг

Давайте сначала поговорим не по теме, как просмотреть составной слой:

Изменение некоторых свойств CSS, таких как ширина, плавание, граница, положение, размер шрифта, выравнивание текста, переполнение-y и т. д., вызовет перекомпоновку, перерисовку и композицию, а также изменит другие свойства, такие как цвет, цвет фона, видимость и т. д. text-decoration и т. д. не вызывают перестановку, только перерисовку и синтез, пожалуйста, погуглите для конкретного списка атрибутов.

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

Тем не менее, это не так.

Это верно только после того, как элемент был переведен в составной слой.

Вернемся к шагу 11 процесса рендеринга, о котором мы говорили ранее:

В то же время очень важный момент: при слиянии текстур с помощью соответствующих параметров композитинга 3D API можно выполнять преобразования текстур (то есть вышеупомянутые смещения, повороты, масштабирование, изменения альфа-канала и т. д.). выполняется перед слиянием, сначала преобразуйте, а затем слейте. После завершения слияния содержимое может быть представлено на экране.

При синтезе нескольких слоев композитинга соответствующие параметры 3D API можно использовать для прямой реализации эффектов преобразования и непрозрачности слоя композитинга. Таким образом, если вы переместите элемент в составной слой, а затем измените его преобразование или непрозрачность с помощью JS или примените переходы или анимацию CSS к преобразованию или непрозрачности, это действительно позволит избежать процесса рисования ЦП, потому что преобразование и непрозрачность могут быть напрямую основаны на параметры композитинга графического процессора.

Однако это делается только при наличии преобразования или непрозрачности в слое композиции в целом. Для элементов, которые не переведены в составной слой, они имеют только преобразование и непрозрачность и являются содержимым составного слоя. Содержимое слоя синтеза и запись в растровое изображение или текстуру выполняются на этапах Paint и Rasterize, поэтому реализация преобразования и непрозрачности этого элемента также выполняется в Paint и Rasterize. Так что он все равно будет переставлен, а анимация с GPU-ускорением, о которой мы часто говорим, не включена.

Напримерэто демо, div#father, который продвигается к составному слою, и div#child, который не продвигается к составному слою, JS изменяет свойства преобразования дочернего и родительского элементов через 3 секунды. Каков процесс рендеринга дальше?

  1. Пересчет стилей
  2. Paint рисует меняющийся составной слой, т.е. div#father
    1. Paint рисует фон и textNode родительского элемента (т. е. «родительский элемент продвигается к составному слою»)
    2. Paint рисует дочерний элемент, т.е. div#child
      1. Paint сначала переводит, завершает ход
      2. Затем Paint рисует фон и textNode дочернего элемента в перемещенной области (т. е. «дочерний элемент не продвигается к составному слою»)
  3. Rasterize
  4. Composite объединяет композитный слой и завершает смещение, вращение и другие преобразования композитного слоя с помощью соответствующих композитных параметров 3D API во время синтеза, поэтому здесь реализован перевод div#father

Итак, мы видим, что для элементов, которые не были повышены до слоя композиции, их преобразование, непрозрачность и т. д. реализуются в основном потоке с помощью Paint и Rasterize (особенно это касается других свойств, которые необходимо перерисовать), и перерисовка все равно будет запущена. Изменение этих двух свойств напрямую с помощью JS не повысит производительность. Если элемент был повышен до слоя композиции, то реализация его преобразования, непрозрачности и других стилей напрямую контролируется потоком графического процессора для завершения композитинга в графическом процессоре.Шаг Composite основного потока вычисляет только параметры композиции, что занимает очень мало времени, скорость очень высокая, поэтому есть опыт использования трансформации и непрозрачности, чтобы максимально завершить анимацию.

заниматьПримеры в этой статье:

div {
    height: 100px;
    transition: height 1s linear;
}

div:hover {
    height: 200px;
}

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

И если код станет таким

div {
    transform: scale(0.5);
    transition: transform 1s linear;
}

div:hover {
    transform: scale(1.0);
}


То есть Main Thread не нужно переставлять или перерисовывать, и Draw им не делается, его шаг Composite просто вычисляет конкретные параметры Compositing (в примере правая сторона должна быть Compositor и GPU Thread, а автор для упрощения концепции и облегчения объяснения, нет прямого упоминания GPU Тема, не вычитать подробности здесь).

Кроме того, почему div продвигается на составной слой во втором примере, на самом деле то, что я сказал, когда составной слой был представлен ранее:

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

Содержимое в круглых скобках также имеет решающее значение.Когда элемент имеет свойства анимации, такие как непрозрачность, он не перемещается непосредственно на слой композиции, а продвигается на слой композиции, когда начинается анимация или переход, а продвижение слоя композиции после конец неверный.
В то же время, когда элемент повышается до составного слоя или происходит сбой повышенного составного слоя, это вызывает перерисовку. Это также начало изображения выше, прежде чем начнется анимация.Layout the element first timeа такжеPaint the element into the bitmapПричина для двух этапов: до начала перехода, Div не продвигается на слой композиции. Когда переход запускается, DIV немедленно продвигает слой композиции, который сразу же вызывает слой композиции, где он изначально располагается для перерисания Поскольку DIV, продвигаемый к слою композиции, должен быть удален), и поскольку div модернизируется до составного слоя, он также немедленно перерисован, а два композитных слоя REDRAWN растилизированы и загружены в график.

демо здесь, поэтому перед началом анимации вы увидите:

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

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

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

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

Синтетические слои подняты не до серебра.

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

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

Суммировать

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

Здесь я снова подчеркиваю некоторые вещи, которые ниспровергли мое восприятие:

  • В соответствии со стандартом HTML5 событие прокрутки запускается один раз за кадр с собственным эффектом регулирования requestAnimationFrame.
  • Согласно осуществлению двигателей Blink и WebKit, входы пользовательского интерфейса, такие как TouchMove и MouseMove, получены потоком композитора, но вход в основной нить один раз на кадру, и он также поставляется с эффектом дроссельной связи.
  • Перерисовка производится в единицах композитных слоев.
  • Шаги покраски до и после снятия композитного слоя

использованная литература

Официальная информация о Хроме

механизм рендеринга

Практическая эксплуатация и оптимизация производительности