Webgl points / use the new typed-array based utils for buffers
`rebuildBuffer` is still a very CPU intensive task and is blocking the main thread.
This commit is contained in:
@@ -5,9 +5,11 @@ import WebGLArrayBuffer from '../../webgl/Buffer.js';
|
|||||||
import {DYNAMIC_DRAW, ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, FLOAT} from '../../webgl.js';
|
import {DYNAMIC_DRAW, ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, FLOAT} from '../../webgl.js';
|
||||||
import {DefaultAttrib, DefaultUniform} from '../../webgl/Helper.js';
|
import {DefaultAttrib, DefaultUniform} from '../../webgl/Helper.js';
|
||||||
import GeometryType from '../../geom/GeometryType.js';
|
import GeometryType from '../../geom/GeometryType.js';
|
||||||
import WebGLLayerRenderer, {getBlankTexture, pushFeatureToBuffer} from './Layer.js';
|
import WebGLLayerRenderer, {
|
||||||
import GeoJSON from '../../format/GeoJSON.js';
|
getBlankTexture,
|
||||||
import {getUid} from '../../util.js';
|
POINT_INSTRUCTIONS_COUNT, POINT_VERTEX_STRIDE,
|
||||||
|
writePointFeatureInstructions, writePointFeatureToBuffers
|
||||||
|
} from './Layer.js';
|
||||||
import ViewHint from '../../ViewHint.js';
|
import ViewHint from '../../ViewHint.js';
|
||||||
import {createEmpty, equals} from '../../extent.js';
|
import {createEmpty, equals} from '../../extent.js';
|
||||||
import {
|
import {
|
||||||
@@ -241,14 +243,6 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.geojsonFormat_ = new GeoJSON();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {Object<string, import("../../format/GeoJSON").GeoJSONFeature>}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
this.geojsonFeatureCache_ = {};
|
|
||||||
|
|
||||||
this.previousExtent_ = createEmpty();
|
this.previousExtent_ = createEmpty();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -272,6 +266,12 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this.invertRenderTransform_ = createTransform();
|
this.invertRenderTransform_ = createTransform();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Float32Array}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.renderInstructions_ = new Float32Array(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -306,8 +306,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
|||||||
const vectorSource = vectorLayer.getSource();
|
const vectorSource = vectorLayer.getSource();
|
||||||
const viewState = frameState.viewState;
|
const viewState = frameState.viewState;
|
||||||
|
|
||||||
// TODO: get this from somewhere...
|
const stride = POINT_VERTEX_STRIDE;
|
||||||
const stride = 12;
|
|
||||||
|
|
||||||
// the source has changed: clear the feature cache & reload features
|
// the source has changed: clear the feature cache & reload features
|
||||||
const sourceChanged = this.sourceRevision_ < vectorSource.getRevision();
|
const sourceChanged = this.sourceRevision_ < vectorSource.getRevision();
|
||||||
@@ -357,46 +356,70 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
|||||||
const vectorLayer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer());
|
const vectorLayer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer());
|
||||||
const vectorSource = vectorLayer.getSource();
|
const vectorSource = vectorLayer.getSource();
|
||||||
|
|
||||||
this.verticesBuffer_.getArray().length = 0;
|
|
||||||
this.indicesBuffer_.getArray().length = 0;
|
|
||||||
|
|
||||||
// saves the projection transform for the current frame state
|
// saves the projection transform for the current frame state
|
||||||
this.helper_.makeProjectionTransform(frameState, this.renderTransform_);
|
const projectionTransform = createTransform();
|
||||||
makeInverseTransform(this.invertRenderTransform_, this.renderTransform_);
|
this.helper_.makeProjectionTransform(frameState, projectionTransform);
|
||||||
|
|
||||||
|
const features = vectorSource.getFeatures();
|
||||||
|
const totalInstructionsCount = POINT_INSTRUCTIONS_COUNT * features.length;
|
||||||
|
if (this.renderInstructions_.length !== totalInstructionsCount) {
|
||||||
|
this.renderInstructions_ = new Float32Array(totalInstructionsCount);
|
||||||
|
}
|
||||||
|
|
||||||
// loop on features to fill the buffer
|
// loop on features to fill the buffer
|
||||||
const features = vectorSource.getFeatures();
|
|
||||||
let feature;
|
let feature;
|
||||||
|
const tmpCoords = [];
|
||||||
|
let elementIndex = 0;
|
||||||
for (let i = 0; i < features.length; i++) {
|
for (let i = 0; i < features.length; i++) {
|
||||||
feature = features[i];
|
feature = features[i];
|
||||||
if (!feature.getGeometry() || feature.getGeometry().getType() !== GeometryType.POINT) {
|
if (!feature.getGeometry() || feature.getGeometry().getType() !== GeometryType.POINT) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let geojsonFeature = this.geojsonFeatureCache_[getUid(feature)];
|
tmpCoords[0] = this.coordCallback_(feature, 0);
|
||||||
if (!geojsonFeature) {
|
tmpCoords[1] = this.coordCallback_(feature, 1);
|
||||||
geojsonFeature = this.geojsonFormat_.writeFeatureObject(feature);
|
applyTransform(projectionTransform, tmpCoords);
|
||||||
this.geojsonFeatureCache_[getUid(feature)] = geojsonFeature;
|
|
||||||
}
|
|
||||||
|
|
||||||
geojsonFeature.geometry.coordinates[0] = this.coordCallback_(feature, 0);
|
elementIndex = writePointFeatureInstructions(
|
||||||
geojsonFeature.geometry.coordinates[1] = this.coordCallback_(feature, 1);
|
this.renderInstructions_,
|
||||||
applyTransform(this.renderTransform_, geojsonFeature.geometry.coordinates);
|
elementIndex,
|
||||||
geojsonFeature.properties = geojsonFeature.properties || {};
|
tmpCoords[0],
|
||||||
geojsonFeature.properties.color = this.colorCallback_(feature, this.colorArray_);
|
tmpCoords[1],
|
||||||
geojsonFeature.properties.u0 = this.texCoordCallback_(feature, 0);
|
this.texCoordCallback_(feature, 0),
|
||||||
geojsonFeature.properties.v0 = this.texCoordCallback_(feature, 1);
|
this.texCoordCallback_(feature, 1),
|
||||||
geojsonFeature.properties.u1 = this.texCoordCallback_(feature, 2);
|
this.texCoordCallback_(feature, 2),
|
||||||
geojsonFeature.properties.v1 = this.texCoordCallback_(feature, 3);
|
this.texCoordCallback_(feature, 3),
|
||||||
geojsonFeature.properties.size = this.sizeCallback_(feature);
|
this.sizeCallback_(feature),
|
||||||
geojsonFeature.properties.opacity = this.opacityCallback_(feature);
|
this.opacityCallback_(feature),
|
||||||
geojsonFeature.properties.rotateWithView = this.rotateWithViewCallback_(feature) ? 1 : 0;
|
this.rotateWithViewCallback_(feature),
|
||||||
|
this.colorCallback_(feature, this.colorArray_)
|
||||||
pushFeatureToBuffer(this.verticesBuffer_, this.indicesBuffer_, geojsonFeature);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const elementsCount = this.renderInstructions_.length / POINT_INSTRUCTIONS_COUNT;
|
||||||
|
const indexBuffer = new Uint32Array(elementsCount * 6);
|
||||||
|
const vertexBuffer = new Float32Array(elementsCount * 4 * POINT_VERTEX_STRIDE);
|
||||||
|
|
||||||
|
let bufferPositions = null;
|
||||||
|
for (let i = 0; i < this.renderInstructions_.length; i += POINT_INSTRUCTIONS_COUNT) {
|
||||||
|
bufferPositions = writePointFeatureToBuffers(
|
||||||
|
this.renderInstructions_,
|
||||||
|
i,
|
||||||
|
vertexBuffer,
|
||||||
|
indexBuffer,
|
||||||
|
bufferPositions,
|
||||||
|
POINT_INSTRUCTIONS_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: improve the WebGLBuffer private api: we shouldn't need to switch back to plain Arrays
|
||||||
|
// also we need to handle the case where Uint32 array cannot be used
|
||||||
|
this.verticesBuffer_.arr_ = Array.from(vertexBuffer);
|
||||||
|
this.indicesBuffer_.arr_ = Array.from(indexBuffer);
|
||||||
this.helper_.flushBufferData(ARRAY_BUFFER, this.verticesBuffer_);
|
this.helper_.flushBufferData(ARRAY_BUFFER, this.verticesBuffer_);
|
||||||
this.helper_.flushBufferData(ELEMENT_ARRAY_BUFFER, this.indicesBuffer_);
|
this.helper_.flushBufferData(ELEMENT_ARRAY_BUFFER, this.indicesBuffer_);
|
||||||
|
|
||||||
|
this.renderTransform_ = projectionTransform;
|
||||||
|
makeInverseTransform(this.invertRenderTransform_, this.renderTransform_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import Feature from '../../../../../src/ol/Feature.js';
|
import Feature from '../../../../../src/ol/Feature.js';
|
||||||
import Point from '../../../../../src/ol/geom/Point.js';
|
import Point from '../../../../../src/ol/geom/Point.js';
|
||||||
import LineString from '../../../../../src/ol/geom/LineString.js';
|
|
||||||
import VectorLayer from '../../../../../src/ol/layer/Vector.js';
|
import VectorLayer from '../../../../../src/ol/layer/Vector.js';
|
||||||
import VectorSource from '../../../../../src/ol/source/Vector.js';
|
import VectorSource from '../../../../../src/ol/source/Vector.js';
|
||||||
import WebGLPointsLayerRenderer from '../../../../../src/ol/renderer/webgl/PointsLayer.js';
|
import WebGLPointsLayerRenderer from '../../../../../src/ol/renderer/webgl/PointsLayer.js';
|
||||||
import {get as getProjection} from '../../../../../src/ol/proj.js';
|
import {get as getProjection} from '../../../../../src/ol/proj.js';
|
||||||
import Polygon from '../../../../../src/ol/geom/Polygon.js';
|
|
||||||
import ViewHint from '../../../../../src/ol/ViewHint.js';
|
import ViewHint from '../../../../../src/ol/ViewHint.js';
|
||||||
|
|
||||||
|
|
||||||
@@ -52,9 +50,9 @@ describe('ol.renderer.webgl.PointsLayer', function() {
|
|||||||
projection: projection,
|
projection: projection,
|
||||||
resolution: 1,
|
resolution: 1,
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
center: [10, 10]
|
center: [0, 0]
|
||||||
},
|
},
|
||||||
size: [256, 256],
|
size: [2, 2],
|
||||||
extent: [-100, -100, 100, 100]
|
extent: [-100, -100, 100, 100]
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -69,24 +67,19 @@ describe('ol.renderer.webgl.PointsLayer', function() {
|
|||||||
layer.getSource().addFeature(new Feature({
|
layer.getSource().addFeature(new Feature({
|
||||||
geometry: new Point([10, 20])
|
geometry: new Point([10, 20])
|
||||||
}));
|
}));
|
||||||
renderer.prepareFrame(frameState);
|
|
||||||
|
|
||||||
const attributePerVertex = 12;
|
|
||||||
expect(renderer.verticesBuffer_.getArray().length).to.eql(4 * attributePerVertex);
|
|
||||||
expect(renderer.indicesBuffer_.getArray().length).to.eql(6);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('ignores geometries other than points', function() {
|
|
||||||
layer.getSource().addFeature(new Feature({
|
layer.getSource().addFeature(new Feature({
|
||||||
geometry: new LineString([[10, 20], [30, 20]])
|
geometry: new Point([30, 40])
|
||||||
}));
|
|
||||||
layer.getSource().addFeature(new Feature({
|
|
||||||
geometry: new Polygon([[10, 20], [30, 20], [30, 10], [10, 20]])
|
|
||||||
}));
|
}));
|
||||||
renderer.prepareFrame(frameState);
|
renderer.prepareFrame(frameState);
|
||||||
|
|
||||||
expect(renderer.verticesBuffer_.getArray().length).to.eql(0);
|
const attributePerVertex = POINT_VERTEX_STRIDE;
|
||||||
expect(renderer.indicesBuffer_.getArray().length).to.eql(0);
|
expect(renderer.verticesBuffer_.getArray().length).to.eql(2 * 4 * attributePerVertex);
|
||||||
|
expect(renderer.indicesBuffer_.getArray().length).to.eql(2 * 6);
|
||||||
|
|
||||||
|
expect(renderer.verticesBuffer_.getArray()[0]).to.eql(10);
|
||||||
|
expect(renderer.verticesBuffer_.getArray()[1]).to.eql(20);
|
||||||
|
expect(renderer.verticesBuffer_.getArray()[4 * attributePerVertex + 0]).to.eql(30);
|
||||||
|
expect(renderer.verticesBuffer_.getArray()[4 * attributePerVertex + 1]).to.eql(40);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('clears the buffers when the features are gone', function() {
|
it('clears the buffers when the features are gone', function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user