Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GPU Aggregation: miscellaneous clean up #9142

Merged
merged 10 commits into from
Sep 8, 2024
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
2 changes: 1 addition & 1 deletion docs/api-reference/aggregation-layers/screen-grid-layer.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ Note that setting this prop does not affect how points are binned.

#### `colorScaleType` (string, optional) {#colorscaletype}

* Default: `'quantize'`
* Default: `'linear'`

The color scale converts from a continuous numeric stretch (`colorDomain`) into a list of colors (`colorRange`). Cells with value of `colorDomain[0]` will be rendered with the color of `colorRange[0]`, and cells with value of `colorDomain[1]` will be rendered with the color of `colorRange[colorRange.length - 1]`.

Expand Down
30 changes: 30 additions & 0 deletions modules/aggregation-layers/src/common/utils/bounds-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/** Utility to estimate binIdRange as expected by AggregatorProps */
export function getBinIdRange({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it only an estimate?

Suggested change
export function getBinIdRange({
export function estimateBinIdRange({

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Export this function for use in custom layers?

dataBounds,
getBinId,
padding = 0
}: {
/** Bounds of the input data */
dataBounds: [min: number[], max: number[]];
/** Given a data point, returns the bin id that it belongs to */
getBinId: (p: number[]) => number[];
/** Add a border around the result to avoid clipping */
padding?: number;
}): [number, number][] {
const corners = [
dataBounds[0],
dataBounds[1],
[dataBounds[0][0], dataBounds[1][1]],
[dataBounds[1][0], dataBounds[0][1]]
].map(p => getBinId(p));

const minX = Math.min(...corners.map(p => p[0])) - padding;
const minY = Math.min(...corners.map(p => p[1])) - padding;
const maxX = Math.max(...corners.map(p => p[0])) + padding + 1;
const maxY = Math.max(...corners.map(p => p[1])) + padding + 1;

return [
Pessimistress marked this conversation as resolved.
Show resolved Hide resolved
[minX, maxX],
[minY, maxY]
];
}
28 changes: 12 additions & 16 deletions modules/aggregation-layers/src/contour-layer/contour-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import AggregationLayer from '../common/aggregation-layer';
import {AggregationLayerProps} from '../common/aggregation-layer';
import {generateContours, Contour, ContourLine, ContourPolygon} from './contour-utils';
import {getAggregatorValueReader} from './value-reader';
import {getBinIdRange} from '../common/utils/bounds-utils';
import {Matrix4} from '@math.gl/core';
import {BinOptions, binOptionsUniforms} from './bin-options-uniforms';

Expand Down Expand Up @@ -233,7 +234,7 @@ export default class GridLayer<DataT = any, ExtraPropsT extends {} = {}> extends
const bounds = this.getBounds();
const cellSizeCommon: [number, number] = [1, 1];
let cellOriginCommon: [number, number] = [0, 0];
const binIdRange: [number, number][] = [
let binIdRange: [number, number][] = [
[0, 1],
[0, 1]
];
Expand Down Expand Up @@ -268,21 +269,16 @@ export default class GridLayer<DataT = any, ExtraPropsT extends {} = {}> extends
// Round to the nearest 32-bit float to match CPU and GPU results
cellOriginCommon = [Math.fround(viewport.center[0]), Math.fround(viewport.center[1])];

const corners = [
bounds[0],
bounds[1],
[bounds[0][0], bounds[1][1]],
[bounds[1][0], bounds[0][1]]
].map(p => viewport.projectFlat(p));

const minX = Math.min(...corners.map(p => p[0]));
const minY = Math.min(...corners.map(p => p[1]));
const maxX = Math.max(...corners.map(p => p[0]));
const maxY = Math.max(...corners.map(p => p[1]));
binIdRange[0][0] = Math.floor((minX - cellOriginCommon[0]) / cellSizeCommon[0]);
binIdRange[0][1] = Math.floor((maxX - cellOriginCommon[0]) / cellSizeCommon[0]) + 1;
binIdRange[1][0] = Math.floor((minY - cellOriginCommon[1]) / cellSizeCommon[1]);
binIdRange[1][1] = Math.floor((maxY - cellOriginCommon[1]) / cellSizeCommon[1]) + 1;
binIdRange = getBinIdRange({
dataBounds: bounds,
getBinId: (p: number[]) => {
const positionCommon = viewport.projectFlat(p);
return [
Math.floor((positionCommon[0] - cellOriginCommon[0]) / cellSizeCommon[0]),
Math.floor((positionCommon[1] - cellOriginCommon[1]) / cellSizeCommon[1])
];
}
});
}

this.setState({cellSizeCommon, cellOriginCommon, binIdRange, aggregatorViewport: viewport});
Expand Down
28 changes: 12 additions & 16 deletions modules/aggregation-layers/src/grid-layer/grid-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import AggregationLayer from '../common/aggregation-layer';
import {AggregateAccessor} from '../common/types';
import {defaultColorRange} from '../common/utils/color-utils';
import {AttributeWithScale} from '../common/utils/scale-utils';
import {getBinIdRange} from '../common/utils/bounds-utils';

import {GridCellLayer} from './grid-cell-layer';
import {BinOptions, binOptionsUniforms} from './bin-options-uniforms';
Expand Down Expand Up @@ -438,7 +439,7 @@ export default class GridLayer<DataT = any, ExtraPropsT extends {} = {}> extends
const bounds = this.getBounds();
const cellSizeCommon: [number, number] = [1, 1];
let cellOriginCommon: [number, number] = [0, 0];
const binIdRange: [number, number][] = [
let binIdRange: [number, number][] = [
[0, 1],
[0, 1]
];
Expand Down Expand Up @@ -471,21 +472,16 @@ export default class GridLayer<DataT = any, ExtraPropsT extends {} = {}> extends
// Round to the nearest 32-bit float to match CPU and GPU results
cellOriginCommon = [Math.fround(viewport.center[0]), Math.fround(viewport.center[1])];

const corners = [
bounds[0],
bounds[1],
[bounds[0][0], bounds[1][1]],
[bounds[1][0], bounds[0][1]]
].map(p => viewport.projectFlat(p));

const minX = Math.min(...corners.map(p => p[0]));
const minY = Math.min(...corners.map(p => p[1]));
const maxX = Math.max(...corners.map(p => p[0]));
const maxY = Math.max(...corners.map(p => p[1]));
binIdRange[0][0] = Math.floor((minX - cellOriginCommon[0]) / cellSizeCommon[0]);
binIdRange[0][1] = Math.floor((maxX - cellOriginCommon[0]) / cellSizeCommon[0]) + 1;
binIdRange[1][0] = Math.floor((minY - cellOriginCommon[1]) / cellSizeCommon[1]);
binIdRange[1][1] = Math.floor((maxY - cellOriginCommon[1]) / cellSizeCommon[1]) + 1;
binIdRange = getBinIdRange({
dataBounds: bounds,
getBinId: (p: number[]) => {
const positionCommon = viewport.projectFlat(p);
return [
Math.floor((positionCommon[0] - cellOriginCommon[0]) / cellSizeCommon[0]),
Math.floor((positionCommon[1] - cellOriginCommon[1]) / cellSizeCommon[1])
];
}
});
}

this.setState({cellSizeCommon, cellOriginCommon, binIdRange, aggregatorViewport: viewport});
Expand Down
30 changes: 11 additions & 19 deletions modules/aggregation-layers/src/hexagon-layer/hexagon-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import AggregationLayer from '../common/aggregation-layer';
import {AggregateAccessor} from '../common/types';
import {defaultColorRange} from '../common/utils/color-utils';
import {AttributeWithScale} from '../common/utils/scale-utils';
import {getBinIdRange} from '../common/utils/bounds-utils';

import HexagonCellLayer from './hexagon-cell-layer';
import {pointToHexbin, HexbinVertices, getHexbinCentroid, pointToHexbinGLSL} from './hexbin';
Expand Down Expand Up @@ -443,7 +444,7 @@ export default class HexagonLayer<
const bounds = this.getBounds();
let radiusCommon = 1;
let hexOriginCommon: [number, number] = [0, 0];
const binIdRange: [number, number][] = [
let binIdRange: [number, number][] = [
[0, 1],
[0, 1]
];
Expand All @@ -470,25 +471,16 @@ export default class HexagonLayer<

hexOriginCommon = [Math.fround(viewport.center[0]), Math.fround(viewport.center[1])];

const corners = [
bounds[0],
bounds[1],
[bounds[0][0], bounds[1][1]],
[bounds[1][0], bounds[0][1]]
].map(p => {
const positionCommon = viewport.projectFlat(p);
positionCommon[0] -= hexOriginCommon[0];
positionCommon[1] -= hexOriginCommon[1];
return pointToHexbin(positionCommon, radiusCommon);
binIdRange = getBinIdRange({
dataBounds: bounds,
getBinId: (p: number[]) => {
const positionCommon = viewport.projectFlat(p);
positionCommon[0] -= hexOriginCommon[0];
positionCommon[1] -= hexOriginCommon[1];
return pointToHexbin(positionCommon, radiusCommon);
},
padding: 1
});

const minX = Math.min(...corners.map(p => p[0]));
const minY = Math.min(...corners.map(p => p[1]));
const maxX = Math.max(...corners.map(p => p[0]));
const maxY = Math.max(...corners.map(p => p[1]));

binIdRange[0] = [minX - 1, maxX + 2]; // i range
binIdRange[1] = [minY - 1, maxY + 2]; // j range
}

this.setState({radiusCommon, hexOriginCommon, binIdRange, aggregatorViewport: viewport});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,18 @@
import {Texture} from '@luma.gl/core';
import {Model, Geometry} from '@luma.gl/engine';
import {Layer, picking, UpdateParameters, DefaultProps, Color} from '@deck.gl/core';
import {defaultColorRange, createColorRangeTexture} from '../common/utils/color-utils';
import {createColorRangeTexture, updateColorRangeTexture} from '../common/utils/color-utils';
import vs from './screen-grid-layer-vertex.glsl';
import fs from './screen-grid-layer-fragment.glsl';
import {ScreenGridProps, screenGridUniforms} from './screen-grid-layer-uniforms';
import {ShaderModule} from '@luma.gl/shadertools';

const defaultProps: DefaultProps<_ScreenGridCellLayerProps> = {
cellSizePixels: {type: 'number', value: 100, min: 1},
cellMarginPixels: {type: 'number', value: 2, min: 0},
colorRange: defaultColorRange
};
import type {ScaleType} from '../common/types';

/** Proprties added by ScreenGridCellLayer. */
export type _ScreenGridCellLayerProps = {
cellSizePixels?: number;
cellMarginPixels?: number;
cellSizePixels: number;
cellMarginPixels: number;
colorScaleType: ScaleType;
colorDomain: () => [number, number];
colorRange?: Color[];
};
Expand All @@ -45,7 +41,6 @@ export default class ScreenGridCellLayer<ExtraPropsT extends {} = {}> extends La
ExtraPropsT & Required<_ScreenGridCellLayerProps>
> {
static layerName = 'ScreenGridCellLayer';
static defaultProps = defaultProps;

state!: {
model?: Model;
Expand Down Expand Up @@ -81,9 +76,15 @@ export default class ScreenGridCellLayer<ExtraPropsT extends {} = {}> extends La

if (oldProps.colorRange !== props.colorRange) {
this.state.colorTexture?.destroy();
this.state.colorTexture = createColorRangeTexture(this.context.device, props.colorRange);
this.state.colorTexture = createColorRangeTexture(
this.context.device,
props.colorRange,
props.colorScaleType
);
const screenGridProps: Partial<ScreenGridProps> = {colorRange: this.state.colorTexture};
model.shaderInputs.setProps({screenGrid: screenGridProps});
} else if (oldProps.colorScaleType !== props.colorScaleType) {
updateColorRangeTexture(this.state.colorTexture, props.colorScaleType);
}

if (
Expand All @@ -110,7 +111,6 @@ export default class ScreenGridCellLayer<ExtraPropsT extends {} = {}> extends La
}

draw({uniforms}) {
// If colorDomain not specified we use dynamic domain from the aggregator
const colorDomain = this.props.colorDomain();
const model = this.state.model!;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,17 @@
// THE SOFTWARE.

/* fragment shader for the grid-layer */
export default `\
export default /* glsl */ `\
#version 300 es
#define SHADER_NAME screen-grid-layer-fragment-shader

precision highp float;

in vec4 vColor;
flat in int vIsValid;

out vec4 fragColor;

void main(void) {
if (vIsValid == 0) {
discard;
}
fragColor = vColor;

DECKGL_FILTER_COLOR(fragColor, geometry);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

export default `\
export default /* glsl */ `\
#version 300 es
#define SHADER_NAME screen-grid-layer-vertex-shader
#define RANGE_COUNT 6
Expand All @@ -31,20 +31,26 @@ in vec3 instancePickingColors;
uniform sampler2D colorRange;

out vec4 vColor;
flat out int vIsValid;

vec4 interp(float value, vec2 domain, sampler2D range) {
float r = (value - domain.x) / (domain.y - domain.x);
return texture(range, vec2(r, 0.5));
}

void main(void) {
if (isnan(instanceWeights)) {
gl_Position = vec4(0.);
return;
}

vec2 pos = instancePositions * screenGrid.gridSizeClipspace + positions * screenGrid.cellSizeClipspace;
pos.x = pos.x - 1.0;
pos.y = 1.0 - pos.y;

gl_Position = vec4(pos, 0., 1.);

vIsValid = isnan(instanceWeights) ? 0 : 1;
float r = min(max((instanceWeights - screenGrid.colorDomain.x) / (screenGrid.colorDomain.y - screenGrid.colorDomain.x), 0.), 1.);
vec4 rangeColor = texture(colorRange, vec2(r, 0.5));

vColor = vec4(rangeColor.rgb, rangeColor.a * layer.opacity);
vColor = interp(instanceWeights, screenGrid.colorDomain, colorRange);
vColor.a *= layer.opacity;

// Set color to be rendered to picking fbo (also used to check for selection highlight).
picking_setPickingColor(instancePickingColors);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,17 @@ import {WebGLAggregator, CPUAggregator, AggregationOperation} from '../common/ag
import AggregationLayer from '../common/aggregation-layer';
import ScreenGridCellLayer from './screen-grid-cell-layer';
import {BinOptions, binOptionsUniforms} from './bin-options-uniforms';
import {defaultColorRange} from '../common/utils/color-utils';

const defaultProps: DefaultProps<ScreenGridLayerProps> = {
...(ScreenGridCellLayer.defaultProps as DefaultProps<ScreenGridLayerProps>),
cellSizePixels: {type: 'number', value: 100, min: 1},
cellMarginPixels: {type: 'number', value: 2, min: 0},
colorRange: defaultColorRange,
colorScaleType: 'linear',
getPosition: {type: 'accessor', value: (d: any) => d.position},
getWeight: {type: 'accessor', value: 1},

gpuAggregation: false, // TODO(v9): Re-enable GPU aggregation.
gpuAggregation: false,
aggregation: 'SUM'
};

Expand Down Expand Up @@ -76,6 +80,13 @@ export type _ScreenGridLayerProps<DataT> = {
*/
colorRange?: Color[];

/**
* Scaling function used to determine the color of the grid cell.
* Supported Values are 'quantize', 'linear', 'quantile' and 'ordinal'.
* @default 'quantize'
*/
colorScaleType?: 'linear' | 'quantize';

/**
* Method called to retrieve the position of each object.
*
Expand Down Expand Up @@ -130,7 +141,9 @@ export default class ScreenGridLayer<
static defaultProps = defaultProps;

getAggregatorType(): string {
return this.props.gpuAggregation ? 'gpu' : 'cpu';
return this.props.gpuAggregation && WebGLAggregator.isSupported(this.context.device)
? 'gpu'
: 'cpu';
}

createAggregator(type: string): WebGLAggregator | CPUAggregator {
Expand Down
38 changes: 38 additions & 0 deletions test/apps/aggregator/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {Deck, OrthographicView} from '@deck.gl/core';
import {HistogramLayer} from './histogram-layer';

const deckgl = new Deck({
views: new OrthographicView(),
initialViewState: {
target: [0, 0, 0],
zoom: 1
},
controller: true
});

const slider = document.getElementById('bin-size-slider') as HTMLInputElement;
slider.oninput = updateLayer;
updateLayer();

function updateLayer() {
const layer = new HistogramLayer({
data: generateData(10000, 0, 100),
getPosition: d => d,
gpuAggregation: true,
binSize: Number(slider.value),
heightScale: 1
});
deckgl.setProps({layers: [layer]});
}

function generateData(count: number, mean: number, stdev: number) {
const result: number[] = [];
for (let i = 0; i < count; i++) {
// Gaussian random
const u = 1 - Math.random();
const v = Math.random();
const z = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
result.push(z * stdev + mean);
}
return result;
}
Loading
Loading