От WebGL к WebGPU

Франсуа Бофор
François Beaufort

Как разработчик WebGL, вы можете быть одновременно напуганы и рады начать использовать WebGPU, преемника WebGL, который привносит в Интернет достижения современных графических API.

Приятно осознавать, что WebGL и WebGPU имеют много общих концепций. Оба API позволяют запускать на графическом процессоре небольшие программы, называемые шейдерами. WebGL поддерживает вершинные и фрагментные шейдеры, а WebGPU также поддерживает вычислительные шейдеры. WebGL использует язык шейдеров OpenGL (GLSL), а WebGPU использует язык шейдеров WebGPU (WGSL). Хотя эти два языка различны, основные концепции в основном одинаковы.

Учитывая это, в этой статье освещаются некоторые различия между WebGL и WebGPU, чтобы помочь вам начать работу.

Глобальное состояние

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

WebGPU — это API без сохранения состояния и не поддерживает глобальное состояние. Вместо этого он использует концепцию конвейера для инкапсуляции всего состояния рендеринга, которое было глобальным в WebGL. Конвейер содержит информацию о том, какое смешивание, топологию и атрибуты использовать. Конвейер неизменяем. Если вы хотите изменить некоторые настройки, вам нужно создать другой конвейер. WebGPU также использует кодировщики команд для группирования команд и их выполнения в том порядке, в котором они были записаны. Это полезно, например, при отображении теней, когда за один проход по объектам приложение может записать несколько потоков команд, по одному для каждой карты теней.

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

Больше не синхронизировать

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

Например, в WebGL вызов gl.getError() требует синхронного IPC от процесса JavaScript к процессу графического процессора и обратно. Это может вызвать пузырек на стороне ЦП при взаимодействии двух процессов.

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

Вычислительные шейдеры

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

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

Обработка видеокадров

Обработка видеокадров с использованием JavaScript и WebAssembly имеет некоторые недостатки: стоимость копирования данных из памяти графического процессора в память ЦП и ограниченный параллелизм, которого можно достичь с помощью рабочих потоков и потоков ЦП. WebGPU не имеет этих ограничений, что делает его идеальным для обработки видеокадров благодаря тесной интеграции с API WebCodecs .

В следующем фрагменте кода показано, как импортировать VideoFrame как внешнюю текстуру в WebGPU и обработать его. Вы можете попробовать эту демо-версию .

// Init WebGPU device and pipeline...
// Configure canvas context...
// Feed camera stream to video...

(function render() {
  const videoFrame = new VideoFrame(video);
  applyFilter(videoFrame);
  requestAnimationFrame(render);
})();

function applyFilter(videoFrame) {
  const texture = device.importExternalTexture({ source: videoFrame });
  const bindgroup = device.createBindGroup({
    layout: pipeline.getBindGroupLayout(0),
    entries: [{ binding: 0, resource: texture }],
  });
  // Finally, submit commands to GPU
}

Переносимость приложения по умолчанию

WebGPU заставляет вас запрашивать limits . По умолчанию requestDevice() возвращает GPUDevice, который может не соответствовать аппаратным возможностям физического устройства, а скорее является разумным и наименьшим общим знаменателем среди всех графических процессоров. Требуя от разработчиков запрашивать ограничения на количество устройств, WebGPU гарантирует, что приложения будут работать на как можно большем количестве устройств.

Обработка холста

WebGL автоматически управляет холстом после создания контекста WebGL и предоставления атрибутов контекста, таких как альфа, сглаживание, colorSpace, глубина, saveDrawingBuffer или трафарет.

С другой стороны, WebGPU требует, чтобы вы сами управляли холстом. Например, чтобы добиться сглаживания в WebGPU, вы должны создать текстуру с несколькими выборками и выполнить ее рендеринг. Затем вы преобразуете мультисэмпловую текстуру в обычную текстуру и рисуете эту текстуру на холсте. Такое ручное управление позволяет вам выводить на любое количество холстов из одного объекта GPUDevice . Напротив, WebGL может создавать только один контекст для каждого холста.

Ознакомьтесь с демо-версией WebGPU Multiple Canvases .

Кстати, в настоящее время браузеры имеют ограничение на количество холстов WebGL на странице. На момент написания Chrome и Safari могут одновременно использовать только до 16 холстов WebGL; Firefox может создать до 200 из них. С другой стороны, нет ограничений на количество холстов WebGPU на странице.

Снимок экрана с максимальным количеством холстов WebGL в браузерах Safari, Chrome и Firefox.
Максимальное количество холстов WebGL в Safari, Chrome и Firefox (слева направо) — демо .

Полезные сообщения об ошибках

WebGPU предоставляет стек вызовов для каждого сообщения, возвращаемого API. Это означает, что вы можете быстро увидеть, где в вашем коде произошла ошибка, что полезно для отладки и исправления ошибок.

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

WebGPU также позволяет вам предоставлять собственную label для каждого объекта WebGPU. Эта метка затем используется браузером в сообщениях GPUError, предупреждениях консоли и инструментах разработчика браузера.

От названий к индексам

В WebGL многие вещи связаны именами. Например, вы можете объявить юниформ-переменную с именем myUniform в GLSL и получить ее местоположение с помощью gl.getUniformLocation(program, 'myUniform') . Это очень удобно, поскольку при неправильном вводе имени универсальной переменной вы получите сообщение об ошибке.

С другой стороны, в WebGPU всё целиком связано смещением байта или индексом (часто называемым location ). Вы несете ответственность за синхронизацию местоположений кода в WGSL и JavaScript.

Генерация MIP-карт

В WebGL вы можете создать mip текстуры уровня 0, а затем вызвать gl.generateMipmap() . WebGL затем сгенерирует для вас все остальные уровни MIP.

В WebGPU вы должны сами генерировать MIP-карты. Для этого нет встроенной функции. См. обсуждение спецификации , чтобы узнать больше о решении. Вы можете использовать удобные библиотеки, такие как webgpu-utils, для создания MIP-карт или научиться делать это самостоятельно .

Буферы хранения и текстуры хранения

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

  • Данные буферов хранения, передаваемые в шейдеры, могут быть намного больше, чем универсальные буферы. Хотя в спецификации говорится, что размер привязки универсальных буферов может достигать 64 КБ (см. maxUniformBufferBindingSize ), максимальный размер привязки буфера хранилища составляет не менее 128 МБ в WebGPU (см. maxStorageBufferBindingSize ).

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

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

Текстуры хранилища поддерживаются только в WebGPU и по отношению к текстурам являются тем же, чем буферы хранения по отношению к универсальным буферам. Они более гибкие, чем обычные текстуры, и поддерживают произвольную запись (и чтение в будущем).

Изменения буфера и текстур

В WebGL вы можете создать буфер или текстуру, а затем изменить его размер в любое время, например, с помощью gl.bufferData() и gl.texImage2D() соответственно.

В WebGPU буферы и текстуры неизменяемы. Это означает, что вы не можете изменить их размер, использование или формат после их создания. Вы можете только изменить их содержимое.

Различия в космических соглашениях

В WebGL диапазон пространства отсечения Z составляет от -1 до 1. В WebGPU диапазон пространства отсечения Z составляет от 0 до 1. Это означает, что объекты со значением az, равным 0, находятся ближе всего к камере, а объекты со значением az из 1 находятся дальше всего.

Иллюстрация диапазонов пространства отсечения Z в WebGL и WebGPU.
Диапазон пространства отсечения Z в WebGL и WebGPU.

WebGL использует соглашение OpenGL, где ось Y направлена ​​вверх, а ось Z — к зрителю. WebGPU использует соглашение Metal, где ось Y находится внизу, а ось Z находится за пределами экрана. Обратите внимание, что направление оси Y направлено вниз по координате кадрового буфера, координате области просмотра и координате фрагмента/пикселя. В пространстве клипа направление оси Y по-прежнему вверх, как и в WebGL.

Благодарности

Благодарим Корентина Валлеза, Грегга Тавареса, Стивена Уайта, Кена Рассела и Рэйчел Эндрю за рецензирование этой статьи.

Я также рекомендую WebGPUFundamentals.org для более глубокого изучения различий между WebGPU и WebGL.