WebGL points / rebuild buffers on every non animation frame
This commit is contained in:
@@ -11,7 +11,6 @@ import {clamp, lerp} from '../src/ol/math';
|
|||||||
import Stamen from '../src/ol/source/Stamen';
|
import Stamen from '../src/ol/source/Stamen';
|
||||||
|
|
||||||
const vectorSource = new Vector({
|
const vectorSource = new Vector({
|
||||||
features: [],
|
|
||||||
attributions: 'NASA'
|
attributions: 'NASA'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import {DefaultAttrib} from '../../webgl/Helper';
|
|||||||
import GeometryType from '../../geom/GeometryType';
|
import GeometryType from '../../geom/GeometryType';
|
||||||
import WebGLLayerRenderer, {getBlankTexture, pushFeatureInBuffer} from './Layer';
|
import WebGLLayerRenderer, {getBlankTexture, pushFeatureInBuffer} from './Layer';
|
||||||
import GeoJSON from '../../format/GeoJSON';
|
import GeoJSON from '../../format/GeoJSON';
|
||||||
|
import {getUid} from '../../util';
|
||||||
|
import ViewHint from '../../ViewHint';
|
||||||
|
|
||||||
const VERTEX_SHADER = `
|
const VERTEX_SHADER = `
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
@@ -232,6 +234,12 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.geojsonFormat_ = new GeoJSON();
|
this.geojsonFormat_ = new GeoJSON();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Object<string, import("../../format/GeoJSON").GeoJSONFeature>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.geojsonFeatureCache_ = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -263,47 +271,25 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
|||||||
prepareFrame(frameState) {
|
prepareFrame(frameState) {
|
||||||
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();
|
||||||
|
const viewState = frameState.viewState;
|
||||||
|
|
||||||
// TODO: get this from somewhere...
|
// TODO: get this from somewhere...
|
||||||
const stride = 12;
|
const stride = 12;
|
||||||
|
|
||||||
this.helper_.prepareDraw(frameState);
|
this.helper_.prepareDraw(frameState);
|
||||||
|
|
||||||
|
// the source has changed: clear the feature cache & reload features
|
||||||
if (this.sourceRevision_ < vectorSource.getRevision()) {
|
if (this.sourceRevision_ < vectorSource.getRevision()) {
|
||||||
this.sourceRevision_ = vectorSource.getRevision();
|
this.sourceRevision_ = vectorSource.getRevision();
|
||||||
this.verticesBuffer_.getArray().length = 0;
|
this.geojsonFeatureCache_ = {};
|
||||||
this.indicesBuffer_.getArray().length = 0;
|
|
||||||
|
|
||||||
const viewState = frameState.viewState;
|
|
||||||
const projection = viewState.projection;
|
const projection = viewState.projection;
|
||||||
const resolution = viewState.resolution;
|
const resolution = viewState.resolution;
|
||||||
|
|
||||||
// loop on features to fill the buffer
|
|
||||||
vectorSource.loadFeatures([-Infinity, -Infinity, Infinity, Infinity], resolution, projection);
|
vectorSource.loadFeatures([-Infinity, -Infinity, Infinity, Infinity], resolution, projection);
|
||||||
vectorSource.forEachFeature((feature) => {
|
}
|
||||||
if (!feature.getGeometry() || feature.getGeometry().getType() !== GeometryType.POINT) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const geojsonFeature = this.geojsonFormat_.writeFeatureObject(feature);
|
if (!frameState.viewHints[ViewHint.ANIMATING] && !frameState.viewHints[ViewHint.INTERACTING]) {
|
||||||
|
this.rebuildBuffers_(frameState);
|
||||||
geojsonFeature.geometry.coordinates[0] = this.coordCallback_(feature, 0);
|
|
||||||
geojsonFeature.geometry.coordinates[1] = this.coordCallback_(feature, 1);
|
|
||||||
geojsonFeature.properties = geojsonFeature.properties || {};
|
|
||||||
geojsonFeature.properties.color = this.colorCallback_(feature, this.colorArray_);
|
|
||||||
geojsonFeature.properties.u0 = this.texCoordCallback_(feature, 0);
|
|
||||||
geojsonFeature.properties.v0 = this.texCoordCallback_(feature, 1);
|
|
||||||
geojsonFeature.properties.u1 = this.texCoordCallback_(feature, 2);
|
|
||||||
geojsonFeature.properties.v1 = this.texCoordCallback_(feature, 3);
|
|
||||||
geojsonFeature.properties.size = this.sizeCallback_(feature);
|
|
||||||
geojsonFeature.properties.opacity = this.opacityCallback_(feature);
|
|
||||||
geojsonFeature.properties.rotateWithView = this.rotateWithViewCallback_(feature) ? 1 : 0;
|
|
||||||
|
|
||||||
pushFeatureInBuffer(this.verticesBuffer_, this.indicesBuffer_, geojsonFeature);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.helper_.flushBufferData(ARRAY_BUFFER, this.verticesBuffer_);
|
|
||||||
this.helper_.flushBufferData(ELEMENT_ARRAY_BUFFER, this.indicesBuffer_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// write new data
|
// write new data
|
||||||
@@ -320,6 +306,52 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rebuild internal webgl buffers based on current view extent; costly, should not be called too much
|
||||||
|
* @param {import("../../PluggableMap.js").FrameState} frameState
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
rebuildBuffers_(frameState) {
|
||||||
|
const vectorLayer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer());
|
||||||
|
const vectorSource = vectorLayer.getSource();
|
||||||
|
|
||||||
|
this.verticesBuffer_.getArray().length = 0;
|
||||||
|
this.indicesBuffer_.getArray().length = 0;
|
||||||
|
|
||||||
|
// loop on features to fill the buffer
|
||||||
|
const features = vectorSource.getFeatures();
|
||||||
|
let feature;
|
||||||
|
for (let i = 0; i < features.length; i++) {
|
||||||
|
feature = features[i];
|
||||||
|
if (!feature.getGeometry() || feature.getGeometry().getType() !== GeometryType.POINT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let geojsonFeature = this.geojsonFeatureCache_[getUid(feature)];
|
||||||
|
if (!geojsonFeature) {
|
||||||
|
geojsonFeature = this.geojsonFormat_.writeFeatureObject(feature);
|
||||||
|
this.geojsonFeatureCache_[getUid(feature)] = geojsonFeature;
|
||||||
|
}
|
||||||
|
|
||||||
|
geojsonFeature.geometry.coordinates[0] = this.coordCallback_(feature, 0);
|
||||||
|
geojsonFeature.geometry.coordinates[1] = this.coordCallback_(feature, 1);
|
||||||
|
geojsonFeature.properties = geojsonFeature.properties || {};
|
||||||
|
geojsonFeature.properties.color = this.colorCallback_(feature, this.colorArray_);
|
||||||
|
geojsonFeature.properties.u0 = this.texCoordCallback_(feature, 0);
|
||||||
|
geojsonFeature.properties.v0 = this.texCoordCallback_(feature, 1);
|
||||||
|
geojsonFeature.properties.u1 = this.texCoordCallback_(feature, 2);
|
||||||
|
geojsonFeature.properties.v1 = this.texCoordCallback_(feature, 3);
|
||||||
|
geojsonFeature.properties.size = this.sizeCallback_(feature);
|
||||||
|
geojsonFeature.properties.opacity = this.opacityCallback_(feature);
|
||||||
|
geojsonFeature.properties.rotateWithView = this.rotateWithViewCallback_(feature) ? 1 : 0;
|
||||||
|
|
||||||
|
pushFeatureInBuffer(this.verticesBuffer_, this.indicesBuffer_, geojsonFeature);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.helper_.flushBufferData(ARRAY_BUFFER, this.verticesBuffer_);
|
||||||
|
this.helper_.flushBufferData(ELEMENT_ARRAY_BUFFER, this.indicesBuffer_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default WebGLPointsLayerRenderer;
|
export default WebGLPointsLayerRenderer;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import VectorSource from '../../../../../src/ol/source/Vector.js';
|
|||||||
import WebGLPointsLayerRenderer from '../../../../../src/ol/renderer/webgl/PointsLayer';
|
import WebGLPointsLayerRenderer from '../../../../../src/ol/renderer/webgl/PointsLayer';
|
||||||
import {get as getProjection} from '../../../../../src/ol/proj';
|
import {get as getProjection} from '../../../../../src/ol/proj';
|
||||||
import Polygon from '../../../../../src/ol/geom/Polygon';
|
import Polygon from '../../../../../src/ol/geom/Polygon';
|
||||||
|
import ViewHint from '../../../../../src/ol/ViewHint';
|
||||||
|
|
||||||
|
|
||||||
describe('ol.renderer.webgl.PointsLayer', function() {
|
describe('ol.renderer.webgl.PointsLayer', function() {
|
||||||
@@ -53,7 +54,8 @@ describe('ol.renderer.webgl.PointsLayer', function() {
|
|||||||
rotation: 0,
|
rotation: 0,
|
||||||
center: [10, 10]
|
center: [10, 10]
|
||||||
},
|
},
|
||||||
size: [256, 256]
|
size: [256, 256],
|
||||||
|
extent: [-100, -100, 100, 100]
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -103,6 +105,24 @@ describe('ol.renderer.webgl.PointsLayer', function() {
|
|||||||
expect(renderer.indicesBuffer_.getArray().length).to.eql(6);
|
expect(renderer.indicesBuffer_.getArray().length).to.eql(6);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('rebuilds the buffers only when not interacting or animating', function() {
|
||||||
|
const spy = sinon.spy(renderer, 'rebuildBuffers_');
|
||||||
|
frameState.viewHints[ViewHint.INTERACTING] = 1;
|
||||||
|
frameState.viewHints[ViewHint.ANIMATING] = 0;
|
||||||
|
renderer.prepareFrame(frameState);
|
||||||
|
expect(spy.called).to.be(false);
|
||||||
|
|
||||||
|
frameState.viewHints[ViewHint.INTERACTING] = 0;
|
||||||
|
frameState.viewHints[ViewHint.ANIMATING] = 1;
|
||||||
|
renderer.prepareFrame(frameState);
|
||||||
|
expect(spy.called).to.be(false);
|
||||||
|
|
||||||
|
frameState.viewHints[ViewHint.INTERACTING] = 0;
|
||||||
|
frameState.viewHints[ViewHint.ANIMATING] = 0;
|
||||||
|
renderer.prepareFrame(frameState);
|
||||||
|
expect(spy.called).to.be(true);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user