Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 46 additions & 22 deletions dev/timeline-layers/examples/horizon-graph-layer/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

import {Deck, OrthographicView, type OrthographicViewState} from '@deck.gl/core';
import {LineLayer, TextLayer} from '@deck.gl/layers';
import {
Deck,
OrthographicView,
type OrthographicViewState,
type ViewStateChangeParameters,
type Widget
} from '@deck.gl/core';
import {LineLayer} from '@deck.gl/layers';
import {FastTextLayer} from '@deck.gl-community/infovis-layers';
import {MultiHorizonGraphLayer} from '@deck.gl-community/timeline-layers';
import {
MarkdownPanel,
Expand All @@ -12,6 +19,7 @@ import {
type SettingsState
} from '@deck.gl-community/panels';
import {BoxPanelWidget, SidebarPanelWidget} from '@deck.gl-community/widgets';
import type {Device} from '@luma.gl/core';

import '@deck.gl/widgets/stylesheet.css';

Expand Down Expand Up @@ -81,9 +89,26 @@ type HorizonExampleConfig = {
infoTitle?: string;
infoMarkdown?: string;
showInfoWidget?: boolean;
onDeckInitialized?: (deck: Deck) => void;
device?: Device;
widgets?: Widget[];
initialViewState?: OrthographicViewState;
onViewStateChange?: <ViewStateT extends OrthographicViewState>(
params: ViewStateChangeParameters<ViewStateT>
) => ViewStateT;
onDeckInitialized?: (deck: Deck<OrthographicView>) => void;
};

type ResolvedHorizonExampleConfig = Required<
Pick<
HorizonExampleConfig,
'layerId' | 'sidebarTitle' | 'infoTitle' | 'infoMarkdown' | 'showInfoWidget'
>
> &
Omit<
HorizonExampleConfig,
'layerId' | 'sidebarTitle' | 'infoTitle' | 'infoMarkdown' | 'showInfoWidget'
>;

const VIEW = new OrthographicView({id: 'ortho'});

const INITIAL_VIEW_STATE: OrthographicViewState = {
Expand Down Expand Up @@ -249,7 +274,10 @@ const SETTINGS_SCHEMA: SettingsSchema = {
]
};

const DEFAULT_EXAMPLE_CONFIG: Required<Omit<HorizonExampleConfig, 'onDeckInitialized'>> = {
const DEFAULT_EXAMPLE_CONFIG: Pick<
ResolvedHorizonExampleConfig,
'layerId' | 'sidebarTitle' | 'infoTitle' | 'infoMarkdown' | 'showInfoWidget'
> = {
layerId: 'horizon-graph-layer',
sidebarTitle: 'Horizon Graph Controls',
infoTitle: 'HorizonGraphLayer',
Expand All @@ -266,7 +294,6 @@ export function mountHorizonGraphLayerExample(
const rootElement = container.ownerDocument.createElement('div');
applyElementStyle(rootElement, ROOT_STYLE);
container.replaceChildren(rootElement);

const state: ExampleState = {
settings: cloneSettings(INITIAL_SETTINGS),
derived: buildDerivedState(INITIAL_SETTINGS),
Expand All @@ -293,7 +320,7 @@ export function mountHorizonGraphLayerExample(
panel: settingsPanel
});

const widgets = [];
const widgets: Widget[] = [...(resolvedConfig.widgets ?? [])];

if (resolvedConfig.showInfoWidget && resolvedConfig.infoTitle && resolvedConfig.infoMarkdown) {
widgets.push(
Expand All @@ -315,11 +342,13 @@ export function mountHorizonGraphLayerExample(
widgets.push(sidebarWidget);

const deck = new Deck({
device: resolvedConfig.device,
parent: rootElement,
width: '100%',
height: '100%',
views: VIEW,
initialViewState: INITIAL_VIEW_STATE,
initialViewState: resolvedConfig.initialViewState ?? INITIAL_VIEW_STATE,
onViewStateChange: resolvedConfig.onViewStateChange,
controller: true,
widgets,
layers: buildLayers(state, resolvedConfig),
Expand All @@ -336,7 +365,7 @@ export function mountHorizonGraphLayerExample(
syncDeck();
}
});
config.onDeckInitialized?.(deck);
resolvedConfig.onDeckInitialized?.(deck);

return () => {
deck.finalize();
Expand Down Expand Up @@ -371,10 +400,7 @@ export function mountMultiHorizonGraphLayerExample(
});
}

function buildLayers(
state: ExampleState,
config: Required<Omit<HorizonExampleConfig, 'onDeckInitialized'>>
) {
function buildLayers(state: ExampleState, config: ResolvedHorizonExampleConfig) {
const {settings, derived, mousePosition} = state;
const {x, y, width} = settings.layout;
const {height} = derived;
Expand All @@ -396,16 +422,15 @@ function buildLayers(
width,
height
}),
new TextLayer<LabelDatum>({
new FastTextLayer<LabelDatum>({
id: 'series-labels',
data: derived.textLabels,
getText: datum => datum.text,
getPosition: datum => datum.position,
getSize: datum => datum.size,
size: 12,
getColor: datum => datum.color,
getAngle: datum => datum.angle,
getTextAnchor: datum => datum.textAnchor,
getAlignmentBaseline: datum => datum.alignmentBaseline,
textAnchor: 'end',
alignmentBaseline: 'center',
fontFamily: 'Arial, sans-serif',
fontWeight: 'normal'
}),
Expand All @@ -418,16 +443,15 @@ function buildLayers(
getWidth: 1,
widthUnits: 'pixels'
}),
new TextLayer<LabelDatum>({
new FastTextLayer<LabelDatum>({
id: 'intersection-values',
data: buildIntersectionData(state),
getText: datum => datum.text,
getPosition: datum => datum.position,
getSize: datum => datum.size,
size: 12,
getColor: datum => datum.color,
getAngle: datum => datum.angle,
getTextAnchor: datum => datum.textAnchor,
getAlignmentBaseline: datum => datum.alignmentBaseline,
textAnchor: 'start',
alignmentBaseline: 'center',
fontFamily: 'Arial, sans-serif',
fontWeight: 'normal'
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"start-local": "vite --config ../../vite.config.local.mjs"
},
"dependencies": {
"@deck.gl-community/infovis-layers": "workspace:*",
"@deck.gl-community/panels": "workspace:*",
"@deck.gl-community/timeline-layers": "workspace:*",
"@deck.gl-community/widgets": "workspace:*",
Expand Down
9 changes: 6 additions & 3 deletions docs/modules/geo-layers/api-reference/delaunay-cover-layer.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import LayerLiveExample from '@site/src/components/docs/layer-live-example';
# DelaunayCoverLayer

:::caution Work in progress
The station-surface appearance and API may change. Its `SolidPolygonLayer` sublayer is not yet
fully portable to WebGPU.
The station-surface appearance and API may change. Its native triangle primitive works on WebGL2
and WebGPU; image-derived mountain terrain remains dependent on upstream `TerrainLayer` support.
:::

`DelaunayCoverLayer` renders one elevation-colored polygon for each triangle in a shared
Expand Down Expand Up @@ -43,7 +43,10 @@ const stationSurface = new DelaunayCoverLayer({

## Sub-layers

- `terrain`: a `SolidPolygonLayer` containing the station-index Delaunay triangles.
- `terrain`: native GLSL/WGSL triangle geometry containing the station-index Delaunay triangles.

The triangulated station surface, elevation scaling, and height-based colors render on both
WebGL2 and WebGPU. This surface is distinct from the smoothed image-based mountain terrain.

Use the example's **Delaunay station surface** control to inspect the triangulation independently
of the smoothed mountains. See the [wind showcase guide](../developer-guide/wind-showcase.md).
6 changes: 6 additions & 0 deletions docs/modules/geo-layers/api-reference/elevation-layer.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ const terrain = new ElevationLayer({
For smoother relief, filter the grayscale image once before passing it as `elevationData`. Do not
recreate or decode the terrain mesh during particle animation.

Height-map rendering currently requires WebGL2 because upstream `TerrainLayer` uses a WebGL-only
mesh renderer. On WebGPU, `ElevationLayer` safely omits its terrain sub-layer; use
[`DelaunayCoverLayer`](/docs/modules/geo-layers/api-reference/delaunay-cover-layer) for a
WebGPU-compatible station-triangulated terrain surface. See the
[WebGPU support matrix](/docs/webgpu) for the current status.

## Properties

- `elevationData` (`string`, required): URL or data URL of a red-channel grayscale height map.
Expand Down
17 changes: 10 additions & 7 deletions docs/modules/geo-layers/api-reference/particle-layer.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import LayerLiveExample from '@site/src/components/docs/layer-live-example';

:::caution Work in progress
The wind-layer API, GPU simulation, particle appearance, and tuning controls are experimental and
may change. WebGL2 and WebGPU particle simulation are browser-tested independently; a complete
WebGPU wind scene still depends on upstream terrain, polygon, and path support.
may change. WebGL2 and WebGPU particle simulation, wind arrows, and station surfaces are
browser-tested independently; image-derived mountain terrain still depends on upstream support.
:::

`ParticleLayer` animates GPU-resident particles through a station-interpolated geographic wind
field. WebGL2 uses transform feedback and single-vertex point rendering; WebGPU uses a compute
shader and portable deck.gl sublayers. Neither simulation reads particle positions back to the CPU.
shader and native GPU-buffer-backed point primitives. Neither simulation reads particle positions
back to the CPU.

<LayerLiveExample highlight="particle-layer" size="tall" />

Expand Down Expand Up @@ -117,11 +118,13 @@ Radius of moving particle heads in screen pixels.

- WebGL2: cached `rgba32float` weather textures, transform-feedback ping-pong buffers, and one
native point vertex per particle.
- WebGPU: cached weather textures, a WGSL compute pipeline, and GPU-buffer-backed sublayers.
- WebGPU: cached weather textures, a WGSL compute pipeline, and native GPU-buffer-backed point
primitives.
- Coverage: invalid samples are respawned within the wind field; overlong segments are clipped.
- Cleanup: deck.gl finalization releases the weather textures, simulation buffers, and pipeline.
- Scope: GPU particle compatibility does not imply that `TerrainLayer`, `SolidPolygonLayer`, or
`PathLayer` is already WebGPU-compatible.
- Cleanup: deck.gl finalization releases the weather textures, simulation buffers, and pipeline
after submitted GPU work has completed.
- Scope: native wind arrows and station-triangulated surfaces support WebGPU, but image-derived
mountain terrain still depends on upstream `TerrainLayer` compatibility.

See the [wind showcase guide](../developer-guide/wind-showcase.md), the
[Wind Map example](/examples/geo-layers/wind), and the
Expand Down
10 changes: 5 additions & 5 deletions docs/modules/geo-layers/api-reference/wind-layer.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import LayerLiveExample from '@site/src/components/docs/layer-live-example';
# WindLayer

:::caution Work in progress
The wind arrow API and styling are experimental. Filled arrows depend on upstream
`SolidPolygonLayer` and `PathLayer`, so complete WebGPU scene support is still in progress.
The wind arrow API and styling are experimental. Native triangle glyphs and portable line
segments render on WebGL2 and WebGPU; image-based mountain terrain remains in progress.
:::

`WindLayer` renders a Delaunay-interpolated station forecast as directional, speed-colored arrow
Expand Down Expand Up @@ -61,9 +61,9 @@ than the animation frame rate.

## Sub-layers

- `glyphs`: filled `SolidPolygonLayer` arrows.
- `shafts`: a `PathLayer` for arrow shafts.
- `arrowheads`: a `PathLayer` for directional arrowheads.
- `glyphs`: native GLSL/WGSL triangle geometry for filled directional arrows.
- `shafts`: a dual-backend `LineLayer` for arrow shafts.
- `arrowheads`: a dual-backend `LineLayer` for directional arrowhead segments.

## Historical source

Expand Down
19 changes: 10 additions & 9 deletions docs/modules/geo-layers/developer-guide/wind-showcase.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import LayerLiveExample from '@site/src/components/docs/layer-live-example';

:::caution Work in progress
The reusable wind layers and the historical showcase are experimental. GPU particle advection has
been independently verified on WebGL2 and WebGPU. The complete mountain-and-arrow scene currently
uses upstream terrain, polygon, and path layers, so its end-to-end WebGPU support remains a work
in progress.
been independently verified on WebGL2 and WebGPU, along with filled wind arrows and station
surfaces. Image-derived mountain terrain still depends on upstream `TerrainLayer` support, so the
WebGPU showcase substitutes the station-triangulated surface.
:::

The Wind Map restores
Expand Down Expand Up @@ -178,8 +178,9 @@ when debugging interpolation coverage; do not replace the mountain terrain with

The example starts with `100_000` particles and provides a debounced slider from `1_000` to
`1_000_000`. WebGL2 advances GPU-resident particle buffers using transform feedback and renders
single-vertex point primitives. WebGPU advances them with a WGSL compute shader. Neither production
animation path reads particle positions back to the CPU.
single-vertex point primitives. WebGPU advances them with a WGSL compute shader and renders native
GPU-buffer-backed point primitives. Neither production animation path reads particle positions
back to the CPU.

Weather textures are cached, static terrain is retained, arrow resampling is throttled, and
high-density rendering favors particle heads over additional line geometry. Changing the particle
Expand All @@ -192,10 +193,10 @@ buffers on every slider event.
| --- | :---: | :---: | --- |
| `ParticleLayer` | Supported | Supported | Independently browser-tested GPU advection and rendering. |
| Wind data and `DelaunayInterpolation` | Supported | Supported | Backend-independent indexing and explicit sampling. |
| `WindLayer` | Supported | In progress | Depends on upstream polygon and path rendering. |
| `ElevationLayer` | Supported | In progress | Depends on upstream `TerrainLayer`. |
| `DelaunayCoverLayer` | Supported | Blocked | Depends on upstream polygon rendering. |
| Complete original showcase | Supported | In progress | GPU particles are ready; remaining scene layers are not. |
| `WindLayer` | Supported | Supported | Native GLSL/WGSL filled arrows and portable line shafts. |
| `ElevationLayer` | Supported | Blocked | Image-derived mountain terrain depends on upstream `TerrainLayer`. |
| `DelaunayCoverLayer` | Supported | Supported | Native GLSL/WGSL station-triangulated surface. |
| Complete original showcase | Supported | In progress | WebGPU uses station terrain while image-derived mountains remain blocked. |

See the full [WebGPU compatibility matrix](/docs/webgpu).

Expand Down
6 changes: 3 additions & 3 deletions docs/upgrade-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ Please refer the documentation of each module for detailed upgrade guides.
WebGL `Transform` and `Texture2D` dependencies.
- `trailLength` controls the device-free CPU fallback; GPU rendering uses ping-pong buffers,
lifetime fading, and high-density point rendering.
- WebGPU support for `ParticleLayer` does not imply support for the complete showcase. Terrain,
filled wind arrows, and station polygons remain dependent on upstream `TerrainLayer`,
`PathLayer`, and `SolidPolygonLayer` compatibility. See the
- WebGPU supports GPU-resident particles, filled wind arrows, and station-triangulated surfaces
through native shaders. Image-derived mountain terrain remains dependent on upstream
`TerrainLayer` compatibility. See the
[WebGPU support matrix](./webgpu.md).

### `@deck.gl-community/panels`
Expand Down
Loading
Loading