Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Bucket class #2140

Merged
merged 13 commits into from
Feb 26, 2016
155 changes: 117 additions & 38 deletions js/data/bucket.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
'use strict';

var featureFilter = require('feature-filter');

var ElementGroups = require('./element_groups');
var Buffer = require('./buffer');
var StyleLayer = require('../style/style_layer');
var util = require('../util/util');

module.exports = Bucket;

Expand All @@ -26,9 +25,26 @@ Bucket.create = function(options) {

Bucket.AttributeType = Buffer.AttributeType;


/**
* The `Bucket` class builds a set of `Buffer`s for a set of vector tile
* features.
* The maximum extent of a feature that can be safely stored in the buffer.
* In practice, all features are converted to this extent before being added.
*
* Positions are stored as signed 16bit integers.
* One bit is lost for signedness to support featuers extending past the left edge of the tile.
* One bit is lost because the line vertex buffer packs 1 bit of other data into the int.
* One bit is lost to support features extending past the extent on the right edge of the tile.
* This leaves us with 2^13 = 8192
*
* @property {number}
* @private
* @readonly
*/
Bucket.EXTENT = 8192;

/**
* The `Bucket` class is the single point of knowledge about turning vector
* tiles into WebGL buffers.
*
* `Bucket` is an abstract class. A subclass exists for each Mapbox GL
* style spec layer type. Because `Bucket` is an abstract class,
Expand All @@ -50,9 +66,7 @@ Bucket.AttributeType = Buffer.AttributeType;
function Bucket(options) {
this.zoom = options.zoom;
this.overscaling = options.overscaling;

this.layer = StyleLayer.create(options.layer);
this.layer.recalculate(this.zoom, { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 });
this.layer = options.layer;

this.layers = [this.layer.id];
this.type = this.layer.type;
Expand All @@ -62,84 +76,111 @@ function Bucket(options) {
this.interactive = this.layer.interactive;
this.minZoom = this.layer.minzoom;
this.maxZoom = this.layer.maxzoom;
this.filter = featureFilter(this.layer.filter);

this.resetBuffers(options.buffers);

for (var shaderName in this.shaderInterfaces) {
var shaderInterface = this.shaderInterfaces[shaderName];
this[this.getAddMethodName(shaderName, 'vertex')] = createVertexAddMethod(
shaderName,
shaderInterface,
this.getBufferName(shaderName, 'vertex')
);
if (options.elementGroups) {
this.elementGroups = options.elementGroups;
this.buffers = util.mapObject(options.buffers, function(options) {
return new Buffer(options);
});
}
}

/**
* Build the buffers! Features are set directly to the `features` property.
* @private
*/
Bucket.prototype.addFeatures = function() {
Bucket.prototype.populateBuffers = function() {
this.createStyleLayer();
this.createBuffers();

for (var i = 0; i < this.features.length; i++) {
this.addFeature(this.features[i]);
}

this.trimBuffers();
};

/**
* Check if there is enough space available in the current element group for
* `vertexLength` vertices. If not, append a new elementGroup. Should be called
* by `addFeatures` and its callees.
* by `populateBuffers` and its callees.
* @private
* @param {string} shaderName the name of the shader associated with the buffer that will receive the vertices
* @param {number} vertexLength The number of vertices that will be inserted to the buffer.
*/
Bucket.prototype.makeRoomFor = function(shaderName, vertexLength) {
return this.elementGroups[shaderName].makeRoomFor(vertexLength);
Bucket.prototype.makeRoomFor = function(shaderName, numVertices) {
var groups = this.elementGroups[shaderName];
var currentGroup = groups.length && groups[groups.length - 1];

if (!currentGroup || currentGroup.vertexLength + numVertices > 65535) {
var vertexBuffer = this.buffers[this.getBufferName(shaderName, 'vertex')];
var elementBuffer = this.buffers[this.getBufferName(shaderName, 'element')];
var secondElementBuffer = this.buffers[this.getBufferName(shaderName, 'secondElement')];

currentGroup = new ElementGroup(
vertexBuffer.length,
elementBuffer && elementBuffer.length,
secondElementBuffer && secondElementBuffer.length
);
groups.push(currentGroup);
}

return currentGroup;
};

/**
* Start using a new shared `buffers` object and recreate instances of `Buffer`
* as necessary.
* @private
* @param {Object.<string, Buffer>} buffers
*/
Bucket.prototype.resetBuffers = function(buffers) {
this.buffers = buffers;
this.elementGroups = {};
Bucket.prototype.createBuffers = function() {
var elementGroups = this.elementGroups = {};
var buffers = this.buffers = {};

for (var shaderName in this.shaderInterfaces) {
var shaderInterface = this.shaderInterfaces[shaderName];

var vertexBufferName = this.getBufferName(shaderName, 'vertex');
if (shaderInterface.vertexBuffer && !buffers[vertexBufferName]) {
if (shaderInterface.vertexBuffer) {
var vertexBufferName = this.getBufferName(shaderName, 'vertex');
var vertexAddMethodName = this.getAddMethodName(shaderName, 'vertex');

buffers[vertexBufferName] = new Buffer({
type: Buffer.BufferType.VERTEX,
attributes: shaderInterface.attributes
});

this[vertexAddMethodName] = this[vertexAddMethodName] || createVertexAddMethod(
shaderName,
shaderInterface,
this.getBufferName(shaderName, 'vertex')
);
}

if (shaderInterface.elementBuffer) {
var elementBufferName = this.getBufferName(shaderName, 'element');
if (!buffers[elementBufferName]) {
buffers[elementBufferName] = createElementBuffer(shaderInterface.elementBufferComponents);
}
buffers[elementBufferName] = createElementBuffer(shaderInterface.elementBufferComponents);
this[this.getAddMethodName(shaderName, 'element')] = createElementAddMethod(this.buffers[elementBufferName]);
}

if (shaderInterface.secondElementBuffer) {
var secondElementBufferName = this.getBufferName(shaderName, 'secondElement');
if (!buffers[secondElementBufferName]) {
buffers[secondElementBufferName] = createElementBuffer(shaderInterface.secondElementBufferComponents);
}
buffers[secondElementBufferName] = createElementBuffer(shaderInterface.secondElementBufferComponents);
this[this.getAddMethodName(shaderName, 'secondElement')] = createElementAddMethod(this.buffers[secondElementBufferName]);
}

this.elementGroups[shaderName] = new ElementGroups(
buffers[this.getBufferName(shaderName, 'vertex')],
buffers[this.getBufferName(shaderName, 'element')],
buffers[this.getBufferName(shaderName, 'secondElement')]
);
elementGroups[shaderName] = [];
}
};

Bucket.prototype.destroy = function(gl) {
for (var k in this.buffers) {
this.buffers[k].destroy(gl);
}
};

Bucket.prototype.trimBuffers = function() {
for (var bufferName in this.buffers) {
this.buffers[bufferName].trim();
}
};

Expand All @@ -163,6 +204,34 @@ Bucket.prototype.getBufferName = function(shaderName, type) {
return shaderName + capitalize(type);
};

Bucket.prototype.serialize = function() {
return {
layer: {
id: this.layer.id,
type: this.layer.type
},
zoom: this.zoom,
elementGroups: this.elementGroups,
buffers: util.mapObject(this.buffers, function(buffer) {
return buffer.serialize();
})
};
};

Bucket.prototype.createStyleLayer = function() {
if (!(this.layer instanceof StyleLayer)) {
this.layer = StyleLayer.create(this.layer);
this.layer.recalculate(this.zoom, { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 });
}
};

Bucket.prototype.createFilter = function() {
if (!this.filter) {
this.filter = featureFilter(this.layer.filter);
}
};


var createVertexAddMethodCache = {};
function createVertexAddMethod(shaderName, shaderInterface, bufferName) {
var pushArgs = [];
Expand Down Expand Up @@ -199,3 +268,13 @@ function createElementBuffer(components) {
function capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}

function ElementGroup(vertexStartIndex, elementStartIndex, secondElementStartIndex) {
// the offset into the vertex buffer of the first vertex in this group
this.vertexStartIndex = vertexStartIndex;
this.elementStartIndex = elementStartIndex;
this.secondElementStartIndex = secondElementStartIndex;
this.elementLength = 0;
this.vertexLength = 0;
this.secondElementLength = 0;
}
2 changes: 1 addition & 1 deletion js/data/bucket/circle_bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
var Bucket = require('../bucket');
var util = require('../../util/util');
var loadGeometry = require('../load_geometry');
var EXTENT = require('../buffer').EXTENT;
var EXTENT = Bucket.EXTENT;

module.exports = CircleBucket;

Expand Down
6 changes: 3 additions & 3 deletions js/data/bucket/line_bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
var Bucket = require('../bucket');
var util = require('../../util/util');
var loadGeometry = require('../load_geometry');
var EXTENT = require('../buffer').EXTENT;
var EXTENT = Bucket.EXTENT;

// NOTE ON EXTRUDE SCALE:
// scale the extrusion vector so that the normal length is this value.
Expand Down Expand Up @@ -366,7 +366,7 @@ LineBucket.prototype.addLine = function(vertices, join, cap, miterLimit, roundLi
LineBucket.prototype.addCurrentVertex = function(currentVertex, distance, normal, endLeft, endRight, round) {
var tx = round ? 1 : 0;
var extrude;
var group = this.elementGroups.line.current;
var group = this.elementGroups.line[this.elementGroups.line.length - 1];
group.vertexLength += 2;

extrude = normal.clone();
Expand Down Expand Up @@ -412,7 +412,7 @@ LineBucket.prototype.addCurrentVertex = function(currentVertex, distance, normal
LineBucket.prototype.addPieSliceVertex = function(currentVertex, distance, extrude, lineTurnsLeft) {
var ty = lineTurnsLeft ? 1 : 0;
extrude = extrude.mult(lineTurnsLeft ? -1 : 1);
var group = this.elementGroups.line.current;
var group = this.elementGroups.line[this.elementGroups.line.length - 1];

this.e3 = this.addLineVertex(currentVertex, extrude, 0, ty, 0, distance) - group.vertexStartIndex;
group.vertexLength++;
Expand Down
Loading