Skip to content

Commit

Permalink
Merge pull request mapbox#2952 from mapbox/shared-tiles
Browse files Browse the repository at this point in the history
Add a minimal global worker pool
  • Loading branch information
Anand Thakker committed Aug 16, 2016
2 parents 38b4581 + 44d23b5 commit d43dc7c
Show file tree
Hide file tree
Showing 13 changed files with 473 additions and 75 deletions.
8 changes: 4 additions & 4 deletions bench/benchmarks/buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,14 @@ function preloadAssets(stylesheet, callback) {

style.on('load', function() {
function getGlyphs(params, callback) {
style['get glyphs'](params, function(err, glyphs) {
style['get glyphs'](0, params, function(err, glyphs) {
assets.glyphs[JSON.stringify(params)] = glyphs;
callback(err, glyphs);
});
}

function getIcons(params, callback) {
style['get icons'](params, function(err, icons) {
style['get icons'](0, params, function(err, icons) {
assets.icons[JSON.stringify(params)] = icons;
callback(err, icons);
});
Expand Down Expand Up @@ -185,10 +185,10 @@ var createLayerFamiliesCacheValue;
function createLayerFamilies(layers) {
if (layers !== createLayerFamiliesCacheKey) {
var worker = new Worker({addEventListener: function() {} });
worker['set layers'](layers);
worker['set layers'](0, layers);

createLayerFamiliesCacheKey = layers;
createLayerFamiliesCacheValue = worker.layerFamilies;
createLayerFamiliesCacheValue = worker.layerFamilies[0];
}
return createLayerFamiliesCacheValue;
}
40 changes: 40 additions & 0 deletions bench/benchmarks/map_load.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use strict';

var Evented = require('../../js/util/evented');
var util = require('../../js/util/util');
var formatNumber = require('../lib/format_number');

module.exports = function(options) {
var evented = util.extend({}, Evented);

var mapsOnPage = 6;

evented.fire('log', { message: 'Creating ' + mapsOnPage + ' maps' });

var loaded = 0;
var start = Date.now();
for (var i = 0; i < mapsOnPage; i++) {
var map = options.createMap({});
map.on('load', onload.bind(null, map));
map.on('error', function (err) {
evented.fire('error', err);
});
}

function onload () {
if (++loaded >= mapsOnPage) {
var duration = Date.now() - start;
evented.fire('end', {
message: formatNumber(duration) + ' ms, loaded ' + mapsOnPage + ' maps.',
score: duration
});
done();
}
}

function done () {
}

return evented;
};

1 change: 1 addition & 0 deletions bench/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ var BenchmarksView = React.createClass({
});

var benchmarks = {
'load-multiple-maps': require('./benchmarks/map_load'),
buffer: require('./benchmarks/buffer'),
fps: require('./benchmarks/fps'),
'frame-duration': require('./benchmarks/frame_duration'),
Expand Down
122 changes: 122 additions & 0 deletions debug/shared_workers.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<!DOCTYPE html>
<html>
<head>
<title>Mapbox GL JS debug page</title>
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">

<link rel='stylesheet' href='../dist/mapbox-gl.css' />
<style>
body { margin: 0; padding: 0; }
html, body, { height: 100%; }
.map {
height: 350px;
width: 350px;
display: inline-block;
}
#checkboxes {
position: absolute;
background: #fff;
top:0;
left:0;
padding:10px;
}
#buffer {
position: absolute;
top:100px;
left:0;
pointer-events: none;
}
#buffer div {
background-color: #fff;
padding: 5px 0;
text-indent: 10px;
white-space: nowrap;
text-shadow:
-1px -1px 0 #fff,
1px -1px 0 #fff,
-1px 1px 0 #fff,
1px 1px 0 #fff;
}
</style>
</head>

<body>
<div class='map'></div>
<div class='map'></div>
<div class='map'></div>
<div class='map'></div>

<div id='checkboxes'>
<input id='show-tile-boundaries-checkbox' name='show-tile-boundaries' type='checkbox'> <label for='show-tile-boundaries'>tile debug</label><br />
<input id='show-symbol-collision-boxes-checkbox' name='show-symbol-collision-boxes' type='checkbox'> <label for='show-symbol-collision-boxes'>collision debug</label><br />
<input id='show-overdraw-checkbox' name='show-overdraw' type='checkbox'> <label for='show-overdraw'>overdraw debug</label><br />
<input id='buffer-checkbox' name='buffer' type='checkbox'> <label for='buffer'>buffer stats</label>
</div>

<div id='buffer' style="display:none">
<em>Waiting for data...</em>
</div>

<script src='mapbox-gl.js'></script>
<script src='access-token.js'></script>

<script>
var containers = document.querySelectorAll('.map')
for (var i = 0; i < containers.length; i++) {
addMap(containers[i]);
}

function addMap (container) {
var map = new mapboxgl.Map({
container: container,
zoom: 12.5,
center: [-77.01866, 38.888],
style: 'mapbox://styles/mapbox/streets-v9',
hash: true
});

map.addControl(new mapboxgl.Navigation());
map.addControl(new mapboxgl.Geolocate());

map.on('load', function() {
map.addSource('geojson', {
"type": "geojson",
"data": "mapbox-gl-test-suite/data/linestring.geojson"
});

map.addLayer({
"id": "route",
"type": "line",
"source": "geojson",
"paint": {
"line-color": "#EC8D8D",
"line-width": {
"base": 1.5,
"stops": [
[
5,
0.75
],
[
18,
32
]
]
}
}
}, 'country-label-lg');
});

map.on('click', function(e) {
if (e.originalEvent.shiftKey) return;
(new mapboxgl.Popup())
.setLngLat(map.unproject(e.point))
.setHTML("<h1>Hello World!</h1>")
.addTo(map);
});
}
</script>

</body>
</html>
85 changes: 50 additions & 35 deletions js/source/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,28 @@ function Worker(self) {
this.self = self;
this.actor = new Actor(self, this);

// simple accessor object for passing to WorkerSources
var styleLayers = {
getLayers: function () { return this.layers; }.bind(this),
getLayerFamilies: function () { return this.layerFamilies; }.bind(this)
};
this.layers = {};
this.layerFamilies = {};

this.workerSources = {
vector: new VectorTileWorkerSource(this.actor, styleLayers),
geojson: new GeoJSONWorkerSource(this.actor, styleLayers)
this.workerSourceTypes = {
vector: VectorTileWorkerSource,
geojson: GeoJSONWorkerSource
};

// [mapId][sourceType] => worker source instance
this.workerSources = {};

this.self.registerWorkerSource = function (name, WorkerSource) {
if (this.workerSources[name]) {
if (this.workerSourceTypes[name]) {
throw new Error('Worker source with name "' + name + '" already registered.');
}
this.workerSources[name] = new WorkerSource(this.actor, styleLayers);
this.workerSourceTypes[name] = WorkerSource;
}.bind(this);
}

util.extend(Worker.prototype, {
'set layers': function(layers) {
this.layers = {};
var that = this;
'set layers': function(mapId, layers) {
var styleLayers = this.layers[mapId] = {};

// Filter layers and create an id -> layer map
var childLayerIndicies = [];
Expand All @@ -60,20 +59,21 @@ util.extend(Worker.prototype, {
function setLayer(serializedLayer) {
var styleLayer = StyleLayer.create(
serializedLayer,
serializedLayer.ref && that.layers[serializedLayer.ref]
serializedLayer.ref && styleLayers[serializedLayer.ref]
);
styleLayer.updatePaintTransitions({}, {transition: false});
that.layers[styleLayer.id] = styleLayer;
styleLayers[styleLayer.id] = styleLayer;
}

this.layerFamilies = createLayerFamilies(this.layers);
this.layerFamilies[mapId] = createLayerFamilies(this.layers[mapId]);
},

'update layers': function(layers) {
var that = this;
'update layers': function(mapId, layers) {
var id;
var layer;

var styleLayers = this.layers[mapId];

// Update ref parents
for (id in layers) {
layer = layers[id];
Expand All @@ -87,41 +87,41 @@ util.extend(Worker.prototype, {
}

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

this.layerFamilies = createLayerFamilies(this.layers);
this.layerFamilies[mapId] = createLayerFamilies(this.layers[mapId]);
},

'load tile': function(params, callback) {
'load tile': function(mapId, params, callback) {
var type = params.type || 'vector';
this.workerSources[type].loadTile(params, callback);
this.getWorkerSource(mapId, type).loadTile(params, callback);
},

'reload tile': function(params, callback) {
'reload tile': function(mapId, params, callback) {
var type = params.type || 'vector';
this.workerSources[type].reloadTile(params, callback);
this.getWorkerSource(mapId, type).reloadTile(params, callback);
},

'abort tile': function(params) {
'abort tile': function(mapId, params) {
var type = params.type || 'vector';
this.workerSources[type].abortTile(params);
this.getWorkerSource(mapId, type).abortTile(params);
},

'remove tile': function(params) {
'remove tile': function(mapId, params) {
var type = params.type || 'vector';
this.workerSources[type].removeTile(params);
this.getWorkerSource(mapId, type).removeTile(params);
},

'redo placement': function(params, callback) {
'redo placement': function(mapId, params, callback) {
var type = params.type || 'vector';
this.workerSources[type].redoPlacement(params, callback);
this.getWorkerSource(mapId, type).redoPlacement(params, callback);
},

/**
Expand All @@ -130,13 +130,28 @@ util.extend(Worker.prototype, {
* function taking `(name, workerSourceObject)`.
* @private
*/
'load worker source': function(params, callback) {
'load worker source': function(map, params, callback) {
try {
this.self.importScripts(params.url);
callback();
} catch (e) {
callback(e);
}
},

getWorkerSource: function(mapId, type) {
if (!this.workerSources[mapId])
this.workerSources[mapId] = {};
if (!this.workerSources[mapId][type]) {
// simple accessor object for passing to WorkerSources
var styleLayers = {
getLayers: function () { return this.layers[mapId]; }.bind(this),
getLayerFamilies: function () { return this.layerFamilies[mapId]; }.bind(this)
};
this.workerSources[mapId][type] = new this.workerSourceTypes[type](this.actor, styleLayers);
}

return this.workerSources[mapId][type];
}
});

Expand Down
6 changes: 3 additions & 3 deletions js/style/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ Style.prototype = util.inherit(Evented, {

// Callbacks from web workers

'get sprite json': function(params, callback) {
'get sprite json': function(_, params, callback) {
var sprite = this.sprite;
if (sprite.loaded()) {
callback(null, { sprite: sprite.data, retina: sprite.retina });
Expand All @@ -762,7 +762,7 @@ Style.prototype = util.inherit(Evented, {
}
},

'get icons': function(params, callback) {
'get icons': function(_, params, callback) {
var sprite = this.sprite;
var spriteAtlas = this.spriteAtlas;
if (sprite.loaded()) {
Expand All @@ -776,7 +776,7 @@ Style.prototype = util.inherit(Evented, {
}
},

'get glyphs': function(params, callback) {
'get glyphs': function(_, params, callback) {
var stacks = params.stacks,
remaining = Object.keys(stacks).length,
allGlyphs = {};
Expand Down
Loading

0 comments on commit d43dc7c

Please sign in to comment.