Worker threads

Mapbox GL JS строится вокруг строгого разделения задач между главным потоком браузера и Web Workers. Это разделение критично для обеспечения плавной отрисовки карты, высокой частоты кадров и отсутствия блокировок UI при обработке геоданных и стилей.

В основе лежит принцип: главный поток отвечает только за WebGL-контекст и взаимодействие с DOM, тогда как вся тяжёлая вычислительная работа переносится в worker threads.


Web Workers как вычислительное ядро Mapbox GL JS

В Mapbox GL JS используется набор специализированных Web Workers, которые выполняют задачи, не требующие доступа к DOM или WebGL-контексту.

Основные характеристики такого подхода:

  • изоляция памяти между потоками
  • обмен данными через message passing
  • отсутствие прямого доступа к DOM
  • возможность параллельной обработки тайлов и стилей

Worker запускается из отдельного JS-файла (worker bundle), который содержит упрощённую среду выполнения без браузерных API верхнего уровня.


Разделение обязанностей между потоками

Главный поток

Главный поток в Mapbox GL JS выполняет ограниченный набор задач:

  • создание и управление WebGL-контекстом
  • компоновка кадров (render loop)
  • обработка пользовательского ввода (pan, zoom, rotate)
  • применение результатов вычислений, полученных от workers
  • управление canvas и интеграция с DOM

Главная цель — поддерживать стабильный FPS без блокировок.


Worker threads

Worker threads берут на себя основную вычислительную нагрузку:

  • парсинг векторных тайлов (vector tiles)
  • декодирование GeoJSON
  • генерация геометрии для рендеринга
  • обработка стилей (style parsing и evaluation)
  • подготовка данных для WebGL буферов
  • вычисление layout слоёв и символов (labels)

Обработка векторных тайлов в worker threads

Одной из ключевых задач является обработка vector tiles формата Mapbox Vector Tile (MVT).

В worker thread происходит:

  • декодирование protobuf-структур тайла
  • преобразование геометрии в внутренние представления
  • сегментация по слоям стиля
  • фильтрация фич по стилевым правилам (filter expressions)

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


Пайплайн стилей и вычисления выражений

Mapbox GL JS использует декларативные стили, которые содержат выражения (expressions), зависящие от zoom, feature properties и state.

Worker threads выполняют:

  • компиляцию style JSON в внутренние структуры
  • предвычисление выражений для разных zoom-уровней
  • фильтрацию слоёв
  • интерполяцию свойств (цвета, ширины линий, opacity)

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


Layout и обработка текста

Отдельно выделяется этап layout, особенно для символических слоёв (symbol layers).

В worker threads выполняются:

  • размещение текстовых меток
  • расчёт collision detection (предотвращение наложения подписей)
  • подготовка glyph-индексов
  • позиционирование и ориентация иконок

Этот этап является одним из самых ресурсоёмких, особенно при большом количестве POI на карте.


WebGL-отрисовка на главном потоке

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

Причины:

  • WebGL context привязан к DOM canvas
  • ограничения браузерной безопасности
  • необходимость синхронного доступа к GPU state

Worker threads лишь подготавливают:

  • vertex buffers
  • index buffers
  • uniform data
  • style-parameter snapshots

Коммуникация между потоками

Обмен данными между главным потоком и workers осуществляется через postMessage.

Типы передаваемых данных:

  • сырые бинарные буферы (ArrayBuffer)
  • сериализованные объекты стилей
  • команды обновления источников данных
  • результаты tile parsing

Для оптимизации используются:

  • Transferable objects (передача владения буфером без копирования)
  • структурированное клонирование (structured clone algorithm)
  • батчинг сообщений

Transferable objects и оптимизация памяти

Ключевая оптимизация worker архитектуры — использование transferables.

При передаче:

  • ArrayBuffer не копируется
  • владение памятью переходит между потоками
  • исключаются лишние аллокации и GC pressure

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


Пул worker threads и масштабирование

Mapbox GL JS использует пул воркеров для параллельной обработки тайлов.

Основные принципы:

  • распределение задач по доступным ядрам CPU
  • очередь задач на загрузку и обработку tiles
  • повторное использование worker instances
  • балансировка нагрузки между потоками

Обычно количество воркеров зависит от:

  • числа логических CPU-ядер
  • настроек окружения
  • ограничений устройства (мобильные vs desktop)

Инкрементальные обновления и diffing стиля

При изменении стиля или источников данных не происходит полной переработки сцены.

Вместо этого worker threads выполняют:

  • diff текущего состояния стиля и нового
  • повторную обработку только затронутых слоёв
  • частичное пересоздание tile buckets
  • инвалидацию устаревших данных

Такой подход позволяет избежать дорогостоящего полного пересчёта сцены.


Обработка источников данных

Worker threads обрабатывают разные типы источников:

VectorSource

  • декодирование MVT
  • генерация геометрии
  • подготовка feature buckets

GeoJSONSource

  • парсинг JSON
  • конвертация в внутренние feature structures
  • индексация по геометрии

RasterSource (частично)

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

Ограничения worker архитектуры

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

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

Особенно заметна стоимость межпоточного взаимодействия при частых обновлениях данных.


Особенности отладки worker threads

Отладка worker-логики требует дополнительных инструментов:

  • source maps для worker bundle
  • DevTools worker inspector
  • логирование через postMessage
  • временная синхронизация исполнения (debug mode)

Ошибки в worker часто проявляются асинхронно, что усложняет трассировку состояния.


Производительность и узкие места

Основные источники нагрузки:

  • декодирование больших vector tiles
  • layout символов на высоких zoom-уровнях
  • частые изменения стиля
  • повторное создание buckets
  • GC pressure из-за больших структур данных

Оптимизация обычно достигается через:

  • уменьшение частоты style updates
  • использование более агрессивного tile caching
  • снижение сложности выражений в стилях
  • ограничение количества символических слоёв

Итоговая модель выполнения

Внутренний pipeline можно описать как последовательность:

  • загрузка tile → worker
  • декодирование → геометрия
  • применение стиля → buckets
  • передача → главный поток
  • WebGL upload → GPU
  • рендер кадра → canvas

Worker threads выступают центральным звеном, превращающим сырые геоданные в готовые GPU-структуры без блокировки интерфейса и без участия DOM-слоя.