Skip to main content

GPU DGGS

Discrete global grid indexes are compact, stable identifiers. Rendering and spatial analysis need coordinates. The experimental @luma.gl/gpgpu/gpu-dggs module connects those two representations without pulling a large cell column back to JavaScript.

InputShared GPU primitiveOutput
Packed uint32x2 cell IDsvalidation → topology decode → spherical projectionfloat32x2 longitude/latitude or float32x3 unit vectors

The same command-graph operation powers the family-specific gpu-h3 and gpu-a5 modules. Use those typed adapters when the grid is known in advance. Use GPUDGGSCellProjection when a planner or application selects the grid at runtime.

Live benchmark

This benchmark runs locally on the reader's WebGPU adapter. It validates every result before and after timing and reports portable completion-fence latency separately from compute-only timestamp queries when the adapter exposes them.

Live DGGS cell-center projection

Decode a repeated, globally distributed set of valid H3 or A5 indexes with the public command-graph primitive. Correctness checks, upload, compilation, and readback stay outside measured submissions.

Ready to decode 16,384 H3 indexes into normalized vectors.

Mobile quality · smallest workload

Benchmark input is already resident when timing begins. Upload, shader compilation, and validation readback are deliberately excluded. That makes the result representative of a projection pass inside a larger GPU pipeline, not a claim about end-to-end file ingestion.

One contract, three entry points

Entry pointPrimary classBest fit
@luma.gl/gpgpu/gpu-dggsGPUDGGSCellProjectionRuntime-selected H3/A5 workflows and reusable planners
@luma.gl/gpgpu/gpu-h3GPUH3CellProjectionH3-specific pipelines and the clearest static types
@luma.gl/gpgpu/gpu-a5GPUA5CellProjectionA5-specific pipelines and the clearest static types

All three classes implement GPUCommandGraphContributor. They add one bounded compute pass; they do not submit commands, allocate hidden result buffers, or read output back.

import {GPUDGGSCellProjection} from '@luma.gl/gpgpu/gpu-dggs';

new GPUDGGSCellProjection({
family: selectedGrid, // 'h3' | 'a5'
cells,
output: centers,
validity,
projection: 'unit-vector'
}).addToGraph(graph);

GPU data contract

cells is a packed GraphDataView<'uint32x2'>. The default wordOrder: 'little-endian' expects the low 32-bit word first and matches a native BigUint64Array on the browser platforms WebGPU targets. Set wordOrder: 'high-low' for canonical high-word-first storage.

PropertyFormatMeaning
cellsuint32x2One split 64-bit cell index per row
output, projection: 'lnglat'float32x2Longitude then latitude, in degrees
output, projection: 'unit-vector'float32x3Normalized Earth-centered Cartesian vector
validityoptional uint321 for a valid cell, 0 for an invalid index

Invalid rows write zero coordinates. All views must have the same row count, belong to the target graph, use packed layouts, and occupy separate buffers. Three-dimensional bounded dispatch removes the device's one-dimensional workgroup-count limit for very large arrays.

tip

Choose unit vectors for GPU-resident geometry.

Unit vectors compose directly with globe rendering, dot-product distance filters, hemisphere tests, and normal generation. They also avoid the inverse trigonometry required to materialize longitude and latitude. Choose geographic output when downstream code genuinely needs degrees.

Reusable pipeline patterns

The primitive is source-aligned: output row i always describes cell row i. That makes it easy to compose with existing graph operations.

GoalSuggested graph
Render cell centersDGGS projection → model vertex input
Cull invalid dataprojection with validity → GPUCompaction
Aggregate spatial metricsunit vectors → expression/filter → reduction
Publish coordinates to the CPUprojection → explicit readback ring
Switch grids at runtimeplanner chooses familyGPUDGGSCellProjection

The fixed H3 icosahedron basis is currently a 960-byte module-scope WGSL constant. A storage-buffer table would add a binding and upload without reducing the working set. The shared family-neutral class deliberately leaves that implementation choice behind the API boundary, so a profiled adapter-specific table path could be added later without changing application graphs.

Scope

The current primitive decodes cell indexes to centers. It does not yet encode coordinates to cells, generate polygon boundaries, or enumerate neighbors. Those operations have different output and precision shapes:

  • coordinate-to-cell encoding needs an exact-integer or hybrid verification strategy near cell boundaries;
  • variable-length boundaries fit a count → prefix scan → write graph;
  • neighborhood expansion needs explicit capacity and overflow semantics.

The shared split-64-bit representation, validity handling, bounded dispatch, and graph-contributor contract are designed to be reused by those future operations.

Benchmark API

The live page uses an isolated optional entry point, keeping measurement code out of application bundles:

import {runGPUDGGSCellProjectionBenchmark} from '@luma.gl/gpgpu/gpu-dggs/benchmarks';

const report = await runGPUDGGSCellProjectionBenchmark(device, {
family: 'h3',
cells: packedCellWords,
referenceValues: precomputedUnitVectorCenters,
projection: 'unit-vector'
});

The runner accepts low-word/high-word Uint32Array input plus row-matched CPU or precomputed reference centers. It rejects invalid cells, compares every output component with that oracle, range-checks geographic centers, verifies vector normalization, and returns distributions rather than a single best-case sample. Use referenceTolerance to override the default 0.03° geographic or 0.002 unit-vector component tolerance.