Буферизация и геометрические операции

Буферизация в deck.gl представляет собой фундаментальный слой работы с данными, на котором строится высокая производительность визуализации. Вся геометрия, отображаемая слоями, в конечном итоге преобразуется в GPU-буферы — структуры WebGL, содержащие упакованные массивы вершин, индексов и атрибутов. Эти буферы определяют, как именно данные будут интерпретированы видеокартой при рендеринге примитивов.

В основе буферизации лежит преобразование JavaScript-объектов в TypedArray-структуры. Deck.gl минимизирует количество операций на CPU, перекладывая вычисления на этап подготовки атрибутов.

Каждый слой (Layer) определяет собственную схему атрибутов:

  • позиции вершин (positions)
  • цвета (colors)
  • индексы (indices)
  • нормали (normals)
  • дополнительные пользовательские атрибуты

Буфер создаётся один раз при инициализации слоя и переиспользуется при обновлениях данных, если не изменяются зависимости.

Ключевая идея — иммутабельность буфера при отсутствии изменений данных. Это позволяет избегать дорогостоящих пересозданий GPU-ресурсов.

AttributeManager и система атрибутов

Центральным компонентом управления буферизацией является AttributeManager. Он отвечает за:

  • создание атрибутов
  • обновление частичных буферов
  • контроль зависимостей (updateTriggers)
  • инстансинг

Атрибут описывается как генератор данных из исходного массива объектов:

  • accessor-функция извлекает данные
  • преобразователь формирует числовые массивы
  • результат записывается в Float32Array или Uint16Array

Буфер обновляется не целиком, а по необходимости. Если изменился только цвет объектов, геометрические буферы не пересчитываются.

Структура бинарных данных

Deck.gl использует бинарный формат представления данных как внутренний стандарт. Вместо массива объектов:

[
  {x: 0, y: 0, z: 0, color: [255, 0, 0]},
  {x: 1, y: 1, z: 0, color: [0, 255, 0]}
]

данные преобразуются в:

  • positions: Float32Array [x0, y0, z0, x1, y1, z1]
  • colors: Uint8Array [r0, g0, b0, r1, g1, b1]

Такой подход снижает накладные расходы на обращение к памяти и улучшает кэш-локальность.

Геометрические операции перед буферизацией

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

Нормализация координат

Многие слои требуют приведения координат к единому пространству:

  • географические координаты (longitude/latitude)
  • мировые координаты Web Mercator
  • локальные координаты объектов

Проекция выполняется на этапе attribute calculation, что позволяет избежать повторных вычислений в шейдерах.

Триангуляция полигонов

Для полигональных слоёв (PolygonLayer, GeoJsonLayer) используется разбиение на треугольники. Основной алгоритм — ear clipping (ушная триангуляция).

Процесс включает:

  • проверку ориентации контура
  • удаление самопересечений
  • разбиение на простые треугольники
  • формирование индексов для element array buffer

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

Разворачивание линий (Path extrusion)

Для линейных слоёв (PathLayer, LineLayer) геометрия преобразуется в набор сегментов. Каждая линия разбивается на:

  • последовательность точек
  • соединительные треугольники для толщины
  • вычисленные векторы нормалей

Это позволяет отрисовывать линии с переменной толщиной через GPU без CPU-растеризации.

Инстансинг и буферизация повторяющихся объектов

Instanced rendering является ключевой оптимизацией. Один набор геометрии (например, сфера или куб) используется многократно.

Буферы делятся на:

  • geometry buffer (общий)
  • instance buffer (уникальные данные каждого объекта)

Instance attributes включают:

  • позицию
  • масштаб
  • поворот
  • цвет
  • параметры анимации

GPU получает один draw call для тысяч объектов, что существенно снижает overhead WebGL.

Обновление буферов и стратегия diffing

Deck.gl использует дифф-алгоритм обновления атрибутов. При изменении props слоя:

  1. Проверяется, изменился ли data array
  2. Сравниваются updateTriggers
  3. Определяется список затронутых атрибутов
  4. Пересчитываются только необходимые буферы

Пример логики:

  • изменение координат → пересчёт positions + indices
  • изменение цвета → пересчёт только colors
  • изменение геометрии → полный rebuild буферов

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

Геометрические преобразования в шейдерах и на CPU

Часть операций выполняется на GPU, часть — на CPU.

CPU-стадия:

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

GPU-стадия:

  • трансформация вершин
  • проекция координат
  • интерполяция цветов
  • вычисление толщины линий

Разделение обязанностей снижает нагрузку на основной поток JavaScript.

TypedArray как основа буферизации

Deck.gl опирается на строго типизированные массивы:

  • Float32Array — координаты и непрерывные значения
  • Uint8Array — цвета и флаги
  • Uint16Array / Uint32Array — индексы

Особенность подхода:

  • отсутствие динамического расширения
  • фиксированная структура памяти
  • прямое отображение в WebGL bufferData

Индексные буферы и повторное использование вершин

Index buffer (ELEMENT_ARRAY_BUFFER) позволяет избегать дублирования геометрии. Вместо повторного описания каждой вершины:

  • вершины хранятся один раз
  • индексы описывают порядок соединения

Это критично для:

  • полигонов с отверстиями
  • сложных сеток
  • 3D-моделей

Экономия памяти растёт пропорционально сложности геометрии.

Геометрические трансформации и матрицы

Каждый слой использует матрицы преобразований:

  • model matrix (локальная трансформация)
  • view matrix (камера)
  • projection matrix (перспектива)

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

Часто используется матрица:

  • 4x4 float matrix
  • хранится в Float32Array длиной 16

Генерация геометрии для абстрактных слоёв

Некоторые слои не используют входную геометрию напрямую:

  • HexagonLayer
  • GridLayer
  • HeatmapLayer

Они генерируют буферы процедурно:

  • разбиение пространства на ячейки
  • вычисление высоты столбцов
  • интерполяция плотности

Буферизация здесь происходит после агрегации данных.

Оптимизация памяти и перераспределение буферов

WebGL-буферы являются ограниченным ресурсом, поэтому применяется:

  • переиспользование буферов при одинаковом размере
  • частичное обновление subData
  • lazy allocation (создание по требованию)

При изменении размера данных:

  • малое увеличение → resize buffer
  • значительное → пересоздание

Связь буферизации с picking-системой

Picking требует отдельного набора буферов:

  • picking colors buffer (уникальный ID объекта)
  • mapping buffer (ID → индекс данных)

Каждый пиксель сцены кодирует идентификатор объекта через цветовую упаковку. Это позволяет выполнять выбор объектов без CPU-итерации по массиву данных.

Геометрические операции в GeoJSON пайплайне

GeoJSON-данные проходят несколько стадий:

  • нормализация структуры Feature / FeatureCollection
  • извлечение geometry
  • разбиение MultiPolygon / MultiLineString
  • триангуляция
  • упаковка в бинарные атрибуты

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

Декодирование и декомпрессия данных

При работе с внешними источниками данных часто используются:

  • quantized coordinates
  • delta encoding
  • compressed attributes

Перед буферизацией данные восстанавливаются в Float32Array, после чего проходят стандартный pipeline атрибутов.

Взаимодействие буферизации и рендер-пайплайна

Финальный этап включает:

  • binding buffer в WebGL context
  • установка attribute pointers
  • вызов drawArrays / drawElements
  • применение instancing (if enabled)

Буферы становятся источником данных для vertex shader, который выполняет финальную геометрическую трансформацию перед растеризацией.