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
4 changes: 2 additions & 2 deletions docs/developer-guide/webgpu.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ The table below covers the public layer exports from the layer packages. It is d
| Module | Layer | WebGL | WebGPU |
| --- | --- | --- | --- |
| `@deck.gl/layers` | `ArcLayer` | ✅ | ✅ |
| `@deck.gl/layers` | `BitmapLayer` | ✅ | |
| `@deck.gl/layers` | `BitmapLayer` | ✅ | 🚧 |
| `@deck.gl/layers` | `IconLayer` | ✅ | ✅ |
| `@deck.gl/layers` | `LineLayer` | ✅ | ✅ |
| `@deck.gl/layers` | `PointCloudLayer` | ✅ | ✅ |
Expand All @@ -59,7 +59,7 @@ The table below covers the public layer exports from the layer packages. It is d
| `@deck.gl/geo-layers` | `GreatCircleLayer` | ✅ | ❌ |
| `@deck.gl/geo-layers` | `S2Layer` | ✅ | ❌ |
| `@deck.gl/geo-layers` | `QuadkeyLayer` | ✅ | ❌ |
| `@deck.gl/geo-layers` | `TileLayer` | ✅ | |
| `@deck.gl/geo-layers` | `TileLayer` | ✅ | |
| `@deck.gl/geo-layers` | `TripsLayer` | ✅ | ❌ |
| `@deck.gl/geo-layers` | `H3ClusterLayer` | ✅ | ❌ |
| `@deck.gl/geo-layers` | `H3HexagonLayer` | ✅ | ❌ |
Expand Down
6 changes: 5 additions & 1 deletion examples/website/map-tile/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import ZoomRangeWidget from './zoom-range-widget';

import type {Position, MapViewState} from '@deck.gl/core';
import type {TileLayerPickingInfo} from '@deck.gl/geo-layers';
import type {Device} from '@luma.gl/core';

const INITIAL_VIEW_STATE: MapViewState = {
latitude: 47.65,
Expand Down Expand Up @@ -58,7 +59,8 @@ export default function App({
visibleMinZoom,
visibleMaxZoom = 7,
zoomOffset = 0,
useExtent = false
useExtent = false,
device
}: {
showBorder?: boolean;
onTilesLoad?: () => void;
Expand All @@ -69,6 +71,7 @@ export default function App({
visibleMaxZoom?: number;
zoomOffset?: number;
useExtent?: boolean;
device?: Device;
}) {
const [zoom, setZoom] = useState(INITIAL_VIEW_STATE.zoom);
const onViewStateChange = useCallback(
Expand Down Expand Up @@ -130,6 +133,7 @@ export default function App({

return (
<DeckGL
device={device}
layers={[tileLayer]}
views={new MapView({repeat: true})}
initialViewState={INITIAL_VIEW_STATE}
Expand Down
12 changes: 10 additions & 2 deletions examples/website/wms/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {_WMSLayer as WMSLayer} from '@deck.gl/geo-layers';
import type {MapViewState} from '@deck.gl/core';
import type {BitmapLayerPickingInfo} from '@deck.gl/layers';
import type {ImageSourceMetadata} from '@loaders.gl/loader-utils';
import type {Device} from '@luma.gl/core';

const INITIAL_VIEW_STATE: MapViewState = {
longitude: -122.4,
Expand Down Expand Up @@ -42,13 +43,15 @@ export default function App({
layers = SAMPLE_SERVICE.layers,
initialViewState = INITIAL_VIEW_STATE,
onMetadataLoad = console.log, // eslint-disable-line
onMetadataLoadError = console.error // eslint-disable-line
onMetadataLoadError = console.error, // eslint-disable-line
device
}: {
serviceUrl?: string;
layers?: string[];
initialViewState?: MapViewState;
onMetadataLoad?: (metadata: ImageSourceMetadata) => void;
onMetadataLoadError?: (error: Error) => void;
device?: Device;
}) {
const [selection, setSelection] = useState<{
x: number;
Expand Down Expand Up @@ -79,7 +82,12 @@ export default function App({

return (
<>
<DeckGL layers={[layer]} initialViewState={initialViewState} controller={CONTROLLER} />
<DeckGL
device={device}
layers={[layer]}
initialViewState={initialViewState}
controller={CONTROLLER}
/>
{selection && (
<div
className="selected-feature-info"
Expand Down
21 changes: 18 additions & 3 deletions modules/layers/src/bitmap-layer/bitmap-layer-uniforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,21 @@
import type {Texture} from '@luma.gl/core';
import type {ShaderModule} from '@luma.gl/shadertools';

const uniformBlock = `\
const uniformBlockWGSL = /* wgsl */ `
struct BitmapUniforms {
bounds: vec4<f32>,
coordinateConversion: f32,
desaturate: f32,
tintColor: vec3<f32>,
transparentColor: vec4<f32>,
};

@group(0) @binding(auto) var<uniform> bitmap: BitmapUniforms;
@group(0) @binding(auto) var bitmapTexture: texture_2d<f32>;
@group(0) @binding(auto) var bitmapTextureSampler: sampler;
`;

const uniformBlockGLSL = `\
layout(std140) uniform bitmapUniforms {
vec4 bounds;
float coordinateConversion;
Expand All @@ -26,8 +40,9 @@ export type BitmapProps = {

export const bitmapUniforms = {
name: 'bitmap',
vs: uniformBlock,
fs: uniformBlock,
source: uniformBlockWGSL,
vs: uniformBlockGLSL,
fs: uniformBlockGLSL,
uniformTypes: {
bounds: 'vec4<f32>',
coordinateConversion: 'f32',
Expand Down
23 changes: 21 additions & 2 deletions modules/layers/src/bitmap-layer/bitmap-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import {
Layer,
color as colorModule,
project32,
picking,
CoordinateSystem,
Expand All @@ -23,6 +24,7 @@ import {lngLatToWorld} from '@math.gl/web-mercator';
import createMesh from './create-mesh';

import {bitmapUniforms, BitmapProps} from './bitmap-layer-uniforms';
import source from './bitmap-layer.wgsl';
import vs from './bitmap-layer-vertex';
import fs from './bitmap-layer-fragment';

Expand Down Expand Up @@ -129,7 +131,16 @@ export default class BitmapLayer<ExtraPropsT extends {} = {}> extends Layer<
};

getShaders() {
return super.getShaders({vs, fs, modules: [project32, picking, bitmapUniforms]});
const isWebGPU = this.context.device.type === 'webgpu';

return super.getShaders({
...(isWebGPU && {source}),
vs,
fs,
modules: isWebGPU
? [colorModule, project32, picking, bitmapUniforms]
: [project32, picking, bitmapUniforms]
});
}

initializeState() {
Expand Down Expand Up @@ -256,10 +267,18 @@ export default class BitmapLayer<ExtraPropsT extends {} = {}> extends Layer<
| |
0,1 --- 1,1
*/
const bufferLayout =
this.context.device.type === 'webgpu'
? this.getAttributeManager()!
.getBufferLayouts({isInstanced: false})
// WebGPU index buffers are bound separately from vertex buffer layouts.
.filter(layout => layout.name !== 'indices')
: this.getAttributeManager()!.getBufferLayouts();

return new Model(this.context.device, {
...this.getShaders(),
id: this.props.id,
bufferLayout: this.getAttributeManager()!.getBufferLayouts(),
bufferLayout,
topology: 'triangle-list',
isInstanced: false
});
Expand Down
145 changes: 145 additions & 0 deletions modules/layers/src/bitmap-layer/bitmap-layer.wgsl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// deck.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

export default /* wgsl */ `\
Comment thread
ibgreen-openai marked this conversation as resolved.
struct Attributes {
@location(0) positions: vec3<f32>,
@location(1) positions64Low: vec3<f32>,
@location(2) texCoords: vec2<f32>,
};

struct Varyings {
@builtin(position) position: vec4<f32>,
@location(0) vTexCoord: vec2<f32>,
@location(1) vTexPos: vec2<f32>,
@location(2) pickingColor: vec3<f32>,
@location(3) pickingDepth: f32,
};

// from degrees to Web Mercator
fn lnglat_to_mercator(lnglat: vec2<f32>) -> vec2<f32> {
let x = lnglat.x;
let y = clamp(lnglat.y, -89.9, 89.9);
return vec2<f32>(
radians(x) + PI,
PI + log(tan(PI * 0.25 + radians(y) * 0.5))
) * WORLD_SCALE;
}

// from Web Mercator to degrees
fn mercator_to_lnglat(xy: vec2<f32>) -> vec2<f32> {
let position = xy / WORLD_SCALE;
return degrees(vec2<f32>(
position.x - PI,
atan(exp(position.y - PI)) * 2.0 - PI * 0.5
));
}

fn color_desaturate(colorValue: vec3<f32>) -> vec3<f32> {
let luminance = (colorValue.r + colorValue.g + colorValue.b) * 0.333333333;
return mix(colorValue, vec3<f32>(luminance), bitmap.desaturate);
}

fn color_tint(colorValue: vec3<f32>) -> vec3<f32> {
return colorValue * bitmap.tintColor;
}

fn apply_opacity(colorValue: vec3<f32>, alpha: f32) -> vec4<f32> {
if (bitmap.transparentColor.a == 0.0) {
return vec4<f32>(colorValue, alpha);
}
let blendedAlpha = alpha + bitmap.transparentColor.a * (1.0 - alpha);
let highLightRatio = alpha / blendedAlpha;
let blendedRGB = mix(bitmap.transparentColor.rgb, colorValue, highLightRatio);
return vec4<f32>(blendedRGB, blendedAlpha);
}

fn getUV(position: vec2<f32>) -> vec2<f32> {
return vec2<f32>(
(position.x - bitmap.bounds[0]) / (bitmap.bounds[2] - bitmap.bounds[0]),
(position.y - bitmap.bounds[3]) / (bitmap.bounds[1] - bitmap.bounds[3])
);
}

// Pack the top 12 bits of two normalized floats into three 8-bit values.
fn packUVsIntoRGB(uv: vec2<f32>) -> vec3<f32> {
let uv8bit = floor(uv * 256.0);
let uvFraction = fract(uv * 256.0);
let uvFraction4bit = floor(uvFraction * 16.0);
let fractions = uvFraction4bit.x + uvFraction4bit.y * 16.0;
return vec3<f32>(uv8bit, fractions) / 255.0;
}

@vertex
fn vertexMain(attributes: Attributes) -> Varyings {
var output: Varyings;
geometry.worldPosition = attributes.positions;
geometry.uv = attributes.texCoords;
geometry.pickingColor = picking_getPickingColorFromIndex(0u);

let projectedPosition = project_position_to_clipspace_and_commonspace(
attributes.positions,
attributes.positions64Low,
vec3<f32>(0.0)
);
geometry.position = projectedPosition.commonPosition;
output.position = projectedPosition.clipPosition;
output.vTexCoord = attributes.texCoords;
output.vTexPos = vec2<f32>(0.0);
output.pickingColor = geometry.pickingColor;
output.pickingDepth = output.position.z / output.position.w;

if (bitmap.coordinateConversion < -0.5) {
output.vTexPos = geometry.position.xy + project.commonOrigin.xy;
} else if (bitmap.coordinateConversion > 0.5) {
output.vTexPos = geometry.worldPosition.xy;
}

return output;
}

@fragment
fn fragmentMain(input: Varyings) -> @location(0) vec4<f32> {
var uv = input.vTexCoord;
if (bitmap.coordinateConversion < -0.5) {
uv = getUV(mercator_to_lnglat(input.vTexPos));
} else if (bitmap.coordinateConversion > 0.5) {
uv = getUV(lnglat_to_mercator(input.vTexPos));
}

let bitmapColor = textureSample(bitmapTexture, bitmapTextureSampler, uv);
var fragColor = apply_opacity(
color_tint(color_desaturate(bitmapColor.rgb)),
bitmapColor.a * layer.opacity
);

geometry.uv = uv;

if (picking.isActive > 0.5) {
if (picking.isAttribute > 0.5) {
return vec4<f32>(input.pickingDepth, 0.0, 0.0, 1.0);
}
return vec4<f32>(packUVsIntoRGB(uv), 1.0);
}

if (picking.isHighlightActive > 0.5) {
let highlightedObjectColor = picking_normalizeColor(picking.highlightedObjectColor);
if (picking_isColorZero(abs(input.pickingColor - highlightedObjectColor))) {
let highLightAlpha = picking.highlightColor.a;
let blendedAlpha = highLightAlpha + fragColor.a * (1.0 - highLightAlpha);
if (blendedAlpha > 0.0) {
let highLightRatio = highLightAlpha / blendedAlpha;
fragColor = vec4<f32>(
mix(fragColor.rgb, picking.highlightColor.rgb, highLightRatio),
blendedAlpha
);
} else {
fragColor = vec4<f32>(fragColor.rgb, 0.0);
}
}
}

return deckgl_premultiplied_alpha(fragColor);
}
`;
4 changes: 4 additions & 0 deletions website/src/examples/tile-layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {makeExample} from '../components';
class MapTileDemo extends Component {
static title = 'Raster Map Tiles';

static hasDeviceTabs = true;

static code = `${GITHUB_TREE}/examples/website/map-tile`;

static parameters = {
Expand Down Expand Up @@ -61,6 +63,8 @@ class MapTileDemo extends Component {
return (
<App
{...otherProps}
key={this.props.device?.type}
device={this.props.device}
showBorder={params.showBorder.value}
minZoom={params.minZoom.value}
maxZoom={params.maxZoom.value}
Expand Down
4 changes: 4 additions & 0 deletions website/src/examples/wms-layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ const SERVICES = {
class WMSDemo extends Component {
static title = 'Web Map Service (WMS)';

static hasDeviceTabs = true;

static code = `${GITHUB_TREE}/examples/website/wms`;

static parameters = {
Expand Down Expand Up @@ -126,6 +128,8 @@ class WMSDemo extends Component {
<DemoContainer>
<App
{...otherProps}
key={this.props.device?.type}
device={this.props.device}
serviceUrl={service.serviceUrl}
initialViewState={service.viewState}
layers={layers[params.layer.value] || service.defaultLayers}
Expand Down
Loading