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

Arrays and buffers #2865

Merged
merged 3 commits into from
Jul 18, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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