Skip to content

Commit

Permalink
Imrove test coverage for Grid layers and utils
Browse files Browse the repository at this point in the history
  • Loading branch information
1chandu committed Jun 20, 2019
1 parent 60f395b commit ab70bed
Show file tree
Hide file tree
Showing 9 changed files with 256 additions and 24 deletions.
Expand Up @@ -23,7 +23,7 @@ import GL from '@luma.gl/constants';
import {Model, CubeGeometry, fp64, PhongMaterial} from '@luma.gl/core';
const {fp64LowPart} = fp64;
const defaultMaterial = new PhongMaterial();
import {defaultColorRange} from '../utils/color-utils';
import {defaultColorRange, colorRangeToFlatArray} from '../utils/color-utils';

import vs from './gpu-grid-cell-layer-vertex.glsl';
import fs from './gpu-grid-cell-layer-fragment.glsl';
Expand Down Expand Up @@ -102,19 +102,19 @@ export default class GPUGridCellLayer extends Layer {
gridSize,
gridOrigin,
gridOffset,
colorRange,
elevationRange
} = this.props;

const gridOriginLow = [fp64LowPart(gridOrigin[0]), fp64LowPart(gridOrigin[1])];
const gridOffsetLow = [fp64LowPart(gridOffset[0]), fp64LowPart(gridOffset[1])];
const colorMaxMinBuffer = data.color.maxMinBuffer;
const elevationMaxMinBuffer = data.elevation.maxMinBuffer;

colorMaxMinBuffer.bind({target: GL.UNIFORM_BUFFER, index: COLOR_DATA_UBO_INDEX});
elevationMaxMinBuffer.bind({target: GL.UNIFORM_BUFFER, index: ELEVATION_DATA_UBO_INDEX});
const domainUniforms = this.getDomainUniforms();
const uniformBuffers = {
colorMaxMinBuffer: data.color.maxMinBuffer,
elevationMaxMinBuffer: data.elevation.maxMinBuffer
};
const colorRange = colorRangeToFlatArray(this.props.colorRange, Float32Array, 255);

this.bindUniformBuffers(uniformBuffers);
this.state.model
.setUniforms(
Object.assign({}, uniforms, domainUniforms, {
Expand All @@ -133,6 +133,15 @@ export default class GPUGridCellLayer extends Layer {
})
)
.draw();
this.unbindUniformBuffers(uniformBuffers);
}

bindUniformBuffers({colorMaxMinBuffer, elevationMaxMinBuffer}) {
colorMaxMinBuffer.bind({target: GL.UNIFORM_BUFFER, index: COLOR_DATA_UBO_INDEX});
elevationMaxMinBuffer.bind({target: GL.UNIFORM_BUFFER, index: ELEVATION_DATA_UBO_INDEX});
}

unbindUniformBuffers({colorMaxMinBuffer, elevationMaxMinBuffer}) {
colorMaxMinBuffer.unbind({target: GL.UNIFORM_BUFFER, index: COLOR_DATA_UBO_INDEX});
elevationMaxMinBuffer.unbind({target: GL.UNIFORM_BUFFER, index: ELEVATION_DATA_UBO_INDEX});
}
Expand Down
3 changes: 3 additions & 0 deletions modules/aggregation-layers/src/utils/color-utils.js
Expand Up @@ -29,6 +29,9 @@ export const defaultColorRange = [

// Converts a colorRange array to a flat array with 4 components per color
export function colorRangeToFlatArray(colorRange, ArrayType, defaultValue) {
if (colorRange.constructor === ArrayType) {
return colorRange;
}
const flatArray = new ArrayType(colorRange.length * 4);
colorRange.forEach((color, index) => {
const flatArrayIdnex = index * 4;
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -27,6 +27,7 @@
"start": "open http://uber.github.io/deck.gl/#/documentation/getting-started/installation?section=running-the-examples",
"test": "ocular-test",
"test-fast": "ocular-test fast",
"test-node": "ocular-test node",
"test-browser": "ocular-test browser",
"bench": "ocular-test bench",
"bench-browser": "ocular-test bench-browser",
Expand Down
@@ -0,0 +1,82 @@
// Copyright (c) 2015 - 2019 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import test from 'tape-catch';
import {Buffer} from '@luma.gl/core';
import {testLayer, testInitializeLayer} from '@deck.gl/test-utils';
import GPUGridCellLayer from '@deck.gl/aggregation-layers/gpu-grid-layer/gpu-grid-cell-layer';
import {setupSpysForWebGL1, restoreSpies} from './webgl1-spies-utils';
import {gl} from '@deck.gl/test-utils';
const SAMPLE_BUFFER = new Buffer(gl);
const SAMPLE_PROPS = {
data: {
color: {
aggregationBuffer: SAMPLE_BUFFER,
maxMinBuffer: SAMPLE_BUFFER
},
elevation: {
aggregationBuffer: SAMPLE_BUFFER,
maxMinBuffer: SAMPLE_BUFFER
}
}
};

test('GPUGridCellLayer#initializeState', t => {
const webgl1Spies = setupSpysForWebGL1(gl);
const layer = new GPUGridCellLayer(SAMPLE_PROPS);

testInitializeLayer({layer, onError: t.notOk});

t.ok(layer.state.model, 'should set state.model');

restoreSpies(webgl1Spies);
t.end();
});

test('GPUGridCellLayer#updates', t => {
const webgl1Spies = setupSpysForWebGL1(gl);

testLayer({
Layer: GPUGridCellLayer,
onError: t.notOk,
testCases: [
{
props: SAMPLE_PROPS,
onAfterUpdate({layer}) {
t.ok(layer.state.model, 'should set state.model');
}
},
{
updateProps: {
cellSize: 123
},
onAfterUpdate({layer, spies}) {
t.ok(
layer.state.model.program.uniforms.cellSize === 123,
'cellSize unifrom should get updated'
);
}
}
]
});

restoreSpies(webgl1Spies);
t.end();
});
Expand Up @@ -22,8 +22,9 @@ import test from 'tape-catch';
import * as FIXTURES from 'deck.gl-test/data';
import {testLayer, generateLayerTests, testInitializeLayer} from '@deck.gl/test-utils';
import {makeSpy} from '@probe.gl/test-utils';
import {GPUGridLayer, _GPUGridAggregator as GPUGridAggregator} from '@deck.gl/aggregation-layers';
import {GPUGridLayer} from '@deck.gl/aggregation-layers';
import GPUGridCellLayer from '@deck.gl/aggregation-layers/gpu-grid-layer/gpu-grid-cell-layer';
import {setupSpysForWebGL1, restoreSpies} from './webgl1-spies-utils';
import {gl} from '@deck.gl/test-utils';

const SAMPLE_PROPS = {
Expand All @@ -32,11 +33,7 @@ const SAMPLE_PROPS = {
};

test('GPUGridLayer', t => {
if (!GPUGridAggregator.isSupported(gl)) {
t.comment('GPUGridLayer not supported, skipping');
t.end();
return;
}
const webgl1Spies = setupSpysForWebGL1(gl);
const testCases = generateLayerTests({
Layer: GPUGridLayer,
sampleProps: SAMPLE_PROPS,
Expand All @@ -49,15 +46,13 @@ test('GPUGridLayer', t => {

testLayer({Layer: GPUGridLayer, testCases, onError: t.notOk});

restoreSpies(webgl1Spies);
t.end();
});

test('GPUGridLayer#renderLayers', t => {
if (!GPUGridAggregator.isSupported(gl)) {
t.comment('GPUGridLayer not supported, skipping');
t.end();
return;
}
const webgl1Spies = setupSpysForWebGL1(gl);

makeSpy(GPUGridLayer.prototype, 'getAggregationFlags');
makeSpy(GPUGridLayer.prototype, 'getLayerData');

Expand All @@ -76,15 +71,12 @@ test('GPUGridLayer#renderLayers', t => {
GPUGridLayer.prototype.getAggregationFlags.restore();
GPUGridLayer.prototype.getLayerData.restore();

restoreSpies(webgl1Spies);
t.end();
});

test('GPUGridLayer#updates', t => {
if (!GPUGridAggregator.isSupported(gl)) {
t.comment('GPUGridLayer not supported, skipping');
t.end();
return;
}
const webgl1Spies = setupSpysForWebGL1(gl);
testLayer({
Layer: GPUGridLayer,
onError: t.notOk,
Expand Down Expand Up @@ -155,5 +147,6 @@ test('GPUGridLayer#updates', t => {
]
});

restoreSpies(webgl1Spies);
t.end();
});
@@ -0,0 +1,67 @@
// Copyright (c) 2015 - 2019 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import {isWebGL2} from '@luma.gl/core';
import {_GPUGridAggregator as GPUGridAggregator} from '@deck.gl/aggregation-layers';
import GPUGridCellLayer from '@deck.gl/aggregation-layers/gpu-grid-layer/gpu-grid-cell-layer';
import {makeSpy} from '@probe.gl/test-utils';

const VS = 'void main() {gl_Position = vec4(0);}';
const FS = 'void main() {gl_FragColor = vec4(0);}';

// Spiy methods to avoid usage of WebGL2 API when running under Node.
export function setupSpysForWebGL1(gl) {
let getShadersSpy = null;
let setupUniformBufferSpy = null;
let bindUniformBuffersSpy = null;
let unbindUniformBuffersSpy = null;
let isSupportedSpy = null;
if (!isWebGL2(gl)) {
isSupportedSpy = makeSpy(GPUGridAggregator, 'isSupported');
getShadersSpy = makeSpy(GPUGridCellLayer.prototype, 'getShaders');
setupUniformBufferSpy = makeSpy(GPUGridCellLayer.prototype, '_setupUniformBuffer');
bindUniformBuffersSpy = makeSpy(GPUGridCellLayer.prototype, 'bindUniformBuffers');
unbindUniformBuffersSpy = makeSpy(GPUGridCellLayer.prototype, 'unbindUniformBuffers');
getShadersSpy.returns({
vs: VS,
fs: FS,
modules: ['project32', 'gouraud-lighting', 'picking', 'fp64']
});
isSupportedSpy.returns(true);
setupUniformBufferSpy.returns(null);
bindUniformBuffersSpy.returns(null);
unbindUniformBuffersSpy.returns(null);
}
return {
getShadersSpy,
setupUniformBufferSpy,
bindUniformBuffersSpy,
unbindUniformBuffersSpy,
isSupportedSpy
};
}

export function restoreSpies(spies) {
for (const name in spies) {
if (spies[name]) {
spies[name].restore();
}
}
}
2 changes: 2 additions & 0 deletions test/modules/aggregation-layers/index.js
Expand Up @@ -22,10 +22,12 @@ import './contour-layer/contour-layer.spec';
import './contour-layer/marching-squares.spec';
import './gpu-grid-layer/gpu-grid-cell-layer-vertex.spec';
import './gpu-grid-layer/gpu-grid-layer.spec';
import './gpu-grid-layer/gpu-grid-cell-layer.spec';
import './cpu-grid-layer/cpu-grid-layer.spec';
import './grid-aggregator.spec';
import './hexagon-layer.spec';
import './hexagon-aggregator.spec';
import './grid-layer.spec';
import './screen-grid-layer.spec';
import './utils/scale-utils.spec';
import './utils/aggregation-operation-utils.spec';
@@ -0,0 +1,66 @@
// Copyright (c) 2015 - 2019 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import test from 'tape-catch';

import {getValueFunc} from '@deck.gl/aggregation-layers/utils/aggregation-operation-utils';

const data = [10, 'a', null, 14, -3, 16, 0.2];
const accessor = x => x;
const TEST_CASES = [
{
name: 'Min Function',
op: 'min',
data,
expected: -3
},
{
name: 'Max Function',
op: 'Max',
data,
expected: 16
},
{
name: 'Sum Function',
op: 'sUM',
data,
expected: 37.2
},
{
name: 'Mean Function',
op: 'MEAN',
data,
expected: 37.2 / 5
},
{
name: 'Invalid(should default to SUM)',
op: 'Invalid',
data,
expected: 37.2
}
];

test('GridAggregationOperationUtils#getValueFunc', t => {
TEST_CASES.forEach(tc => {
const func = getValueFunc(tc.op, accessor);
t.ok(func(data) === tc.expected, `${tc.name} should return expected result`);
});
t.end();
});
Expand Up @@ -42,7 +42,7 @@ test('GridAggregationUtils#pointToDensityGridData (CPU vs GPU)', t => {
const opts = {
data: FIXTURES.points,
getPosition,
weightParams: {weight: {needMax: 1, getWeight: x => 1}},
weightParams: {weight: {needMax: 1, needMin: 1, getWeight: x => 1}},
gpuGridAggregator,
aggregationFlags: {dataChanged: true},
fp64: true // NOTE this test fails wihtout FP64 gpu aggregation.
Expand Down Expand Up @@ -71,6 +71,15 @@ test('GridAggregationUtils#pointToDensityGridData (CPU vs GPU)', t => {
gpuMaxCountData[0],
`Max data should match for cellSizeMeters:${cellSizeMeters}`
);

const cpuMinCountsData = cpuResults.weights.weight.maxBuffer.getData();
const gpuMinCountData = gpuResults.weights.weight.maxBuffer.getData();
t.deepEqual(
cpuMinCountsData[0],
gpuMinCountData[0],
`Max data should match for cellSizeMeters:${cellSizeMeters}`
);

// TODO - This is failing in headless browser test. Might be related to
// https://github.com/uber/deck.gl/issues/3156
// t.deepEqual(
Expand Down

0 comments on commit ab70bed

Please sign in to comment.