Skip to content

Commit

Permalink
GPU Aggregation: miscellaneous clean up (#9142)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pessimistress authored Sep 8, 2024
1 parent 8a7c3c7 commit c20af82
Show file tree
Hide file tree
Showing 14 changed files with 377 additions and 79 deletions.
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({
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 [
[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

0 comments on commit c20af82

Please sign in to comment.