Offscreen вычисления

В основе высокопроизводительного рендеринга карт в Mapbox GL JS лежит жёсткое разделение вычислений между основным потоком браузера и фоновыми рабочими потоками. Главная цель такой архитектуры — исключить блокировки UI при обработке геоданных, рендеринге тайлов и пересчёте стилей.

Ключевая идея offscreen-вычислений заключается в переносе всех тяжёлых операций — парсинга векторных тайлов, построения геометрии, генерации буферов WebGL и подготовки стилей — в Web Worker и OffscreenCanvas, оставляя основному потоку только координацию событий и пользовательский ввод.


Модель потоков в Mapbox GL JS

Внутренняя архитектура Mapbox GL JS делится на несколько уровней:

  • Main thread (UI-поток) Обрабатывает:

    • события пользователя (pan, zoom, rotate)
    • управление экземпляром карты
    • интеграцию с DOM
    • управление слоями высокого уровня
  • Worker thread (offscreen вычисления) Отвечает за:

    • парсинг векторных тайлов
    • декодирование GeoJSON
    • генерацию геометрии
    • кластеризацию и агрегацию данных
    • вычисление выражений стилей
  • WebGL rendering context (часто через OffscreenCanvas) Выполняет:

    • отрисовку слоёв
    • управление буферами вершин
    • компоновку тайлов

Такое разделение позволяет масштабировать карту на больших наборах данных без деградации UX.


Web Worker как ядро offscreen-вычислений

В Mapbox GL JS Web Worker используется не как вспомогательный механизм, а как основной вычислительный слой.

Основные задачи worker-а:

1. Обработка векторных тайлов Векторные тайлы (MVT) поступают в бинарном виде и требуют:

  • декодирования protobuf
  • преобразования координат
  • распаковки геометрий
  • построения feature-структур

Эти операции являются CPU-интенсивными и полностью выносятся в worker.

2. Подготовка геометрии для WebGL После парсинга геометрия преобразуется в:

  • vertex buffers
  • index buffers
  • layout-структуры атрибутов

Именно здесь происходит оптимизация под GPU-рендеринг.

3. Стилизация на уровне данных Mapbox Style Specification включает выражения, которые вычисляются не на UI-уровне, а в worker:

  • фильтрация слоёв
  • вычисление свойств цвета, толщины, прозрачности
  • интерполяции (zoom-based styling)

OffscreenCanvas и WebGL вне main thread

С появлением OffscreenCanvas рендеринг может полностью покинуть основной поток.

В Mapbox GL JS это используется следующим образом:

  • Canvas создаётся в main thread
  • Передаётся в worker через transferControlToOffscreen
  • WebGL контекст создаётся внутри worker-а
  • Рендеринг выполняется асинхронно

Схема взаимодействия:

  1. Main thread создаёт карту
  2. Canvas «передаётся» в worker
  3. Worker получает контроль над WebGL context
  4. Render loop выполняется в worker
  5. Main thread получает только события обновления состояния

Это критически снижает нагрузку на UI-поток при:

  • интенсивном зуме
  • анимациях
  • перерисовке больших слоёв

Поток данных между main thread и worker

Передача данных реализована через сериализованные сообщения (postMessage), где используются:

  • ArrayBuffer (для геометрии)
  • TypedArray (Float32Array, Uint16Array)
  • структурированные команды
  • JSON-описания стилей

Оптимизация передачи:

  • transferables вместо копирования
  • минимизация JSON на горячем пути
  • батчинг обновлений

Особенно важно, что геометрические данные не копируются, а передаются по владению буфера.


Offscreen вычисления в тайловой системе

Mapbox GL JS работает с тайловой системой, где каждый тайл — это независимая единица вычислений.

Offscreen pipeline тайла:

  1. Запрос тайла
  2. Декодирование (worker)
  3. Применение стиля (worker)
  4. Генерация геометрии (worker)
  5. Передача в GPU
  6. Рендер (worker/WebGL)

Преимущество этой модели — параллелизм:

  • несколько тайлов обрабатываются одновременно
  • загрузка CPU распределяется по ядрам
  • GPU получает уже готовые буферы

Декодирование и подготовка GeoJSON в offscreen режиме

GeoJSON кажется простым форматом, но в реальности он становится узким местом при больших объёмах данных.

Offscreen обработка включает:

  • парсинг JSON
  • нормализацию координат
  • фильтрацию свойств
  • упрощение геометрии (simplification)
  • генерацию индексированных структур

Особенно важна операция simplification:

  • удаление избыточных точек
  • оптимизация полилиний
  • снижение количества вершин для WebGL

Кластеризация как offscreen-операция

При работе с точечными данными (POI, события, маркеры) применяется кластеризация.

В offscreen-слое выполняется:

  • пространственная индексация (grid / quadtree)
  • объединение точек по zoom level
  • вычисление центров кластеров
  • агрегация метаданных

Эти операции невозможно выполнять в main thread без лагов при десятках тысяч точек.


Вычисление стилей в worker

Style system в Mapbox GL JS — это декларативная система, но под капотом она компилируется в вычисляемые выражения.

Offscreen вычисления включают:

  • интерполяцию по zoom
  • step expressions
  • match expressions
  • property functions

Например:

  • цвет слоя может зависеть от масштаба
  • толщина линии динамически пересчитывается
  • фильтры применяются до рендеринга

Вся логика выполняется до попадания данных в GPU pipeline.


Роль requestAnimationFrame и offscreen loop

В классической архитектуре UI управляет requestAnimationFrame. В offscreen-модели Mapbox GL JS:

  • render loop живёт в worker
  • основной поток лишь отправляет state updates
  • кадры синхронизируются через message passing

Это позволяет:

  • не блокировать UI при 60 FPS рендере
  • стабилизировать frame time
  • уменьшить jank при интеракциях

Инкрементальный рендеринг и offscreen оптимизация

Offscreen вычисления поддерживают инкрементальный подход:

  • пересчитываются только изменённые тайлы
  • стили применяются частично
  • геометрия обновляется диффами

Это особенно важно при:

  • динамических данных (real-time feeds)
  • изменении фильтров
  • анимации слоёв

Ограничения offscreen модели

Несмотря на эффективность, архитектура имеет ограничения:

  • стоимость сериализации данных между потоками
  • сложность отладки worker-логики
  • ограничения OffscreenCanvas в некоторых браузерах
  • задержки при больших transferable payloads

В ряде случаев main-thread fallback остаётся необходимым.


Оптимизационные стратегии

Для эффективной работы offscreen-вычислений применяются следующие подходы:

Минимизация данных

  • передача только необходимых свойств feature
  • удаление лишних атрибутов

Batch processing

  • объединение нескольких тайлов в один пакет
  • сокращение количества сообщений между потоками

Lazy evaluation

  • вычисление стилей только при необходимости
  • отсрочка тяжелых операций до zoom threshold

Memory pooling

  • повторное использование буферов
  • снижение GC pressure

Offscreen вычисления в кастомных слоях

При создании custom layers разработчик может встроиться в offscreen pipeline:

  • подготовка данных выполняется в worker
  • WebGL команды формируются вне main thread
  • рендер интегрируется в общий цикл карты

Это позволяет:

  • добавлять собственные GPU-эффекты
  • обрабатывать нестандартные источники данных
  • сохранять производительность ядра системы

Взаимодействие с GPU через offscreen слой

GPU получает уже подготовленные структуры:

  • vertex buffer objects (VBO)
  • index buffers
  • uniform parameters

Offscreen слой гарантирует, что:

  • данные валидны
  • координаты трансформированы
  • стили уже рассчитаны

Таким образом GPU не занимается логикой, а только отрисовкой.


Значение offscreen архитектуры для масштабируемости

Offscreen-вычисления превращают Mapbox GL JS в систему, способную:

  • обрабатывать миллионы объектов
  • поддерживать плавный zoom/rotate
  • работать с real-time потоками данных
  • масштабироваться на многослойные карты

Ключевой эффект — перенос вычислительной нагрузки с UI-потока на параллельные worker-слои и GPU-конвейер, что обеспечивает стабильное время кадра даже при высокой сложности сцены.