Skip to content

Commit

Permalink
Arrays and buffers (#2865)
Browse files Browse the repository at this point in the history
* Extract ArrayGroup

* Extract BufferGroup

* Remove Buffer dependency from Bucket

The constants are inlined into what are effective higher-abstracted constants.
  • Loading branch information
jfirebaugh authored and lucaswoj committed Jul 18, 2016
1 parent 2c5db34 commit 0c0127d
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 143 deletions.
94 changes: 94 additions & 0 deletions js/data/array_group.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
'use strict';

var util = require('../util/util');

module.exports = ArrayGroup;

/**
* A class that manages vertex and element arrays for a range of features. It handles initialization,
* serialization for transfer to the main thread, and certain intervening mutations.
*
* Array elements are broken into array groups based on inherent limits of WebGL. Within a group is:
*
* * A "layout" vertex array, with fixed layout, containing values calculated from layout properties.
* * Zero, one, or two element arrays, with fixed layout, typically for eventual use in
* `gl.drawElements(gl.TRIANGLES, ...)`.
* * Zero or more "paint" vertex arrays keyed by layer ID, each with a dynamic layout which depends
* on which paint properties of that layer use data-driven-functions (property functions or
* property-and-zoom functions). Values are calculated by evaluating those functions.
*
* @private
*/
function ArrayGroup(arrayTypes) {
var LayoutVertexArrayType = arrayTypes.layoutVertexArrayType;
this.layoutVertexArray = new LayoutVertexArrayType();

var ElementArrayType = arrayTypes.elementArrayType;
if (ElementArrayType) this.elementArray = new ElementArrayType();

var ElementArrayType2 = arrayTypes.elementArrayType2;
if (ElementArrayType2) this.elementArray2 = new ElementArrayType2();

this.paintVertexArrays = util.mapObject(arrayTypes.paintVertexArrayTypes, function (PaintVertexArrayType) {
return new PaintVertexArrayType();
});
}

/**
* The maximum size of a vertex array. This limit is imposed by WebGL's 16 bit
* addressing of vertex buffers.
* @private
* @readonly
*/
ArrayGroup.MAX_VERTEX_ARRAY_LENGTH = Math.pow(2, 16) - 1;

ArrayGroup.prototype.hasCapacityFor = function(numVertices) {
return this.layoutVertexArray.length + numVertices <= ArrayGroup.MAX_VERTEX_ARRAY_LENGTH;
};

ArrayGroup.prototype.isEmpty = function() {
return this.layoutVertexArray.length === 0;
};

ArrayGroup.prototype.trim = function() {
this.layoutVertexArray.trim();

if (this.elementArray) {
this.elementArray.trim();
}

if (this.elementArray2) {
this.elementArray2.trim();
}

for (var layerName in this.paintVertexArrays) {
this.paintVertexArrays[layerName].trim();
}
};

ArrayGroup.prototype.serialize = function() {
return {
layoutVertexArray: this.layoutVertexArray.serialize(),
elementArray: this.elementArray && this.elementArray.serialize(),
elementArray2: this.elementArray2 && this.elementArray2.serialize(),
paintVertexArrays: util.mapObject(this.paintVertexArrays, function(array) {
return array.serialize();
})
};
};

ArrayGroup.prototype.getTransferables = function(transferables) {
transferables.push(this.layoutVertexArray.arrayBuffer);

if (this.elementArray) {
transferables.push(this.elementArray.arrayBuffer);
}

if (this.elementArray2) {
transferables.push(this.elementArray2.arrayBuffer);
}

for (var layerName in this.paintVertexArrays) {
transferables.push(this.paintVertexArrays[layerName].arrayBuffer);
}
};
154 changes: 33 additions & 121 deletions js/data/bucket.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use strict';

var featureFilter = require('feature-filter');
var Buffer = require('./buffer');
var ArrayGroup = require('./array_group');
var BufferGroup = require('./buffer_group');
var util = require('../util/util');
var StructArrayType = require('../util/struct_array');
var VertexArrayObject = require('../render/vertex_array_object');
var assert = require('assert');

module.exports = Bucket;
Expand Down Expand Up @@ -41,14 +41,6 @@ Bucket.create = function(options) {
*/
Bucket.EXTENT = 8192;

/**
* The maximum size of a vertex array. This limit is imposed by WebGL's 16 bit
* addressing of vertex buffers.
* @private
* @readonly
*/
Bucket.MAX_VERTEX_ARRAY_LENGTH = Math.pow(2, 16) - 1;

/**
* The `Bucket` class is the single point of knowledge about turning vector
* tiles into WebGL buffers.
Expand Down Expand Up @@ -85,39 +77,17 @@ function Bucket(options) {
this.paintAttributes = createPaintAttributes(this);

if (options.arrays) {
var childLayers = this.childLayers;
var programInterfaces = this.programInterfaces;
this.bufferGroups = util.mapObject(options.arrays, function(programArrayGroups, programName) {
var programInterface = programInterfaces[programName];
var paintVertexArrayTypes = options.paintVertexArrayTypes[programName];
return programArrayGroups.map(function(arrayGroup) {
var group = {
layoutVertexBuffer: new Buffer(arrayGroup.layoutVertexArray,
programInterface.layoutVertexArrayType.serialize(), Buffer.BufferType.VERTEX),
paintVertexBuffers: util.mapObject(arrayGroup.paintVertexArrays, function(array, name) {
return new Buffer(array, paintVertexArrayTypes[name], Buffer.BufferType.VERTEX);
}),
vaos: {}
};

if (arrayGroup.elementArray) {
group.elementBuffer = new Buffer(arrayGroup.elementArray,
programInterface.elementArrayType.serialize(), Buffer.BufferType.ELEMENT);
}

if (arrayGroup.elementArray2) {
group.elementBuffer2 = new Buffer(arrayGroup.elementArray2,
programInterface.elementArrayType2.serialize(), Buffer.BufferType.ELEMENT);
group.secondVaos = {};
}

for (var l = 0; l < childLayers.length; l++) {
var layerName = childLayers[l].id;
group.vaos[layerName] = new VertexArrayObject();
if (arrayGroup.elementArray2) group.secondVaos[layerName] = new VertexArrayObject();
}

return group;
return new BufferGroup(arrayGroup, {
layoutVertexArrayType: programInterface.layoutVertexArrayType.serialize(),
elementArrayType: programInterface.elementArrayType && programInterface.elementArrayType.serialize(),
elementArrayType2: programInterface.elementArrayType2 && programInterface.elementArrayType2.serialize(),
paintVertexArrayTypes: paintVertexArrayTypes
});
});
});
}
Expand All @@ -144,15 +114,6 @@ Bucket.prototype.populateArrays = function() {
* by `populateArrays` and its callees.
*
* Array groups are added to this.arrayGroups[programName].
* An individual array group looks like:
* {
* index: number,
* layoutVertexArray: VertexArrayType,
* ?elementArray: ElementArrayType,
* ?elementArray2: ElementArrayType2,
* paintVertexArrays: { [layerName]: PaintArrayType, ... }
* }
*
*
* @private
* @param {string} programName the name of the program associated with the buffer that will receive the vertices
Expand All @@ -163,29 +124,15 @@ Bucket.prototype.prepareArrayGroup = function(programName, numVertices) {
var groups = this.arrayGroups[programName];
var currentGroup = groups.length && groups[groups.length - 1];

if (!currentGroup || currentGroup.layoutVertexArray.length + numVertices > Bucket.MAX_VERTEX_ARRAY_LENGTH) {

var programInterface = this.programInterfaces[programName];
var LayoutVertexArrayType = programInterface.layoutVertexArrayType;

currentGroup = {
index: groups.length,
layoutVertexArray: new LayoutVertexArrayType(),
paintVertexArrays: {}
};

var ElementArrayType = programInterface.elementArrayType;
if (ElementArrayType) currentGroup.elementArray = new ElementArrayType();

var ElementArrayType2 = programInterface.elementArrayType2;
if (ElementArrayType2) currentGroup.elementArray2 = new ElementArrayType2();
if (!currentGroup || !currentGroup.hasCapacityFor(numVertices)) {
currentGroup = new ArrayGroup({
layoutVertexArrayType: this.programInterfaces[programName].layoutVertexArrayType,
elementArrayType: this.programInterfaces[programName].elementArrayType,
elementArrayType2: this.programInterfaces[programName].elementArrayType2,
paintVertexArrayTypes: this.paintVertexArrayTypes[programName]
});

var paintVertexArrayTypes = this.paintVertexArrayTypes[programName];
for (var i = 0; i < this.childLayers.length; i++) {
var layerName = this.childLayers[i].id;
var PaintVertexArrayType = paintVertexArrayTypes[layerName];
currentGroup.paintVertexArrays[layerName] = new PaintVertexArrayType();
}
currentGroup.index = groups.length;

groups.push(currentGroup);
}
Expand Down Expand Up @@ -221,43 +168,16 @@ Bucket.prototype.destroy = function(gl) {
for (var programName in this.bufferGroups) {
var programBufferGroups = this.bufferGroups[programName];
for (var i = 0; i < programBufferGroups.length; i++) {
var bufferGroup = programBufferGroups[i];
bufferGroup.layoutVertexBuffer.destroy(gl);
if (bufferGroup.elementBuffer) {
bufferGroup.elementBuffer.destroy(gl);
}
if (bufferGroup.elementBuffer2) {
bufferGroup.elementBuffer2.destroy(gl);
}
for (var n in bufferGroup.paintVertexBuffers) {
bufferGroup.paintVertexBuffers[n].destroy(gl);
}
for (var j in bufferGroup.vaos) {
bufferGroup.vaos[j].destroy(gl);
}
for (var k in bufferGroup.secondVaos) {
bufferGroup.secondVaos[k].destroy(gl);
}
programBufferGroups[i].destroy(gl);
}
}

};

Bucket.prototype.trimArrays = function() {
for (var programName in this.arrayGroups) {
var arrayGroups = this.arrayGroups[programName];
for (var i = 0; i < arrayGroups.length; i++) {
var arrayGroup = arrayGroups[i];
arrayGroup.layoutVertexArray.trim();
if (arrayGroup.elementArray) {
arrayGroup.elementArray.trim();
}
if (arrayGroup.elementArray2) {
arrayGroup.elementArray2.trim();
}
for (var paintArray in arrayGroup.paintVertexArrays) {
arrayGroup.paintVertexArrays[paintArray].trim();
}
arrayGroups[i].trim();
}
}
};
Expand All @@ -266,8 +186,7 @@ Bucket.prototype.isEmpty = function() {
for (var programName in this.arrayGroups) {
var arrayGroups = this.arrayGroups[programName];
for (var i = 0; i < arrayGroups.length; i++) {
var arrayGroup = arrayGroups[i];
if (arrayGroup.layoutVertexArray.length > 0) {
if (!arrayGroups[i].isEmpty()) {
return false;
}
}
Expand All @@ -279,17 +198,7 @@ Bucket.prototype.getTransferables = function(transferables) {
for (var programName in this.arrayGroups) {
var arrayGroups = this.arrayGroups[programName];
for (var i = 0; i < arrayGroups.length; i++) {
var arrayGroup = arrayGroups[i];
transferables.push(arrayGroup.layoutVertexArray.arrayBuffer);
if (arrayGroup.elementArray) {
transferables.push(arrayGroup.elementArray.arrayBuffer);
}
if (arrayGroup.elementArray2) {
transferables.push(arrayGroup.elementArray2.arrayBuffer);
}
for (var paintArray in arrayGroup.paintVertexArrays) {
transferables.push(arrayGroup.paintVertexArrays[paintArray].arrayBuffer);
}
arrayGroups[i].getTransferables(transferables);
}
}
};
Expand All @@ -309,14 +218,7 @@ Bucket.prototype.serialize = function() {
zoom: this.zoom,
arrays: util.mapObject(this.arrayGroups, function(programArrayGroups) {
return programArrayGroups.map(function(arrayGroup) {
return {
layoutVertexArray: arrayGroup.layoutVertexArray.serialize(),
elementArray: arrayGroup.elementArray && arrayGroup.elementArray.serialize(),
elementArray2: arrayGroup.elementArray2 && arrayGroup.elementArray2.serialize(),
paintVertexArrays: util.mapObject(arrayGroup.paintVertexArrays, function(array) {
return array.serialize();
})
};
return arrayGroup.serialize();
});
}),
paintVertexArrayTypes: util.mapObject(this.paintVertexArrayTypes, function(arrayTypes) {
Expand Down Expand Up @@ -375,17 +277,27 @@ Bucket.prototype.populatePaintArrays = function(interfaceName, globalProperties,
}
};

/**
* A vertex array stores data for each vertex in a geometry. Elements are aligned to 4 byte
* boundaries for best performance in WebGL.
* @private
*/
Bucket.VertexArrayType = function (members) {
return new StructArrayType({
members: members,
alignment: Buffer.VERTEX_ATTRIBUTE_ALIGNMENT
alignment: 4
});
};

/**
* An element array stores Uint16 indicies of vertexes in a corresponding vertex array. With no
* arguments, it defaults to three components per element, forming triangles.
* @private
*/
Bucket.ElementArrayType = function (components) {
return new StructArrayType({
members: [{
type: Buffer.ELEMENT_ATTRIBUTE_TYPE,
type: 'Uint16',
name: 'vertices',
components: components || 3
}]
Expand Down
16 changes: 0 additions & 16 deletions js/data/buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,19 +97,3 @@ Buffer.BufferType = {
VERTEX: 'ARRAY_BUFFER',
ELEMENT: 'ELEMENT_ARRAY_BUFFER'
};

/**
* An `BufferType.ELEMENT` buffer holds indicies of a corresponding `BufferType.VERTEX` buffer.
* These indicies are stored in the `BufferType.ELEMENT` buffer as `UNSIGNED_SHORT`s.
*
* @private
* @readonly
*/
Buffer.ELEMENT_ATTRIBUTE_TYPE = 'Uint16';

/**
* WebGL performs best if vertex attribute offsets are aligned to 4 byte boundaries.
* @private
* @readonly
*/
Buffer.VERTEX_ATTRIBUTE_ALIGNMENT = 4;
Loading

0 comments on commit 0c0127d

Please sign in to comment.