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:
Olivier Guyot
2019-05-16 22:46:44 +02:00
parent eb912d95ca
commit 532b8194b1
2 changed files with 72 additions and 56 deletions

View File

@@ -5,9 +5,11 @@ import WebGLArrayBuffer from '../../webgl/Buffer.js';
import {DYNAMIC_DRAW, ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, FLOAT} from '../../webgl.js';
import {DefaultAttrib, DefaultUniform} from '../../webgl/Helper.js';
import GeometryType from '../../geom/GeometryType.js';
import WebGLLayerRenderer, {getBlankTexture, pushFeatureToBuffer} from './Layer.js';
import GeoJSON from '../../format/GeoJSON.js';
import {getUid} from '../../util.js';
import WebGLLayerRenderer, {
getBlankTexture,
POINT_INSTRUCTIONS_COUNT, POINT_VERTEX_STRIDE,
writePointFeatureInstructions, writePointFeatureToBuffers
} from './Layer.js';
import ViewHint from '../../ViewHint.js';
import {createEmpty, equals} from '../../extent.js';
import {
@@ -241,14 +243,6 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
return false;
};
this.geojsonFormat_ = new GeoJSON();
/**
* @type {Object<string, import("../../format/GeoJSON").GeoJSONFeature>}
* @private
*/
this.geojsonFeatureCache_ = {};
this.previousExtent_ = createEmpty();
/**
@@ -272,6 +266,12 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
* @private
*/
this.invertRenderTransform_ = createTransform();
/**
* @type {Float32Array}
* @private
*/
this.renderInstructions_ = new Float32Array(0);
}
/**
@@ -306,8 +306,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
const vectorSource = vectorLayer.getSource();
const viewState = frameState.viewState;
// TODO: get this from somewhere...
const stride = 12;
const stride = POINT_VERTEX_STRIDE;
// the source has changed: clear the feature cache & reload features
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 vectorSource = vectorLayer.getSource();
this.verticesBuffer_.getArray().length = 0;
this.indicesBuffer_.getArray().length = 0;
// saves the projection transform for the current frame state
this.helper_.makeProjectionTransform(frameState, this.renderTransform_);
makeInverseTransform(this.invertRenderTransform_, this.renderTransform_);
const projectionTransform = createTransform();
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
const features = vectorSource.getFeatures();
let feature;
const tmpCoords = [];
let elementIndex = 0;
for (let i = 0; i < features.length; i++) {
feature = features[i];
if (!feature.getGeometry() || feature.getGeometry().getType() !== GeometryType.POINT) {
continue;
}
let geojsonFeature = this.geojsonFeatureCache_[getUid(feature)];
if (!geojsonFeature) {
geojsonFeature = this.geojsonFormat_.writeFeatureObject(feature);
this.geojsonFeatureCache_[getUid(feature)] = geojsonFeature;
}
tmpCoords[0] = this.coordCallback_(feature, 0);
tmpCoords[1] = this.coordCallback_(feature, 1);
applyTransform(projectionTransform, tmpCoords);
geojsonFeature.geometry.coordinates[0] = this.coordCallback_(feature, 0);
geojsonFeature.geometry.coordinates[1] = this.coordCallback_(feature, 1);
applyTransform(this.renderTransform_, geojsonFeature.geometry.coordinates);
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;
pushFeatureToBuffer(this.verticesBuffer_, this.indicesBuffer_, geojsonFeature);
elementIndex = writePointFeatureInstructions(
this.renderInstructions_,
elementIndex,
tmpCoords[0],
tmpCoords[1],
this.texCoordCallback_(feature, 0),
this.texCoordCallback_(feature, 1),
this.texCoordCallback_(feature, 2),
this.texCoordCallback_(feature, 3),
this.sizeCallback_(feature),
this.opacityCallback_(feature),
this.rotateWithViewCallback_(feature),
this.colorCallback_(feature, this.colorArray_)
);
}
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(ELEMENT_ARRAY_BUFFER, this.indicesBuffer_);
this.renderTransform_ = projectionTransform;
makeInverseTransform(this.invertRenderTransform_, this.renderTransform_);
}
}