• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

visgl / deck.gl / 9640057527

24 Jun 2024 05:26AM UTC coverage: 89.281% (-0.2%) from 89.454%
9640057527

push

github

web-flow
GPU Aggregation (4/8): ScreenGridLayer (#8942)

6512 of 7149 branches covered (91.09%)

Branch coverage included in aggregate %.

341 of 359 new or added lines in 12 files covered. (94.99%)

234 existing lines in 6 files now uncovered.

53640 of 60225 relevant lines covered (89.07%)

6419.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

38.52
/modules/aggregation-layers/src/grid-aggregation-layer.ts
1
// Copyright (c) 2015 - 2017 Uber Technologies, Inc.
1✔
2
//
1✔
3
// Permission is hereby granted, free of charge, to any person obtaining a copy
1✔
4
// of this software and associated documentation files (the "Software"), to deal
1✔
5
// in the Software without restriction, including without limitation the rights
1✔
6
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1✔
7
// copies of the Software, and to permit persons to whom the Software is
1✔
8
// furnished to do so, subject to the following conditions:
1✔
9
//
1✔
10
// The above copyright notice and this permission notice shall be included in
1✔
11
// all copies or substantial portions of the Software.
1✔
12
//
1✔
13
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1✔
14
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1✔
15
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1✔
16
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1✔
17
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1✔
18
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1✔
19
// THE SOFTWARE.
1✔
20

1✔
21
import {GL} from '@luma.gl/constants';
1✔
22
import {LayerContext, log, UpdateParameters, Accessor} from '@deck.gl/core';
1✔
23
import AggregationLayer, {AggregationLayerProps} from './aggregation-layer';
1✔
24
import GPUGridAggregator from './utils/gpu-grid-aggregation/gpu-grid-aggregator';
1✔
25
import BinSorter from './utils/bin-sorter';
1✔
26
import {pointToDensityGridDataCPU} from './cpu-grid-layer/grid-aggregator';
1✔
27
import type {Buffer} from '@luma.gl/core';
1✔
28

1✔
29
export type GridAggregationLayerProps<DataT> = AggregationLayerProps<DataT>;
1✔
30

1✔
31
export default abstract class GridAggregationLayer<
1✔
32
  DataT,
1✔
33
  ExtraPropsT extends {} = {}
1✔
34
> extends AggregationLayer<DataT, ExtraPropsT> {
1✔
35
  static layerName = 'GridAggregationLayer';
1✔
36

1✔
37
  state!: AggregationLayer<DataT>['state'] & {
1✔
38
    aggregationDataDirty?: boolean;
1✔
39
    aggregationWeightsDirty?: boolean;
1✔
40
    aggregationDirty?: boolean;
1✔
41
    gpuAggregation?: any;
1✔
42
    getValue?: () => any;
1✔
43
    sortedBins?: BinSorter;
1✔
44
    weights: {
1✔
45
      [key: string]: {
1✔
46
        aggregationBuffer?: Buffer;
1✔
47
        maxMinBuffer?: Buffer;
1✔
48
        aggregationData: Float32Array | null;
1✔
49
        maxMinData: Float32Array;
1✔
50
        maxData: Float32Array;
1✔
51
        minData: Float32Array;
1✔
52
        getWeight: Accessor<DataT, number>;
1✔
53
        operation: number;
1✔
54
      };
1✔
55
    };
1✔
56
    cpuGridAggregator?: Function;
1✔
57
    gpuGridAggregator?: GPUGridAggregator;
1✔
58
    layerData: any;
1✔
59
    numRow: number;
1✔
60
    numCol: number;
1✔
61
    gridOffset: {xOffset: number; yOffset: number};
1✔
62
    posOffset: number[];
1✔
63
    gridOrigin: number[];
1✔
64
    translation: number[];
1✔
65
    scaling: number[];
1✔
66
    boundingBox: {xMin: number; yMin: number; xMax: number; yMax: number};
1✔
67
    projectPoints?: (p: [number, number]) => [number, number];
1✔
68
  };
1✔
69

1✔
70
  initializeAggregationLayer({dimensions}) {
1✔
UNCOV
71
    super.initializeAggregationLayer(dimensions);
×
UNCOV
72
    this.setState({
×
UNCOV
73
      // CPU aggregation results
×
UNCOV
74
      layerData: {},
×
UNCOV
75
      gpuGridAggregator: new GPUGridAggregator(this.context.device, {
×
UNCOV
76
        id: `${this.id}-gpu-aggregator`
×
UNCOV
77
      }),
×
UNCOV
78
      cpuGridAggregator: pointToDensityGridDataCPU
×
UNCOV
79
    });
×
UNCOV
80
  }
×
81

1✔
82
  updateState(opts: UpdateParameters<this>) {
1✔
UNCOV
83
    // get current attributes
×
UNCOV
84
    super.updateState(opts);
×
UNCOV
85

×
UNCOV
86
    this.updateAggregationState(opts);
×
UNCOV
87

×
UNCOV
88
    const {aggregationDataDirty, aggregationWeightsDirty, gpuAggregation} = this.state;
×
UNCOV
89
    if (this.getNumInstances() <= 0) {
×
UNCOV
90
      return;
×
UNCOV
91
    }
×
UNCOV
92
    let aggregationDirty = false;
×
UNCOV
93
    // CPU aggregation is two steps
×
UNCOV
94
    // 1. Create bins (based on cellSize and position) 2. Aggregate weights for each bin
×
UNCOV
95
    // For GPU aggregation both above steps are combined into one step
×
UNCOV
96

×
UNCOV
97
    // step-1
×
UNCOV
98
    if (aggregationDataDirty || (gpuAggregation && aggregationWeightsDirty)) {
×
UNCOV
99
      this._updateAggregation(opts);
×
UNCOV
100
      aggregationDirty = true;
×
UNCOV
101
    }
×
UNCOV
102
    // step-2 (Applicalbe for CPU aggregation only)
×
UNCOV
103
    if (!gpuAggregation && (aggregationDataDirty || aggregationWeightsDirty)) {
×
UNCOV
104
      this._updateWeightBins();
×
UNCOV
105
      this._uploadAggregationResults();
×
UNCOV
106
      aggregationDirty = true;
×
UNCOV
107
    }
×
UNCOV
108

×
UNCOV
109
    this.setState({aggregationDirty});
×
UNCOV
110
  }
×
111

1✔
112
  finalizeState(context: LayerContext) {
1✔
UNCOV
113
    const {count} = this.state.weights;
×
UNCOV
114
    if (count && count.aggregationBuffer) {
×
UNCOV
115
      count.aggregationBuffer.delete();
×
UNCOV
116
    }
×
UNCOV
117
    this.state.gpuGridAggregator?.delete();
×
UNCOV
118
    super.finalizeState(context);
×
UNCOV
119
  }
×
120

1✔
121
  updateShaders(shaders: any): void {
1✔
UNCOV
122
    if (this.state.gpuAggregation) {
×
123
      this.state.gpuGridAggregator!.updateShaders(shaders);
×
124
    }
×
UNCOV
125
  }
×
126

1✔
127
  // Methods that can be overriden by subclasses for customizations
1✔
128

1✔
129
  updateAggregationState(opts: UpdateParameters<this>) {
1✔
130
    // Sublayers should implement this method.
×
131
    log.assert(false);
×
132
  }
×
133

1✔
134
  allocateResources(numRow, numCol) {
1✔
UNCOV
135
    if (this.state.numRow !== numRow || this.state.numCol !== numCol) {
×
UNCOV
136
      const dataBytes = numCol * numRow * 4 * 4;
×
UNCOV
137
      const {weights} = this.state;
×
UNCOV
138
      for (const name in weights) {
×
UNCOV
139
        const weight = weights[name];
×
UNCOV
140
        if (weight.aggregationBuffer) {
×
UNCOV
141
          weight.aggregationBuffer.delete();
×
UNCOV
142
        }
×
UNCOV
143
        weight.aggregationBuffer = this.context.device.createBuffer({
×
UNCOV
144
          byteLength: dataBytes,
×
UNCOV
145
          // @ts-expect-error legacy
×
UNCOV
146
          accessor: {
×
UNCOV
147
            size: 4,
×
UNCOV
148
            type: GL.FLOAT,
×
UNCOV
149
            divisor: 1
×
UNCOV
150
          }
×
UNCOV
151
        });
×
UNCOV
152
      }
×
UNCOV
153
    }
×
UNCOV
154
  }
×
155

1✔
156
  updateResults({aggregationData, maxMinData, maxData, minData}) {
1✔
157
    const {count} = this.state.weights;
×
158
    if (count) {
×
159
      count.aggregationData = aggregationData;
×
160
      count.maxMinData = maxMinData;
×
161
      count.maxData = maxData;
×
162
      count.minData = minData;
×
163
    }
×
164
  }
×
165

1✔
166
  // Private
1✔
167

1✔
168
  _updateAggregation(opts) {
1✔
UNCOV
169
    const {
×
UNCOV
170
      cpuGridAggregator,
×
UNCOV
171
      gpuGridAggregator,
×
UNCOV
172
      gridOffset,
×
UNCOV
173
      posOffset,
×
UNCOV
174
      translation = [0, 0],
×
UNCOV
175
      scaling = [0, 0, 0],
×
UNCOV
176
      boundingBox,
×
UNCOV
177
      projectPoints,
×
UNCOV
178
      gpuAggregation,
×
UNCOV
179
      numCol,
×
UNCOV
180
      numRow
×
UNCOV
181
    } = this.state;
×
UNCOV
182
    const {props} = opts;
×
UNCOV
183
    const {viewport} = this.context;
×
UNCOV
184
    const attributes = this.getAttributes();
×
UNCOV
185
    const vertexCount = this.getNumInstances();
×
UNCOV
186

×
UNCOV
187
    if (!gpuAggregation) {
×
UNCOV
188
      const result = cpuGridAggregator!(props, {
×
UNCOV
189
        gridOffset,
×
UNCOV
190
        projectPoints,
×
UNCOV
191
        attributes,
×
UNCOV
192
        viewport,
×
UNCOV
193
        posOffset,
×
UNCOV
194
        boundingBox
×
UNCOV
195
      });
×
UNCOV
196
      this.setState({
×
UNCOV
197
        layerData: result
×
UNCOV
198
      });
×
UNCOV
199
    } else {
×
200
      const {weights} = this.state;
×
201
      gpuGridAggregator!.run({
×
202
        weights,
×
203
        cellSize: [gridOffset.xOffset, gridOffset.yOffset],
×
204
        numCol,
×
205
        numRow,
×
206
        translation,
×
207
        scaling,
×
208
        vertexCount,
×
209
        projectPoints,
×
210
        attributes,
×
211
        moduleSettings: this.getModuleSettings()
×
212
      });
×
213
    }
×
UNCOV
214
  }
×
215

1✔
216
  _updateWeightBins() {
1✔
UNCOV
217
    const {getValue} = this.state;
×
UNCOV
218

×
UNCOV
219
    const sortedBins = new BinSorter(this.state.layerData.data || [], {getValue});
×
UNCOV
220
    this.setState({sortedBins});
×
UNCOV
221
  }
×
222

1✔
223
  _uploadAggregationResults(): void {
1✔
UNCOV
224
    const {numCol, numRow} = this.state;
×
UNCOV
225
    const {data} = this.state.layerData;
×
UNCOV
226
    const {aggregatedBins, minValue, maxValue, totalCount} = this.state.sortedBins!;
×
UNCOV
227

×
UNCOV
228
    const ELEMENTCOUNT = 4;
×
UNCOV
229
    const aggregationSize = numCol * numRow * ELEMENTCOUNT;
×
UNCOV
230
    const aggregationData = new Float32Array(aggregationSize).fill(0);
×
UNCOV
231
    for (const bin of aggregatedBins) {
×
232
      const {lonIdx, latIdx} = data[bin.i];
×
233
      const {value, counts} = bin;
×
234
      const cellIndex = (lonIdx + latIdx * numCol) * ELEMENTCOUNT;
×
235
      aggregationData[cellIndex] = value;
×
236
      aggregationData[cellIndex + ELEMENTCOUNT - 1] = counts;
×
237
    }
×
UNCOV
238
    const maxMinData = new Float32Array([maxValue, 0, 0, minValue]);
×
UNCOV
239
    const maxData = new Float32Array([maxValue, 0, 0, totalCount]);
×
UNCOV
240
    const minData = new Float32Array([minValue, 0, 0, totalCount]);
×
UNCOV
241
    this.updateResults({aggregationData, maxMinData, maxData, minData});
×
UNCOV
242
  }
×
243
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc