WebGL контекст и низкоуровневое API

Deck.gl построен как слой над низкоуровневым графическим API и почти полностью опирается на прямое управление WebGL-контекстом. Архитектура библиотеки рассчитана на высокую производительность при отрисовке миллионов геометрических примитивов, поэтому контроль над контекстом и ресурсами GPU вынесен в отдельный уровень абстракции, но не скрыт полностью.

WebGL-контекст является центральной точкой всей графической системы. Он создаётся один раз на canvas-элементе и затем переиспользуется всеми слоями и шейдерами.

Внутри deck.gl контекст создаётся через механизм, который учитывает:

  • поддержку WebGL1 / WebGL2
  • наличие расширений (extensions)
  • ограничения устройства
  • совместимость с контекстными флагами (alpha, depth, stencil, antialias)

Типичная инициализация включает получение контекста из canvas:

const gl = canvas.getContext('webgl2', {
  alpha: true,
  depth: true,
  stencil: false,
  antialias: true,
  preserveDrawingBuffer: false
});

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

Ключевая особенность заключается в том, что deck.gl не создаёт отдельный WebGL-контекст на каждый слой. Вместо этого применяется модель «один контекст — множество слоёв», что позволяет минимизировать переключения состояния GPU.

Управление состоянием WebGL

WebGL по своей природе является stateful API. Любое изменение состояния (blend mode, depth test, bound buffers) влияет на последующие draw calls. Это создаёт проблему конфликтов между слоями.

Внутри deck.gl реализован слой управления состоянием, который:

  • кэширует текущие состояния контекста
  • сравнивает новое состояние с текущим
  • применяет изменения только при необходимости
  • минимизирует дорогостоящие вызовы gl.enable / gl.disable

Основные категории состояния:

  • состояние рендеринга (blend, depth test, cull face)
  • привязка буферов (ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER)
  • текущие шейдерные программы
  • текстурные слоты
  • framebuffer binding

Каждый слой при рендере описывает «желаемое состояние», после чего система приводит WebGL-контекст к этому состоянию минимальным числом операций.

Контекст как ресурсный менеджер

WebGL-контекст в deck.gl рассматривается не только как API отрисовки, но и как менеджер GPU-ресурсов.

Через него управляются:

  • буферы вершин (VertexBuffer)
  • индексные буферы (IndexBuffer)
  • текстуры (Texture2D, TextureCube)
  • framebuffers (FramebufferObject)
  • программы шейдеров (ShaderProgram)

Каждый из этих ресурсов привязан к конкретному контексту. При уничтожении контекста автоматически освобождаются все GPU-ресурсы.

Это особенно важно в браузерной среде, где потеря контекста (context loss) является штатным событием.

Обработка потери WebGL-контекста

Потеря контекста происходит при:

  • перегрузке GPU
  • переключении вкладок
  • ограничениях драйвера
  • аппаратных сбоях

deck.gl реализует стратегию восстановления:

  1. фиксация события webglcontextlost
  2. предотвращение дефолтного поведения браузера
  3. освобождение зависимых ресурсов
  4. повторная инициализация всех буферов и шейдеров
  5. восстановление сцены из сериализованного состояния

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

Низкоуровневое API и абстракция над WebGL

Хотя deck.gl предоставляет высокоуровневые слои (layers), внутри используется низкоуровневая прослойка, близкая к чистому WebGL.

Основные элементы этой прослойки:

  • Model — инкапсуляция геометрии и шейдеров
  • Geometry — описание вершин и индексов
  • Attribute — привязка данных к атрибутам шейдера
  • Program — компиляция GLSL шейдеров
  • Uniforms — передача параметров в GPU

Model как центральная сущность

Модель объединяет:

  • vertex shader
  • fragment shader
  • геометрию
  • атрибуты
  • uniform-параметры

Примерно на уровне концепции:

const model = new Model(gl, {
  vs: vertexShaderSource,
  fs: fragmentShaderSource,
  geometry: new Geometry({
    attributes: {
      positions: new Float32Array([...])
    }
  })
});

При вызове model.draw() происходит:

  • активация WebGL программы
  • биндинг буферов
  • установка uniforms
  • вызов drawArrays / drawElements

WebGL2 и расширения контекста

При наличии WebGL2 deck.gl переключается на расширенные возможности:

  • Vertex Array Objects (VAO)
  • instanced rendering
  • 3D textures
  • multiple render targets (MRT)
  • более эффективные buffer streaming механизмы

Если WebGL2 недоступен, используется набор WebGL1-совместимых fallback-стратегий.

Особое значение имеют расширения:

  • OES_element_index_uint
  • ANGLE_instanced_arrays
  • OES_texture_float
  • WEBGL_depth_texture

Каждое расширение проверяется при инициализации контекста и сохраняется в виде capability-флага.

Управление draw pipeline

Рендеринг в deck.gl построен вокруг последовательности:

  1. подготовка слоя
  2. обновление атрибутов
  3. биндинг ресурсов
  4. настройка состояния WebGL
  5. выполнение draw call

Каждый слой может генерировать несколько draw calls, но система старается минимизировать их количество через batching.

Ключевой механизм — инстансинг:

  • один набор геометрии
  • множество экземпляров
  • разные атрибуты на экземпляр

Это снижает нагрузку на CPU и уменьшает количество переключений контекста.

Буферизация и стриминг данных

WebGL требует явного управления буферами. deck.gl использует стратегию частичного обновления:

  • создание buffer once
  • последующее обновление через subData
  • избежание полной пересборки буфера

Типичный поток данных:

  • CPU массив данных
  • трансформация в typed array
  • загрузка в GPU buffer
  • привязка к attribute location

При больших датасетах применяется chunking — разбиение данных на блоки.

Связь с системой рендера deck.gl

WebGL-контекст интегрирован в более высокоуровневую систему рендеринга:

  • Deck instance управляет жизненным циклом контекста
  • Layer система описывает декларативный рендеринг
  • Core engine преобразует декларации в WebGL calls

Каждый цикл рендера включает:

  • diff состояния слоёв
  • обновление ресурсов
  • синхронизацию с GPU
  • выполнение команд рендера

Изоляция WebGL состояния между слоями

Одной из ключевых проблем является конфликт состояния WebGL между слоями.

Для решения применяется:

  • state tracking cache
  • scoped state application
  • reset pipeline between layers (частично)
  • explicit binding management

Каждый слой не должен предполагать состояние, оставленное предыдущим слоем.

Framebuffer и offscreen rendering

WebGL-контекст используется не только для прямого рендера в canvas, но и для offscreen-операций:

  • генерация текстур
  • постобработка
  • picking (определение объектов под курсором)
  • генерация depth buffer

Framebuffer объект позволяет перенаправить вывод GPU:

  • рендер в texture
  • последующая композиция
  • использование результата в следующем проходе

Это критично для интерактивных визуализаций.

Pick-проход как отдельное использование контекста

Picking реализуется через отдельный render pass:

  • каждому объекту назначается уникальный id
  • id кодируется в цвет
  • рендер выполняется в offscreen framebuffer
  • чтение пикселя через readPixels

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

Производительность и ограничения контекста

WebGL-контекст ограничен аппаратно и программно:

  • ограниченное число uniform slots
  • лимит текстурных единиц
  • размер buffer uploads
  • стоимость state changes

deck.gl минимизирует влияние этих ограничений через:

  • батчинг draw calls
  • переиспользование шейдеров
  • атрибутный стриминг
  • кеширование uniform значений

Абстракция поверх WebGL как компромисс

Несмотря на наличие низкоуровневого доступа, deck.gl не превращается в чистый WebGL wrapper. Абстракция сохраняет баланс:

  • низкоуровневый контроль над GPU
  • декларативное описание визуализации
  • автоматическое управление состоянием
  • оптимизация draw pipeline

WebGL-контекст остаётся фундаментом, но большая часть прямых вызовов изолирована внутри системных модулей, что позволяет сочетать производительность и масштабируемость визуализаций.