Skip to content

Commit

Permalink
Make bucket-side style construction faster
Browse files Browse the repository at this point in the history
… allows for buffer-style mismatches at intermediate zoom levels
  • Loading branch information
Lucas Wojciechowski committed Mar 15, 2016
1 parent 851d01c commit ce7fc2b
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 99 deletions.
35 changes: 28 additions & 7 deletions bench/buffer/buffer_benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ var WorkerTile = require('../../js/source/worker_tile');
var ajax = require('../../js/util/ajax');
var Coordinate = require('../../js/geo/coordinate');
var Style = require('../../js/style/style');
var StyleLayer = require('../../js/style/style_layer');
var util = require('../../js/util/util');
var Evented = require('../../js/util/evented');
var config = require('../../js/util/config');
Expand Down Expand Up @@ -259,13 +260,7 @@ function preloadAssets(stylesheet, callback) {
function runSample(stylesheet, getGlyphs, getIcons, getTile, callback) {
var timeStart = performance.now();

var layers = {};
for (var i = 0; i < stylesheet.layers.length; i++) {
var layer = stylesheet.layers[i];
if (layer.type === 'fill' || layer.type === 'line' || layer.type === 'circle' || layer.type === 'symbol') {
layers[layer.id] = layer;
}
}
var layers = createLayers(stylesheet);

util.asyncAll(coordinates, function(coordinate, eachCallback) {
var url = 'https://a.tiles.mapbox.com/v4/mapbox.mapbox-terrain-v2,mapbox.mapbox-streets-v6/' + coordinate.zoom + '/' + coordinate.row + '/' + coordinate.column + '.vector.pbf?access_token=' + config.ACCESS_TOKEN;
Expand Down Expand Up @@ -318,3 +313,29 @@ function asyncTimesSeries(times, work, callback) {
callback();
}
}

function createLayers(stylesheet) {
var layers = {};
var styleLayers = {};

// Filter layers and create an id -> layer map
for (var i = 0; i < stylesheet.layers.length; i++) {
var layer = stylesheet.layers[i];
if (layer.type === 'fill' || layer.type === 'line' || layer.type === 'circle' || layer.type === 'symbol') {
layers[layer.id] = layer;
if (!layer.ref) {
styleLayers[layer.id] = StyleLayer.create(layer);
}
}
}

// Create an instance of StyleLayer per layer
for (var id in layers) {
var layer = layers[id];
if (layer.ref) {
styleLayers[layer.id] = StyleLayer.create(layer, styleLayers[layer.ref]);
}
}

return styleLayers;
}
44 changes: 17 additions & 27 deletions js/data/bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

var featureFilter = require('feature-filter');
var Buffer = require('./buffer');
var StyleLayer = require('../style/style_layer');
var util = require('../util/util');
var assert = require('assert');
var StyleLayer = require('../style/style_layer');

module.exports = Bucket;

Expand Down Expand Up @@ -77,11 +78,14 @@ function Bucket(options) {
this.minZoom = this.layer.minzoom;
this.maxZoom = this.layer.maxzoom;

// TODO make this call more efficient or unnecessary
this.createStyleLayers(options.style);
this.attributes = createAttributes(this);
this.attributeMap = createAttributeMap(this);

for (var key in this.childLayers) {
assert(this.childLayers[key] instanceof StyleLayer);
}
assert(this.layer instanceof StyleLayer);

if (options.elementGroups) {
this.elementGroups = options.elementGroups;
this.buffers = util.mapObject(options.buffers, function(options) {
Expand All @@ -95,6 +99,7 @@ function Bucket(options) {
* @private
*/
Bucket.prototype.populateBuffers = function() {
this.recalculateStyleLayers();
this.createBuffers();

for (var i = 0; i < this.features.length; i++) {
Expand Down Expand Up @@ -255,39 +260,18 @@ Bucket.prototype.getBufferName = function(shaderName, type) {

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

// TODO there will be race conditions when the layer passed here has changed
// since it was used to construct the buffers
Bucket.prototype.createStyleLayers = function(style) {
var that = this;
var refLayer = this.layer = create(this.layer);
this.childLayers = this.childLayers.map(create);

function create(layer) {
if (style) {
return style.getLayer(layer.id);
} else if (!(layer instanceof StyleLayer)) {
layer = StyleLayer.create(layer, refLayer);
layer.cascade({}, {transition: false});
layer.recalculate(that.zoom, { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 });
return layer;
} else {
return layer;
}
}
};

// TODO use lazy evaluation to get rid of this call
Bucket.prototype.createFilter = function() {
if (!this.filter) {
Expand All @@ -297,6 +281,12 @@ Bucket.prototype.createFilter = function() {

Bucket.prototype._premultiplyColor = util.premultiply;

var FAKE_ZOOM_HISTORY = { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 };
Bucket.prototype.recalculateStyleLayers = function() {
for (var i = 0; i < this.childLayers.length; i++) {
this.childLayers[i].recalculate(this.zoom, FAKE_ZOOM_HISTORY);
}
};

var createVertexAddMethodCache = {};
function createVertexAddMethod(bucket, interfaceName) {
Expand Down
5 changes: 5 additions & 0 deletions js/data/bucket/symbol_bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ SymbolBucket.prototype.shaderInterfaces = {
};

SymbolBucket.prototype.populateBuffers = function(collisionTile, stacks, icons) {
this.recalculateStyleLayers();
this.createBuffers();

// To reduce the number of labels that jump around when zooming we need
Expand Down Expand Up @@ -330,6 +331,8 @@ SymbolBucket.prototype.anchorIsTooClose = function(text, repeatDistance, anchor)
};

SymbolBucket.prototype.placeFeatures = function(collisionTile, collisionDebug) {
this.recalculateStyleLayers();

// Calculate which labels can be shown and when they can be shown and
// create the bufers used for rendering.

Expand Down Expand Up @@ -478,6 +481,7 @@ SymbolBucket.prototype.addSymbols = function(shaderName, quads, scale, keepUprig
};

SymbolBucket.prototype.updateIcons = function(icons) {
this.recalculateStyleLayers();
var iconValue = this.layer.layout['icon-image'];
if (!iconValue) return;

Expand All @@ -489,6 +493,7 @@ SymbolBucket.prototype.updateIcons = function(icons) {
};

SymbolBucket.prototype.updateFont = function(stacks) {
this.recalculateStyleLayers();
var fontName = this.layer.layout['text-font'],
stack = stacks[fontName] = stacks[fontName] || {};

Expand Down
1 change: 0 additions & 1 deletion js/render/draw_circle.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ function drawCircles(painter, source, layer, coords) {
var tile = source.getTile(coord);
var bucket = tile.getBucket(layer);
if (!bucket) continue;
bucket.createStyleLayers(painter.style);
var elementGroups = bucket.elementGroups.circle;
if (!elementGroups) continue;

Expand Down
2 changes: 1 addition & 1 deletion js/source/geojson_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ GeoJSONSource.prototype = util.inherit(Evented, /** @lends GeoJSONSource.prototy
return;
}

tile.loadVectorData(data);
tile.loadVectorData(data, this.map.style);

if (tile.redoWhenDone) {
tile.redoWhenDone = false;
Expand Down
13 changes: 8 additions & 5 deletions js/source/tile.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ Tile.prototype = {
* @returns {undefined}
* @private
*/
loadVectorData: function(data) {
loadVectorData: function(data, style) {
this.loaded = true;

// empty GeoJSON tile
if (!data) return;

this.buckets = unserializeBuckets(data.buckets);
this.buckets = unserializeBuckets(data.buckets, style);
},

/**
Expand All @@ -89,7 +89,7 @@ Tile.prototype = {
reloadSymbolData: function(data, painter) {
if (this.isUnloaded) return;

var newBuckets = unserializeBuckets(data.buckets);
var newBuckets = unserializeBuckets(data.buckets, painter.style);
for (var id in newBuckets) {
var newBucket = newBuckets[id];
var oldBucket = this.buckets[id];
Expand Down Expand Up @@ -151,10 +151,13 @@ Tile.prototype = {
}
};

function unserializeBuckets(input) {
function unserializeBuckets(input, style) {
var output = {};
for (var i = 0; i < input.length; i++) {
var bucket = Bucket.create(input[i]);
var bucket = Bucket.create(util.extend({
childLayers: input[i].childLayerIds.map(style.getLayer.bind(style)),
layer: style.getLayer(input[i].layerId)
}, input[i]));
output[bucket.id] = bucket;
}
return output;
Expand Down
2 changes: 1 addition & 1 deletion js/source/vector_tile_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ VectorTileSource.prototype = util.inherit(Evented, {
return;
}

tile.loadVectorData(data);
tile.loadVectorData(data, this.map.style);

if (tile.redoWhenDone) {
tile.redoWhenDone = false;
Expand Down
61 changes: 56 additions & 5 deletions js/source/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

var Actor = require('../util/actor');
var WorkerTile = require('./worker_tile');
var StyleLayer = require('../style/style_layer');
var util = require('../util/util');
var ajax = require('../util/ajax');
var vt = require('vector-tile');
Expand All @@ -26,14 +27,33 @@ function Worker(self) {

util.extend(Worker.prototype, {
'set layers': function(layers) {
this.layers = {};
for (var i = 0; i < layers.length; i++) {
this.layers[layers[i].id] = layers[i];
}
this.layers = unserializeLayers(layers);
},

'update layers': function(layers) {
util.extend(this.layers, layers);
var that = this;
var id;

// Update ref parents
for (id in layers) {
var layer = layers[id];
if (layer.ref) updateLayer(layer);
}

// Update ref children
for (id in layers) {
var layer = layers[id];
if (!layer.ref) updateLayer(layers[id]);
}

function updateLayer(layer) {
if (that.layers[layer.id]) {
that.layers[layer.id].set(layer);
} else {
that.layers[layer.id] = StyleLayer.create(layer, that.layers[layer.ref]);
}
that.layers[layer.id].cascade({}, {transition: false});
}
},

'load tile': function(params, callback) {
Expand Down Expand Up @@ -166,3 +186,34 @@ util.extend(Worker.prototype, {
}
}
});

function unserializeLayers(layersArray) {
var layersMap = {};
var styleLayersMap = {};
var layer;
var styleLayer;

// Filter layers and create an id -> layer map
for (var i = 0; i < layersArray.length; i++) {
layer = layersArray[i];
if (layer.type === 'fill' || layer.type === 'line' || layer.type === 'circle' || layer.type === 'symbol') {
if (layer.ref) {
layersMap[layer.id] = layer;
} else {
styleLayer = StyleLayer.create(layer);
styleLayer.cascade({}, {transition: false});
styleLayersMap[layer.id] = styleLayer;
}
}
}

// Create an instance of StyleLayer per layer
for (var id in layersMap) {
layer = layersMap[id];
styleLayer = StyleLayer.create(layer, styleLayersMap[layer.ref]);
styleLayer.cascade({}, {transition: false});
styleLayersMap[layer.id] = styleLayer;
}

return styleLayersMap;
}
4 changes: 2 additions & 2 deletions js/source/worker_tile.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ WorkerTile.prototype.parse = function(data, layers, actor, callback) {
bucketsById[layer.id] = bucket;

if (data.layers) { // vectortile
sourceLayerId = layer['source-layer'];
sourceLayerId = layer.sourceLayer;
bucketsBySourceLayer[sourceLayerId] = bucketsBySourceLayer[sourceLayerId] || {};
bucketsBySourceLayer[sourceLayerId][layer.id] = bucket;
}
Expand Down Expand Up @@ -185,7 +185,7 @@ WorkerTile.prototype.parse = function(data, layers, actor, callback) {

callback(null, {
buckets: buckets.map(function(bucket) { return bucket.serialize(); }),
bucketStats: stats // TODO put this in a separate message?
bucketStats: stats // TODO put this in a separate message
}, getTransferables(buckets));
}
};
Expand Down
Loading

0 comments on commit ce7fc2b

Please sign in to comment.