Графические приложения в браузере долгое время строились на основе WebGL — API, предоставляющего доступ к возможностям GPU через интерфейс, основанный на принципах OpenGL ES. Появление WebGPU изменило модель взаимодействия между JavaScript и графическим процессором, предложив значительно более низкоуровневый и предсказуемый механизм управления ресурсами и командами.
В контексте библиотеки Babylon.js оба интерфейса поддерживаются через разные движки рендеринга:
WebGLRenderingEngineWebGPUEngineОсновное отличие между ними заключается в подходе к управлению состояниями и выполнением команд. WebGL использует состояние-ориентированную модель, в которой состояние графического конвейера изменяется через последовательность вызовов функций. WebGPU реализует командно-буферную архитектуру, где команды записываются заранее и отправляются на GPU пакетами.
Эта разница оказывает прямое влияние на производительность, особенно в сложных сценах с большим количеством объектов и операций.
WebGL построен на принципах немедленного режима исполнения команд. Каждый вызов API немедленно влияет на состояние графического контекста.
Пример типичной последовательности:
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(0);
gl.drawArrays(gl.TRIANGLES, 0, vertexCount);
Каждый вызов:
Основные ограничения:
1. Высокая стоимость вызовов API
Каждый вызов WebGL проходит через слой валидации браузера.
2. Частые переключения состояния
Изменение:
вызывает дополнительные накладные расходы.
3. Ограниченный контроль над памятью
WebGL автоматически управляет многими ресурсами, что снижает контроль над временем выделения и освобождения памяти GPU.
4. Низкая эффективность при большом количестве draw calls
Сцены с тысячами объектов приводят к значительным накладным расходам.
WebGPU проектировался как современный графический API, близкий по концепции к:
Основная идея — разделение подготовки команд и их выполнения.
Типичный цикл WebGPU включает:
Пример (упрощённая схема):
const commandEncoder = device.createCommandEncoder();
const pass = commandEncoder.beginRenderPass(renderPassDescriptor);
pass.setPipeline(pipeline);
pass.setVertexBuffer(0, vertexBuffer);
pass.draw(vertexCount);
pass.end();
device.queue.submit([commandEncoder.finish()]);
1. Предварительная запись команд
Команды формируются заранее и выполняются GPU без дополнительных проверок.
2. Минимизация синхронизации CPU–GPU
Браузер не вмешивается в каждый вызов API.
3. Явное управление ресурсами
Разработчик контролирует:
4. Высокая масштабируемость
Большое количество draw calls обрабатывается значительно эффективнее.
Babylon.js реализует WebGPU через отдельный движок:
const engine = new BABYLON.WebGPUEngine(canvas);
await engine.initAsync();
После инициализации API Babylon.js остаётся практически неизменным:
const scene = new BABYLON.Scene(engine);
Это позволяет переключать backend без изменения логики сцены.
Внутри движка происходят существенные изменения:
| Подсистема | WebGL | WebGPU |
|---|---|---|
| Шейдеры | GLSL | WGSL |
| Буферы | GL Buffer | GPUBuffer |
| Текстуры | WebGLTexture | GPUTexture |
| Команды | immediate mode | command buffers |
Один из главных факторов производительности графического приложения — количество draw calls.
Каждый draw call требует:
В сценах с тысячами объектов производительность резко падает.
Типичный предел:
2000–5000 draw calls
После этого наблюдается сильная нагрузка на CPU.
В WebGPU команды записываются в буфер и отправляются на GPU пакетно.
Это уменьшает накладные расходы CPU.
Практические тесты показывают, что WebGPU способен обрабатывать:
20 000 – 100 000 draw calls
без значительных потерь производительности.
Babylon.js дополнительно оптимизирует этот процесс через:
Шейдеры играют важную роль в производительности графического конвейера.
Использует язык GLSL.
Проблемы:
Пример:
gl.compileShader(shader);
gl.linkProgram(program);
При сложных сценах возможно заметное время загрузки.
Использует язык WGSL (WebGPU Shading Language).
Особенности:
Babylon.js автоматически преобразует шейдеры системы материалов в WGSL при использовании WebGPU.
WebGL скрывает многие аспекты управления памятью.
Например:
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
В этом случае браузер и драйвер GPU самостоятельно решают:
WebGPU требует явного указания параметров буфера:
device.createBuffer({
size: data.byteLength,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
});
Это позволяет:
Babylon.js автоматически создаёт такие буферы при загрузке геометрии.
WebGL работает преимущественно в одном потоке JavaScript.
Даже при использовании Web Workers передача данных
требует копирования или использования Transferable.
WebGPU изначально проектировался с учётом параллелизма.
Поддерживаются:
Это особенно важно для:
Babylon.js использует этот потенциал при:
Материалы в Babylon.js создают значительную нагрузку на графический конвейер.
Сложные материалы включают:
Каждый материал требует:
Это создаёт overhead.
Pipeline state создаётся заранее.
Babylon.js формирует pipeline cache, который снижает стоимость переключений.
Результат:
Постобработка (post-processing) включает:
Каждый эффект требует дополнительных проходов рендеринга.
Каждый проход:
Render pass может объединять несколько операций.
Это уменьшает:
Babylon.js оптимизирует post-processing через render graph, особенно эффективно при использовании WebGPU.
Передача данных между CPU и GPU является критическим фактором.
Обновление буфера:
gl.bufferSubData(...)
Может вызывать:
Использует staging buffers и асинхронные операции:
queue.writeBuffer(...)
Это позволяет:
Результаты тестирования сложных сцен показывают:
| Параметр | WebGL | WebGPU |
|---|---|---|
| draw calls | ~3000 | >30000 |
| CPU нагрузка | высокая | ниже |
| загрузка шейдеров | медленнее | быстрее |
| масштабируемость | ограничена | высокая |
Наиболее заметный прирост наблюдается в:
Несмотря на преимущества, WebGPU имеет ряд ограничений.
Поддержка постепенно расширяется, но всё ещё уступает WebGL.
Наиболее стабильная реализация присутствует в:
Safari и Firefox внедряют поддержку постепенно.
WebGPU требует:
Babylon.js скрывает большую часть этой сложности, однако внутренняя архитектура движка становится значительно более сложной.
Явное управление ресурсами может приводить к большему использованию VRAM, если ресурсы не освобождаются своевременно.
Максимальный прирост производительности наблюдается в следующих случаях:
Большие сцены
10 000+ объектов
Инстансинг
100 000+ экземпляров моделей
Сложные PBR материалы
Большое количество источников света
Интенсивная постобработка
В простых сценах разница между WebGL и WebGPU может быть минимальной, поскольку узким местом становится не GPU, а логика JavaScript.
Развитие движка Babylon.js постепенно смещается в сторону WebGPU.
Основные направления оптимизации:
Compute shaders открывают новые возможности:
Эти возможности практически недоступны в WebGL.
WebGL остаётся надёжным и широко поддерживаемым API для браузерной графики, однако его архитектура ограничивает масштабируемость современных приложений.
WebGPU предоставляет:
В контексте Babylon.js WebGPU становится основой для построения высокопроизводительных 3D-приложений следующего поколения.