-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Refactored bucket classes [do not merge] #1620
+935
−678
Closed
Changes from all commits
Commits
Show all changes
52 commits
Select commit
Hold shift + click to select a range
f29bb9d
Added Bucket2 class, migrated circle
b11f639
Use layer type uniform values
0f60afa
Removed custom push methods
efe428b
Added special push object to layer type iterator
ab28da2
Automatically handle element group lengths
f53e106
Added better api
28161eb
Converted fill
1132909
Convertex line buckets
f93438d
Fixed circle uniforms
99b4aaf
Rewrote symbol bucket
a612611
Lint cleanup, little bug cleanup
a117acb
Removed create_bucket
6252953
Renamed bucket2 to bucket
92386a2
Use objects for Buffer::push
814f139
Started refactoring buffer_builder WIP
d3586a9
Extracted BufferBuilder classes
19ef1f2
Move BufferBuilder::layers definition
0a13cbc
Minor style changes
4a161c2
fixed featuresat
645b5ed
Completely moved over to LayerType buffer layout definitions
551d767
Completely removed buffer_set
3845296
Moved buffer builder classes into data dir
d9e02c4
Use composition instead of util.extend for layer type in BufferBuilder
bf4c8b0
Removed uniform calculation in LayerType
4287447
Renamed LayerType files
479c840
Misc PR cleanup
0aefc27
Added BufferBuilder::makeRoomFor method
3795ebb
Removed stray TODOs
1aa2cd7
Removed extraneous whitespace
4adf75d
Check itemSize after setting it
4b77680
Simplified BufferBuilder._createBuffers
d7db04a
Use array instead of an object in Buffer::push
a14456d
Fixed incorrect makeRoomFor calls
afd728d
😈
4b8987d
Simplified createElementAddMethod
85e5689
Remove BufferBuilder class methods
92f87ef
Refactoring
dfdc2b6
Refactoring
55cc147
Skip _createPushMethod in main thread
128d67f
Only call _refreshViews in worker thread
0a3bba0
Improved documentation on Buffer methods
790ee8d
Minor refactoring
6c352bf
Made BufferBuilder::type set by subclasses
5519090
Removed fill layer type uniforms
ccb1620
Added basic BufferBuilder tests
2ef457f
Added documentation to BufferBuilder
c6cda4b
Remove all references to "bucket"
d27f159
PR cleanup
1cf8ae8
Revert bench changes
b5512a7
Revert bucket-stats changes
6e9bca9
Fixed typo in line_buffer_builder
3e49540
Fix reloadSymbolData error
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,255 @@ | ||
'use strict'; | ||
|
||
var featureFilter = require('feature-filter'); | ||
|
||
var StyleDeclarationSet = require('../style/style_declaration_set'); | ||
var LayoutProperties = require('../style/layout_properties'); | ||
var ElementGroups = require('./element_groups'); | ||
var Buffer = require('./buffer'); | ||
|
||
module.exports = BufferBuilder; | ||
|
||
/** | ||
* Instantiate the appropriate subclass of `BufferBuilder` for `options`. | ||
* @private | ||
* @param options See `BufferBuilder` constructor options | ||
* @returns {BufferBuilder} | ||
*/ | ||
BufferBuilder.create = function(options) { | ||
var Classes = { | ||
fill: require('./fill_buffer_builder'), | ||
line: require('./line_buffer_builder'), | ||
circle: require('./circle_buffer_builder'), | ||
symbol: require('./symbol_buffer_builder') | ||
}; | ||
return new Classes[options.layer.type](options); | ||
}; | ||
|
||
/** | ||
* The `BufferBuilder` class builds a set of `Buffer`s for a set of vector tile | ||
* features. | ||
* | ||
* `BufferBuilder` is an abstract class. A subclass exists for each Mapbox GL | ||
* style spec layer type. Because `BufferBuilder` is an abstract class, | ||
* instances should be created via the `BufferBuilder.create` method. | ||
* | ||
* For performance reasons, `BufferBuilder` creates its "add"s methods at | ||
* runtime using `new Function(...)`. | ||
* | ||
* @class BufferBuilder | ||
* @private | ||
* @param options | ||
* @param {number} options.zoom Zoom level of the buffers being built. May be | ||
* a fractional zoom level. | ||
* @param options.layer A Mapbox GL style layer object | ||
* @param {Object.<string, Buffer>} options.buffers The set of `Buffer`s being | ||
* built for this tile. This object facilitates sharing of `Buffer`s be | ||
between `BufferBuilder`s. | ||
*/ | ||
function BufferBuilder(options) { | ||
this.layer = options.layer; | ||
this.zoom = options.zoom; | ||
|
||
this.layers = [this.layer.id]; | ||
this.features = []; | ||
this.id = this.layer.id; | ||
this['source-layer'] = this.layer['source-layer']; | ||
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); | ||
|
||
this.layoutProperties = createLayoutProperties(this.layer, this.zoom); | ||
|
||
for (var shaderName in this.type.shaders) { | ||
var shader = this.type.shaders[shaderName]; | ||
|
||
var vertexName = this.getAddVertexMethodName(shaderName); | ||
var elementName = this.getAddElementMethodName(shaderName, false); | ||
var secondElementName = this.getAddElementMethodName(shaderName, true); | ||
|
||
this[vertexName] = createVertexAddMethod(shaderName, shader); | ||
this[elementName] = createElementAddMethod(shaderName, shader, false); | ||
this[secondElementName] = createElementAddMethod(shaderName, shader, true); | ||
} | ||
} | ||
|
||
/** | ||
* Build the buffers! Features are set directly to the `features` property. | ||
* @private | ||
*/ | ||
BufferBuilder.prototype.addFeatures = function() { | ||
for (var i = 0; i < this.features.length; i++) { | ||
this.addFeature(this.features[i]); | ||
} | ||
}; | ||
|
||
/** | ||
* 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. | ||
* @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. | ||
*/ | ||
BufferBuilder.prototype.makeRoomFor = function(shaderName, vertexLength) { | ||
this.elementGroups[shaderName].makeRoomFor(vertexLength); | ||
}; | ||
|
||
/** | ||
* Get the name of the generated "add vertex" method for a particular shader. | ||
* @private | ||
* @param {string} shaderName The name of the shader | ||
* @returns {string} The name of the method | ||
*/ | ||
BufferBuilder.prototype.getAddVertexMethodName = function(shaderName) { | ||
var shader = this.type.shaders[shaderName]; | ||
return 'add' + capitalize(shader.vertexBuffer); | ||
}; | ||
|
||
/** | ||
* Get the name of the generated "add element" method for a particular shader. | ||
* @private | ||
* @param {string} shaderName The name of the shader | ||
* @param {bool} isSecond If true, return the name of the method for the second element buffer. | ||
* @returns {string} The name of the method | ||
*/ | ||
BufferBuilder.prototype.getAddElementMethodName = function(shaderName, isSecond) { | ||
var shader = this.type.shaders[shaderName]; | ||
var bufferName = isSecond ? shader.secondElementBuffer : shader.elementBuffer; | ||
|
||
if (bufferName) return 'add' + capitalize(bufferName); | ||
else return null; | ||
}; | ||
|
||
/** | ||
* Start using a new shared `buffers` object and recreate instances of `Buffer` | ||
* as necessary. | ||
* @private | ||
* @param {Object.<string, Buffer>} buffers | ||
*/ | ||
BufferBuilder.prototype.resetBuffers = function(buffers) { | ||
this.buffers = buffers; | ||
|
||
for (var shaderName in this.type.shaders) { | ||
var shader = this.type.shaders[shaderName]; | ||
|
||
var vertexBufferName = shader.vertexBuffer; | ||
if (vertexBufferName && !buffers[vertexBufferName]) { | ||
buffers[vertexBufferName] = new Buffer({ | ||
type: Buffer.BufferType.VERTEX, | ||
attributes: shader.attributes | ||
}); | ||
} | ||
|
||
var elementBufferName = shader.elementBuffer; | ||
if (elementBufferName && !buffers[elementBufferName]) { | ||
buffers[elementBufferName] = createElementBuffer(shader.elementBufferComponents); | ||
} | ||
|
||
var secondElementBufferName = shader.secondElementBuffer; | ||
if (secondElementBufferName && !buffers[secondElementBufferName]) { | ||
buffers[secondElementBufferName] = createElementBuffer(shader.secondElementBufferComponents); | ||
} | ||
} | ||
|
||
this.elementGroups = createElementGroups(this.type.shaders, this.buffers); | ||
}; | ||
|
||
function createLayoutProperties(layer, zoom) { | ||
var values = new StyleDeclarationSet('layout', layer.type, layer.layout, {}).values(); | ||
var fakeZoomHistory = { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 }; | ||
|
||
var layout = {}; | ||
for (var k in values) { | ||
layout[k] = values[k].calculate(zoom, fakeZoomHistory); | ||
} | ||
|
||
if (layer.type === 'symbol') { | ||
// To reduce the number of labels that jump around when zooming we need | ||
// to use a text-size value that is the same for all zoom levels. | ||
// This calculates text-size at a high zoom level so that all tiles can | ||
// use the same value when calculating anchor positions. | ||
if (values['text-size']) { | ||
layout['text-max-size'] = values['text-size'].calculate(18, fakeZoomHistory); | ||
layout['text-size'] = values['text-size'].calculate(zoom + 1, fakeZoomHistory); | ||
} | ||
if (values['icon-size']) { | ||
layout['icon-max-size'] = values['icon-size'].calculate(18, fakeZoomHistory); | ||
layout['icon-size'] = values['icon-size'].calculate(zoom + 1, fakeZoomHistory); | ||
} | ||
} | ||
|
||
return new LayoutProperties[layer.type](layout); | ||
} | ||
|
||
function createVertexAddMethod(shaderName, shader) { | ||
if (!shader.vertexBuffer) return null; | ||
|
||
// Find max arg length of all attribute value functions | ||
var argCount = 0; | ||
for (var i = 0; i < shader.attributes.length; i++) { | ||
var attribute = shader.attributes[i]; | ||
argCount = Math.max(attribute.value.length, argCount); | ||
} | ||
|
||
var argIds = []; | ||
for (var j = 0; j < argCount; j++) { | ||
argIds.push('a' + j); | ||
} | ||
|
||
var body = ''; | ||
body += 'var attributes = this.type.shaders.' + shaderName + '.attributes;\n'; | ||
body += 'var elementGroups = this.elementGroups.' + shaderName + ';\n'; | ||
body += 'elementGroups.current.vertexLength++;\n'; | ||
body += 'return this.buffers.' + shader.vertexBuffer + '.push(\n'; | ||
|
||
for (var k = 0; k < shader.attributes.length; k++) { | ||
body += ' attributes[' + k + '].value(' + argIds.join(', ') + ')'; | ||
body += (k !== shader.attributes.length - 1) ? ',\n' : ''; | ||
} | ||
body += '\n) - elementGroups.current.vertexStartIndex;'; | ||
|
||
return new Function(argIds, body); | ||
} | ||
|
||
function createElementAddMethod(shaderName, shader, isSecond) { | ||
var bufferName = isSecond ? shader.secondElementBuffer : shader.elementBuffer; | ||
if (!bufferName) return null; | ||
var lengthName = isSecond ? 'secondElementLength' : 'elementLength'; | ||
|
||
return function() { | ||
this.elementGroups[shaderName].current[lengthName]++; | ||
return this.buffers[bufferName].push(arguments); | ||
}; | ||
} | ||
|
||
function createElementGroups(shaders, buffers) { | ||
var elementGroups = {}; | ||
for (var shaderName in shaders) { | ||
var shader = shaders[shaderName]; | ||
elementGroups[shaderName] = new ElementGroups( | ||
buffers[shader.vertexBuffer], | ||
buffers[shader.elementBuffer], | ||
buffers[shader.secondElementBuffer] | ||
); | ||
} | ||
return elementGroups; | ||
} | ||
|
||
function createElementBuffer(components) { | ||
return new Buffer({ | ||
type: Buffer.BufferType.ELEMENT, | ||
attributes: [{ | ||
name: 'vertices', | ||
components: components || 3, | ||
type: Buffer.ELEMENT_ATTRIBUTE_TYPE | ||
}] | ||
}); | ||
} | ||
|
||
function capitalize(string) { | ||
return string.charAt(0).toUpperCase() + string.slice(1); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry about undoing your work here @mourner . We need to construct
attribute
from scratch because it sometimes includes some extraneous non-serializable fields (namely thevalue
property which is a function)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this looks good. One minor remark is that asserting
itemSize
should probably happen after it gets its new value.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch.