Расширения (Extensions)

Система расширений в Deck.gl построена как механизм композиции функциональности поверх базовых слоёв без необходимости наследования или модификации исходного класса слоя. Расширение представляет собой модуль, который подключается к слою и динамически добавляет:

  • новые свойства (props),
  • поведение на уровне жизненного цикла слоя,
  • шейдерные модули WebGL,
  • логику подготовки данных,
  • дополнительные uniform-переменные,
  • обработчики взаимодействия.

Ключевая идея заключается в разделении ответственности: слой отвечает за визуализацию геометрии, расширение — за добавление ортогональной функциональности.


Механизм подключения расширений

Расширения подключаются через массив extensions в конфигурации слоя:

new ScatterplotLayer({
  id: 'points',
  data,
  getPosition: d => d.position,
  getRadius: d => d.radius,
  extensions: [
    new DataFilterExtension({filterSize: 1}),
    new BrushingExtension()
  ]
});

Каждое расширение реализует интерфейс, который Deck.gl использует для:

  • модификации state слоя,
  • внедрения шейдерных модулей,
  • расширения props,
  • участия в процессе рендеринга.

Внутренняя модель расширений

Расширение в Deck.gl — это объект, который может реализовать набор “хуков”. Эти хуки вызываются в определённые моменты жизненного цикла слоя:

  • initializeState
  • updateState
  • draw
  • finalizeState
  • getShaders
  • getUniforms
  • getAttributes

Каждый слой при инициализации агрегирует поведение всех подключённых расширений, формируя единый pipeline рендеринга.


Расширения как модификаторы шейдеров

Одной из ключевых возможностей является расширение GLSL-шейдеров. Расширения могут добавлять:

  • varyings,
  • uniform-переменные,
  • функции GLSL,
  • фрагменты логики в vertex/fragment shader.

Пример принципа расширения шейдера

Расширение может внедрять код в шейдерный pipeline:

getShaders() {
  return {
    modules: [dataFilter, brushing]
  };
}

Каждый модуль содержит GLSL-фрагменты:

  • inject: { 'vs:#decl': '...', 'fs:#main-end': '...' }

Таким образом формируется композиция шейдерного кода без ручного редактирования базового слоя.


DataFilterExtension

Назначение

DataFilterExtension реализует фильтрацию данных на GPU уровне. Основная задача — исключать элементы до стадии рендеринга, минимизируя CPU-нагрузку.

Принцип работы

Каждый элемент получает дополнительный атрибут фильтра, который сравнивается с заданным диапазоном:

  • минимальное значение фильтра
  • максимальное значение фильтра

Фильтрация выполняется в шейдере через discard-логику.

Использование

new ScatterplotLayer({
  data,
  getPosition: d => d.position,
  extensions: [
    new DataFilterExtension({filterSize: 1})
  ],
  getFilterValue: d => d.timestamp,
  filterRange: [start, end]
});

Особенности реализации

  • добавляет attribute buffer для фильтруемого значения,
  • вводит uniform filterRange,
  • модифицирует fragment shader,
  • поддерживает анимацию фильтрации через обновление props.

BrushingExtension

Назначение

BrushingExtension используется для интерактивного выделения объектов в зависимости от расстояния до курсора или центра взаимодействия.

Механика

Каждый объект получает дополнительный атрибут, участвующий в вычислении “distance field”. В шейдере вычисляется расстояние до активной точки взаимодействия.

Основные параметры

  • getBrushingTarget
  • brushingRadius
  • brushingEnabled

Поведение

Объекты, находящиеся вне радиуса, могут:

  • становиться прозрачными,
  • уменьшаться,
  • полностью скрываться.

PathStyleExtension

Назначение

Расширение, отвечающее за расширенные стили линий и путей, выходящие за пределы базового PathLayer.

Возможности

  • переменная ширина линии по сегментам,
  • динамические цвета вдоль пути,
  • поддержка текстурирования,
  • управление join/cap стилями на GPU.

Архитектурная особенность

Расширение переопределяет атрибуты:

  • instanceWidths
  • instanceColors
  • instanceTimestamps

и модифицирует геометрию линий в шейдере.


MaskExtension

Назначение

MaskExtension реализует геометрическую маскировку слоёв с использованием масочных слоёв (например, полигонов).

Принцип работы

Каждый пиксель проверяется на принадлежность маске через stencil buffer или геометрическое тестирование.

Особенности

  • поддержка нескольких масок,
  • комбинирование через AND/OR операции,
  • работа с GeoJsonLayer как источником маски.

TerrainExtension

Назначение

Расширение для работы с рельефом и подстройки визуализации под высотные данные.

Основные функции

  • коррекция координат Z-оси,
  • привязка объектов к цифровой модели рельефа,
  • синхронизация с TerrainLayer.

Поведение

Каждый объект получает дополнительную трансформацию позиции:

  • sampling heightmap,
  • добавление offset,
  • пересчёт нормалей.

Механизм композиции расширений

Deck.gl не объединяет расширения последовательно, а строит единый агрегированный слой:

  1. Слой запрашивает список расширений.

  2. Каждое расширение возвращает:

    • модификаторы шейдеров,
    • дополнительные props,
    • атрибуты.
  3. Deck.gl выполняет merge всех модулей.

  4. Формируется единый shader program.

Важный момент: порядок расширений может влиять на результат, особенно при конфликте shader injections.


Расширения и state management слоя

Расширения могут вмешиваться в состояние слоя через:

  • state.attributeManager,
  • state.model,
  • state.uniforms.

Они могут:

  • добавлять новые buffers,
  • инициировать пересчёт атрибутов,
  • обновлять GPU state при изменении props.

Атрибуты и AttributeManager

Расширения часто регистрируют дополнительные атрибуты через AttributeManager:

initializeState() {
  const attributeManager = this.getAttributeManager();

  attributeManager.addInstanced({
    filterValues: {
      size: 1,
      accessor: 'getFilterValue'
    }
  });
}

Это позволяет интегрировать новые данные в существующий rendering pipeline без модификации базового слоя.


Шейдерные модули расширений

Каждое расширение может содержать shader module:

export default {
  name: 'customExtension',
  vs: `
    attribute float customValue;
  `,
  fs: `
    float intensity = customValue * 0.5;
  `,
  inject: {
    'vs:#main-end': `
      vCustom = customValue;
    `
  }
};

Модули объединяются через систему препроцессинга shader assembly.


Взаимодействие нескольких расширений

При одновременном использовании нескольких расширений возможны конфликты:

  • пересечение uniform names,
  • дублирование attributes,
  • конкуренция за shader injection points.

Deck.gl решает это через:

  • namespace для shader modules,
  • автоматическое переименование атрибутов,
  • композицию inject-патчей по ключам.

Производительность расширений

Расширения влияют на performance через:

  • увеличение количества attribute buffers,
  • усложнение shader logic,
  • дополнительные проходы GPU,
  • рост memory footprint.

Критические оптимизации:

  • минимизация filter attributes,
  • использование bitmask вместо float при фильтрации,
  • объединение нескольких расширений в один shader module,
  • избегание частых recompile shaders.

Расширения и интерактивность

Некоторые расширения напрямую связаны с user interaction pipeline:

  • обработка hover событий,
  • вычисление picking distance,
  • динамическая модификация opacity,
  • реакция на camera movement.

Например, BrushingExtension зависит от координат курсора и пересчитывает uniform при каждом frame update.


Расширяемость системы расширений

Deck.gl позволяет создавать собственные расширения, реализуя интерфейс:

  • initializeState
  • updateState
  • draw
  • getShaders
  • finalizeState

Это превращает extensions в полноценный слой абстракции, способный:

  • реализовывать новые визуальные модели,
  • добавлять GPU-алгоритмы,
  • интегрировать внешние вычисления в рендеринг pipeline.

Типовые паттерны использования

Фильтрация + интерактивность

Комбинация DataFilterExtension и BrushingExtension позволяет реализовать:

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

Маскирование + стилизация

MaskExtension совместно с PathStyleExtension используется для:

  • визуального ограничения географических зон,
  • стилизации границ внутри масок.

Рельеф + 3D объекты

TerrainExtension применяется для:

  • размещения объектов на поверхности,
  • корректной визуализации сцен с высотами.