Рендеринг с малой задержкой и десинхронизированной подсказкой

Джо Медли
Joe Medley

Различия в рендеринге стилусом

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

desynchronized подсказка для canvas.getContext() вызывает другой путь кода, который обходит обычный механизм обновления DOM . Вместо этого подсказка сообщает базовой системе пропустить столько композиций, сколько возможно, а в некоторых случаях базовый буфер холста отправляется непосредственно в контроллер дисплея экрана. Это устраняет задержку, которая могла бы быть вызвана использованием очереди компоновщика рендеринга.

Насколько это хорошо?

Одновременный рендеринг Синтел

Если вы хотите перейти к коду, прокрутите вперед. Чтобы увидеть это в действии, вам понадобится устройство с сенсорным экраном и желательно стилус. (Пальцы тоже работают.) Если они у вас есть, попробуйте образцы 2d или webgl . Для остальных посмотрите демо Мигеля Касаса , одного из инженеров, реализовавших эту функцию. Откройте демо-версию, нажмите «Воспроизвести», затем быстро и в случайном порядке перемещайте ползунок взад и вперед.

В этом примере используется отрывок длительностью одну минуту двадцать одна секунда из короткометражного фильма «Синтел » Дуриана, открытого кинопроекта Blender. В этом примере фильм воспроизводится в элементе <video> , содержимое которого одновременно отображается в элементе <canvas> . Многие устройства могут делать это без разрыва, хотя устройства с рендерингом переднего буфера, такие как ChromeOS, например, могут иметь разрыв. (Фильм великолепный, но душераздирающий. Я был бесполезен в течение часа после того, как посмотрел его. Считайте, что вы предупреждены.)

Использование подсказки

Использование низкой задержки — это нечто большее, чем просто добавление desynchronized в canvas.getContext() . Я буду рассматривать вопросы по одному.

Создайте холст

Что касается другого API, я бы сначала обсудил обнаружение функций. Для desynchronized подсказки сначала необходимо создать холст. Вызовите canvas.getContext() и передайте ему новую desynchronized подсказку со значением true .

const canvas = document.querySelector('myCanvas');
const ctx = canvas.getContext('2d', {
  desynchronized: true,
  // Other options. See below.
});

Обнаружение функций

Затем вызовите getContextAttributes() . Если возвращаемый объект атрибутов имеет desynchronized свойство, проверьте его.

if (ctx.getContextAttributes().desynchronized) {
  console.log('Low latency canvas supported. Yay!');
} else {
  console.log('Low latency canvas not supported. Boo!');
}

Как избежать мерцания

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

Некоторые браузеры, включая Chrome, очищают холсты WebGL между кадрами. Контроллер дисплея может прочитать буфер, пока он пуст, что приведет к мерцанию рисуемого изображения. Чтобы избежать этого, установите для preserveDrawingBuffer значение true .

const canvas = document.querySelector('myCanvas');
const ctx = canvas.getContext('webgl', {
  desynchronized: true,
  preserveDrawingBuffer: true
});

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

Альфа-каналы

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

Может быть только один

Вы не можете изменить атрибуты контекста после первого вызова canvas.getContext() . Это всегда было правдой, но повторение этого может избавить вас от некоторого разочарования, если вы не знаете или забыли.

Например, предположим, что я получаю контекст и указываю альфа как false, а затем где-то позже в своем коде я вызываю canvas.getContext() во второй раз с альфа, установленным в значение true, как показано ниже.

const canvas = document.querySelector('myCanvas');
const ctx1 = canvas.getContext('2d', {
  alpha: false,
  desynchronized: true,
});

//Some time later, in another corner of code.
const ctx2 = canvas.getContext('2d', {
  alpha: true,
  desynchronized: true,
});

Неочевидно, что ctx1 и ctx2 — один и тот же объект. Альфа по-прежнему ложна, и контекст с альфа, равным истине, никогда не создается.

Поддерживаемые типы холстов

Первый параметр, передаваемый в getContext() — это contextType . Если вы уже знакомы с getContext() вам, несомненно, интересно, поддерживается ли что-либо кроме типов контекста «2d». В таблице ниже показаны типы контекстов, поддерживающие desynchronized .

тип контекста Объект типа контекста

'2d'

CanvasRenderingContext2D

'webgl'

WebGLRenderingContext

'webgl2'

WebGL2RenderingContext

Заключение

Если вы хотите увидеть больше этого, посмотрите образцы. В дополнение к уже описанному примеру видео есть примеры, показывающие контексты «2d» и «webgl» .