Skip to content

Commit

Permalink
Merge pull request #1022 from OpenGeoscience/webgl-point-improvements
Browse files Browse the repository at this point in the history
Speed up webgl point updates.
  • Loading branch information
manthey authored Sep 26, 2019
2 parents 17c3cee + 3d6472f commit 9a3f7f8
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 70 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

### Improvements
- Points with small radii or thin strokes are rendered better (#1021)
- When only updating point styles, don't recompute geometry transforms (#1022)

### Changes
- Switched the default tile server to Stamen Design's toner-lite. (#1020)
Expand Down
94 changes: 57 additions & 37 deletions src/webgl/pointFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,11 @@ var webgl_pointFeature = function (arg) {

/**
* Create and style the data needed to render the points.
*
* @param {boolean} onlyStyle if true, use the existing geoemtry and just
* recalculate the style.
*/
function createGLPoints() {
function createGLPoints(onlyStyle) {
// unit and associated data is not used when drawing sprite
var i, j, numPts = m_this.data().length,
unit = pointPolygon(0, 0, 1, 1),
Expand Down Expand Up @@ -158,31 +161,38 @@ var webgl_pointFeature = function (arg) {
fillOpacityFunc = m_this.style.get('fillOpacity');
fillColorFunc = m_this.style.get('fillColor');

/* It is more efficient to do a transform on a single array rather than on
* an array of arrays or an array of objects. */
for (i = i3 = 0; i < numPts; i += 1, i3 += 3) {
posVal = posFunc(data[i], i);
position[i3] = posVal.x;
position[i3 + 1] = posVal.y;
// ignore the z values until we support them
position[i3 + 2] = 0; // posVal.z || 0;
}
position = transform.transformCoordinates(
m_this.gcs(), m_this.layer().map().gcs(), position, 3);
m_origin = new Float32Array(m_this.style.get('origin')(position));
if (m_origin[0] || m_origin[1] || m_origin[2]) {
if (!onlyStyle) {
/* It is more efficient to do a transform on a single array rather than on
* an array of arrays or an array of objects. */
for (i = i3 = 0; i < numPts; i += 1, i3 += 3) {
position[i3] -= m_origin[0];
position[i3 + 1] -= m_origin[1];
position[i3 + 2] -= m_origin[2];
posVal = posFunc(data[i], i);
position[i3] = posVal.x;
position[i3 + 1] = posVal.y;
// ignore the z values until we support them
position[i3 + 2] = 0; // posVal.z || 0;
}
}
m_modelViewUniform.setOrigin(m_origin);
position = transform.transformCoordinates(
m_this.gcs(), m_this.layer().map().gcs(), position, 3);
m_origin = new Float32Array(m_this.style.get('origin')(position));
if (m_origin[0] || m_origin[1] || m_origin[2]) {
for (i = i3 = 0; i < numPts; i += 1, i3 += 3) {
position[i3] -= m_origin[0];
position[i3 + 1] -= m_origin[1];
position[i3 + 2] -= m_origin[2];
}
}
m_modelViewUniform.setOrigin(m_origin);

posBuf = util.getGeomBuffer(geom, 'pos', vpf * numPts * 3);
posBuf = util.getGeomBuffer(geom, 'pos', vpf * numPts * 3);

if (m_primitiveShape !== 'sprite') {
unitBuf = util.getGeomBuffer(geom, 'unit', vpf * numPts * 2);
if (m_primitiveShape !== 'sprite') {
unitBuf = util.getGeomBuffer(geom, 'unit', vpf * numPts * 2);
}
indices = geom.primitive(0).indices();
if (!(indices instanceof Uint16Array) || indices.length !== vpf * numPts) {
indices = new Uint16Array(vpf * numPts);
geom.primitive(0).setIndices(indices);
}
}

radius = util.getGeomBuffer(geom, 'radius', vpf * numPts);
Expand All @@ -193,17 +203,14 @@ var webgl_pointFeature = function (arg) {
fill = util.getGeomBuffer(geom, 'fill', vpf * numPts);
fillOpacity = util.getGeomBuffer(geom, 'fillOpacity', vpf * numPts);
fillColor = util.getGeomBuffer(geom, 'fillColor', vpf * numPts * 3);
indices = geom.primitive(0).indices();
if (!(indices instanceof Uint16Array) || indices.length !== vpf * numPts) {
indices = new Uint16Array(vpf * numPts);
geom.primitive(0).setIndices(indices);
}

for (i = ivpf = ivpf3 = iunit = i3 = 0; i < numPts; i += 1, i3 += 3) {
item = data[i];
if (m_primitiveShape !== 'sprite') {
for (j = 0; j < unit.length; j += 1, iunit += 1) {
unitBuf[iunit] = unit[j];
if (!onlyStyle) {
if (m_primitiveShape !== 'sprite') {
for (j = 0; j < unit.length; j += 1, iunit += 1) {
unitBuf[iunit] = unit[j];
}
}
}
/* We can ignore the indicies (they will all be zero) */
Expand All @@ -216,9 +223,11 @@ var webgl_pointFeature = function (arg) {
fillOpacityVal = fillOpacityFunc(item, i);
fillColorVal = fillColorFunc(item, i);
for (j = 0; j < vpf; j += 1, ivpf += 1, ivpf3 += 3) {
posBuf[ivpf3] = position[i3];
posBuf[ivpf3 + 1] = position[i3 + 1];
posBuf[ivpf3 + 2] = position[i3 + 2];
if (!onlyStyle) {
posBuf[ivpf3] = position[i3];
posBuf[ivpf3 + 1] = position[i3 + 1];
posBuf[ivpf3 + 2] = position[i3 + 2];
}
radius[ivpf] = radiusVal;
stroke[ivpf] = strokeVal;
strokeWidth[ivpf] = strokeWidthVal;
Expand All @@ -234,9 +243,20 @@ var webgl_pointFeature = function (arg) {
}
}

geom.boundsDirty(true);
m_mapper.modified();
m_mapper.boundsDirtyTimestamp().modified();
if (!onlyStyle) {
geom.boundsDirty(true);
m_mapper.modified();
m_mapper.boundsDirtyTimestamp().modified();
} else {
m_mapper.updateSourceBuffer('radius');
m_mapper.updateSourceBuffer('stroke');
m_mapper.updateSourceBuffer('strokeWidth');
m_mapper.updateSourceBuffer('strokeColor');
m_mapper.updateSourceBuffer('strokeOpacity');
m_mapper.updateSourceBuffer('fill');
m_mapper.updateSourceBuffer('fillColor');
m_mapper.updateSourceBuffer('fillOpacity');
}
}

/**
Expand Down Expand Up @@ -468,7 +488,7 @@ var webgl_pointFeature = function (arg) {
* @returns {this}
*/
this._build = function () {
createGLPoints();
createGLPoints(m_this.dataTime().timestamp() < m_this.buildTime().timestamp());
if (!m_this.renderer().contextRenderer().hasActor(m_actor)) {
m_this.renderer().contextRenderer().addActor(m_actor);
}
Expand Down
8 changes: 3 additions & 5 deletions src/webgl/pointFeaturePoly.frag
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,27 @@ varying vec4 fillColorVar;
varying vec4 strokeColorVar;
varying float radiusVar;
varying float strokeWidthVar;
varying float fillVar;
varying float strokeVar;
varying vec3 unitVar; // distinct for square/triangle

void main () {
vec4 strokeColor, fillColor;
float endStep;
// No stroke or fill implies nothing to draw
if (fillVar == 0.0 && strokeVar == 0.0)
if (fillColorVar.a == 0.0 && strokeColorVar.a == 0.0)
discard;
float rad = length(unitVar.xy); // distinct for square/triangle
if (rad > 1.0)
discard;
// If there is no stroke, the fill region should transition to nothing
if (strokeVar == 0.0) {
if (strokeColorVar.a == 0.0) {
strokeColor = vec4(fillColorVar.rgb, 0.0);
endStep = 1.0;
} else {
strokeColor = strokeColorVar;
endStep = radiusVar / (radiusVar + strokeWidthVar);
}
// Likewise, if there is no fill, the stroke should transition to nothing
if (fillVar == 0.0)
if (fillColorVar.a == 0.0)
fillColor = vec4(strokeColor.rgb, 0.0);
else
fillColor = fillColorVar;
Expand Down
18 changes: 6 additions & 12 deletions src/webgl/pointFeaturePoly.vert
Original file line number Diff line number Diff line change
Expand Up @@ -20,35 +20,29 @@ varying vec4 fillColorVar;
varying vec4 strokeColorVar;
varying float radiusVar;
varying float strokeWidthVar;
varying float fillVar;
varying float strokeVar;
attribute vec2 unit; // for non-sprite
varying vec3 unitVar; // for non-sprite

void main(void)
{
strokeWidthVar = strokeWidth;
fillColorVar = vec4(fillColor, fillOpacity);
strokeColorVar = vec4(strokeColor, strokeOpacity);
// No stroke or fill implies nothing to draw
if (stroke < 1.0 || strokeWidth <= 0.0 || strokeOpacity <= 0.0) {
strokeVar = 0.0;
strokeColorVar.a = 0.0;
strokeWidthVar = 0.0;
}
else
strokeVar = 1.0;
if (fill < 1.0 || radius <= 0.0 || fillOpacity <= 0.0)
fillVar = 0.0;
else
fillVar = 1.0;
fillColorVar.a = 0.0;
/* If the point has no visible pixels, skip doing computations on it. */
if (fillVar == 0.0 && strokeVar == 0.0) {
if (fillColorVar.a == 0.0 && strokeColorVar.a == 0.0) {
gl_Position = vec4(2, 2, 0, 1);
return;
}
fillColorVar = vec4 (fillColor, fillOpacity);
strokeColorVar = vec4 (strokeColor, strokeOpacity);
radiusVar = radius;
// for non-sprite
unitVar = vec3 (unit, 1.0);
unitVar = vec3(unit, 1.0);
vec4 p = (projectionMatrix * modelViewMatrix * vec4(pos, 1.0)).xyzw;
if (p.w != 0.0) {
p = p / p.w;
Expand Down
8 changes: 3 additions & 5 deletions src/webgl/pointFeatureSprite.frag
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,27 @@ varying vec4 fillColorVar;
varying vec4 strokeColorVar;
varying float radiusVar;
varying float strokeWidthVar;
varying float fillVar;
varying float strokeVar;
// the square/triangle shade defines unitVar

void main () {
vec4 strokeColor, fillColor;
float endStep;
// No stroke or fill implies nothing to draw
if (fillVar == 0.0 && strokeVar == 0.0)
if (fillColorVar.a == 0.0 && strokeColorVar.a == 0.0)
discard;
float rad = 2.0 * length(gl_PointCoord - vec2(0.5)); // distinct for sprite
if (rad > 1.0)
discard;
// If there is no stroke, the fill region should transition to nothing
if (strokeVar == 0.0) {
if (strokeColorVar.a == 0.0) {
strokeColor = vec4(fillColorVar.rgb, 0.0);
endStep = 1.0;
} else {
strokeColor = strokeColorVar;
endStep = radiusVar / (radiusVar + strokeWidthVar);
}
// Likewise, if there is no fill, the stroke should transition to nothing
if (fillVar == 0.0)
if (fillColorVar.a == 0.0)
fillColor = vec4(strokeColor.rgb, 0.0);
else
fillColor = fillColorVar;
Expand Down
16 changes: 5 additions & 11 deletions src/webgl/pointFeatureSprite.vert
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,25 @@ varying vec4 fillColorVar;
varying vec4 strokeColorVar;
varying float radiusVar;
varying float strokeWidthVar;
varying float fillVar;
varying float strokeVar;
// non-sprite has ither definitions.

void main(void)
{
strokeWidthVar = strokeWidth;
fillColorVar = vec4(fillColor, fillOpacity);
strokeColorVar = vec4(strokeColor, strokeOpacity);
// No stroke or fill implies nothing to draw
if (stroke < 1.0 || strokeWidth <= 0.0 || strokeOpacity <= 0.0) {
strokeVar = 0.0;
strokeColorVar.a = 0.0;
strokeWidthVar = 0.0;
}
else
strokeVar = 1.0;
if (fill < 1.0 || radius <= 0.0 || fillOpacity <= 0.0)
fillVar = 0.0;
else
fillVar = 1.0;
fillColorVar.a = 0.0;
/* If the point has no visible pixels, skip doing computations on it. */
if (fillVar == 0.0 && strokeVar == 0.0) {
if (fillColorVar.a == 0.0 && strokeColorVar.a == 0.0) {
gl_Position = vec4(2, 2, 0, 1);
return;
}
fillColorVar = vec4 (fillColor, fillOpacity);
strokeColorVar = vec4 (strokeColor, strokeOpacity);
radiusVar = radius;
// for sprite
gl_Position = (projectionMatrix * modelViewMatrix * vec4(pos, 1.0)).xyzw;
Expand Down
8 changes: 8 additions & 0 deletions tests/cases/pointFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,14 @@ describe('geo.pointFeature', function () {
point.updateStyleFromArray('radius', array1, true);
expect(count).toBe(2);
});
it('modify and refresh', function () {
glCounts = $.extend({}, vgl.mockCounts());
point2.modified().draw();
expect(count).toBe(2);
});
waitForIt('next render gl E', function () {
return vgl.mockCounts().bufferSubData >= (glCounts.bufferSubData || 0) + 8;
});
it('_exit', function () {
expect(point.actors().length).toBe(1);
layer.deleteFeature(point);
Expand Down

0 comments on commit 9a3f7f8

Please sign in to comment.