Skip to content

Commit

Permalink
Autoshare webgl renderers.
Browse files Browse the repository at this point in the history
Autosharing is now the default.  Renderers can be manually shared, and
webgl layers can be manually be moved to different renderer.

To manually move a layer to a different renderer, for instance, given
two layers: `var layer1 = map.createLayer('feature', {renderer: 'webgl',
autoshareRenderer: false});  var layer2 = map.createLayer('feature',
{renderer: layer1.renderer(), autoshareRenderer: false});`, the second
layer can be moved to a new renderer by doing:
`layer2.switchRenderer(geo.createRenderer('webgl', layer2), true);`.
  • Loading branch information
manthey committed Jan 4, 2019
1 parent 3b048d9 commit 0fe47be
Show file tree
Hide file tree
Showing 26 changed files with 474 additions and 41 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

### Features
- Layers that use webgl renderers automatically share contexts when possible. Layers can switch renderers manually as well. This largely avoids the limitation of number of webgl contexts in a browser.

### Changes
- Rename the d3 renderer to svg. d3 still works as an alias (#965)
- Rename the vgl renderer to webgl. vgl still works as an alias (#965)
Expand Down
2 changes: 1 addition & 1 deletion src/canvas/quadFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ var canvas_quadFeature = function (arg) {
}
}
});
if (render) {
if (render && m_this.renderer()) {
m_this.renderer()._render();
}
if (changing) {
Expand Down
13 changes: 7 additions & 6 deletions src/feature.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ var geo_event = require('./event');
* bin number, their order relative to one another is indeterminate and may
* be unstable. A value of `null` will use the current position of the
* feature within its parent's list of children as the bin number.
* @property {geo.renderer?} [renderer] A reference to the renderer used for
* the feature.
* @property {geo.renderer} [renderer] A reference to the renderer used for
* the feature. If `null` or unset or identical to `layer.renderer()`, the
* layer's renderer is used.
* @property {geo.feature.styleSpec} [style] An object that contains style
* values for the feature.
*/
Expand Down Expand Up @@ -120,7 +121,7 @@ var feature = function (arg) {
m_gcs = arg.gcs,
m_visible = arg.visible === undefined ? true : arg.visible,
m_bin = arg.bin === undefined ? null : arg.bin,
m_renderer = arg.renderer === undefined ? null : arg.renderer,
m_renderer = arg.renderer === undefined || (m_layer && arg.renderer === m_layer.renderer()) ? null : arg.renderer,
m_dataTime = timestamp(),
m_buildTime = timestamp(),
m_updateTime = timestamp(),
Expand Down Expand Up @@ -574,7 +575,7 @@ var feature = function (arg) {
* @returns {geo.renderer} The renderer used to render the feature.
*/
this.renderer = function () {
return m_renderer;
return m_renderer || (m_layer && m_layer.renderer());
};

/**
Expand Down Expand Up @@ -610,8 +611,8 @@ var feature = function (arg) {
var map = m_layer.map();
c = map.gcsToWorld(c, m_this.gcs());
c = map.worldToDisplay(c);
if (m_renderer.baseToLocal) {
c = m_renderer.baseToLocal(c);
if (m_this.renderer().baseToLocal) {
c = m_this.renderer().baseToLocal(c);
}
return c;
};
Expand Down
18 changes: 18 additions & 0 deletions src/layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ var rendererForAnnotations = require('./registry').rendererForAnnotations;
* to determine the renderer. If a {@link geo.renderer} instance, the
* renderer is not recreated; not all renderers can be shared by multiple
* layers.
* @property {boolean} [autoshareRenderer=true] If truthy and the renderer
* supports it, auto-share renderers between layers. Currently, auto-sharing
* can only occur for webgl renderers, and will only occur between adjacent
* layers than have the same opacity. Shared renderers has slightly
* different behavior than non-shared renderers: changing z-index may result
* in rerendering and be slightly slower; only one DOM canvas is used for all
* shared renderers. Some features have slight z-stacking differences in
* shared versus non-shared renderers.
* @property {HTMLElement} [canvas] If specified, use this canvas rather than
* a canvas associaied with the renderer directly. Renderers may not support
* sharing a canvas.
Expand Down Expand Up @@ -87,6 +95,7 @@ var layer = function (arg) {
arg.renderer instanceof renderer ? arg.renderer.api() : arg.renderer) : (
arg.annotations ? rendererForAnnotations(arg.annotations) :
rendererForFeatures(arg.features)),
m_autoshareRenderer = arg.autoshareRenderer === undefined ? true : arg.autoshareRenderer,
m_dataTime = timestamp(),
m_updateTime = timestamp(),
m_sticky = arg.sticky === undefined ? true : arg.sticky,
Expand Down Expand Up @@ -125,6 +134,15 @@ var layer = function (arg) {
return m_rendererName;
};

/**
* Get the setting of autoshareRenderer.
*
* @returns {boolean}
*/
this.autoshareRenderer = function () {
return m_autoshareRenderer;
};

/**
* Get or set the z-index of the layer. The z-index controls the display
* order of the layers in much the same way as the CSS z-index property.
Expand Down
4 changes: 2 additions & 2 deletions src/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -1761,13 +1761,13 @@ var map = function (arg) {
layer.node().children('canvas').each(function () {
var canvasElem = $(this);
defer = defer.then(function () {
if (layer.renderer().api() === 'webgl') {
if (layer.renderer() && layer.renderer().api() === 'webgl') {
layer.renderer()._renderFrame();
}
drawLayerImageToContext(context, opacity, canvasElem, canvasElem[0]);
});
});
if (layer.node().children().not('canvas').length || !layer.node().children().length) {
if ((layer.node().children().not('canvas').length || !layer.node().children().length) && (!layer.renderer() || layer.renderer().api() !== 'webgl')) {
defer = defer.then(function () {
return util.htmlToImage(layer.node(), 1).done(function (img) {
drawLayerImageToContext(context, 1, $([]), img);
Expand Down
1 change: 1 addition & 0 deletions src/util/mockVGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ module.exports.mockWebglRenderer = function mockWebglRenderer(supported) {
deleteProgram: noop('deleteProgram'),
deleteShader: noop('deleteShader'),
deleteTexture: noop('deleteTexture'),
detachShader: noop('detachShader'),
depthFunc: noop('depthFunc'),
disable: noop('disable'),
disableVertexAttribArray: noop('disableVertexAttribArray'),
Expand Down
9 changes: 9 additions & 0 deletions src/webgl/contourFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,15 @@ var webgl_contourFeature = function (arg) {
m_mapper.setGeometryData(geom);
};

/**
* List vgl actors.
*
* @returns {vgl.actor[]} The list of actors.
*/
this.actors = function () {
return m_actor ? [m_actor] : [];
};

/**
* Build.
*/
Expand Down
140 changes: 140 additions & 0 deletions src/webgl/layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,31 @@ var registerLayerAdjustment = require('../registry').registerLayerAdjustment;
var webgl_layer = function () {
'use strict';

var createRenderer = require('../registry').createRenderer;
var geo_event = require('../event');
var webglRenderer = require('./webglRenderer');

var m_this = this,
s_init = this._init,
s_opacity = this.opacity,
s_visible = this.visible,
s_zIndex = this.zIndex;

/**
* Get or set the current layer opacity. The opacity is in the range [0-1].
*
* @param {number} [opacity] If specified, set the opacity. Otherwise,
* return the opacity.
* @returns {number|this} The current opacity or the current layer.
*/
this.opacity = function (opacity) {
var result = s_opacity.apply(m_this, arguments);
if (opacity !== undefined && m_this.initialized()) {
m_this.map()._updateAutoshareRenderers();
}
return result;
};

/**
* Get/Set visibility of the layer.
*
Expand Down Expand Up @@ -45,9 +66,128 @@ var webgl_layer = function () {
/* If the z-index has changed, schedule rerendering the layer. */
m_this.map().scheduleAnimationFrame(m_this._update, true);
m_this.renderer()._render();
m_this.map()._updateAutoshareRenderers();
}
return result;
};

/**
* Move all of the objects associated with this layer to a different webgl
* renderer. This runs the _cleanup routine for any feature or child of the
* layer (if it has one), removes all actors associated with this layer from
* the existing renderer, then adds those actors to the new renderer and
* calls `_update` on any feature that had a `_cleanup` routine. If desired,
* the old and new renderers are both asked to rerender. If moving multiple
* renderers for multiple layers, rerendering can be delayed.
*
* @param {geo.webgl.webglRenderer} newRenderer The renderer to move to.
* @param {boolean} [rerender=false] If truthy, rerender after the switch.
* @returns {this}
*/
this.switchRenderer = function (newRenderer, rerender) {
if (newRenderer instanceof webglRenderer && newRenderer !== m_this.renderer()) {
var oldRenderer = m_this.renderer(),
actors = [],
updates = [];
m_this.map().listSceneObjects([m_this]).forEach(function (obj) {
if (obj._cleanup) {
obj._cleanup();
if (obj._update) {
updates.push(obj);
}
}
if (obj.actors) {
actors = actors.concat(obj.actors());
}
});
actors.forEach(function (actor) {
oldRenderer.contextRenderer().removeActor(actor);
newRenderer.contextRenderer().addActor(actor);
});
m_this._renderer(newRenderer);
m_this._canvas(newRenderer.canvas());
if (rerender && (actors.length || updates.length)) {
oldRenderer._render();
updates.forEach(function (obj) {
obj._update();
});
newRenderer._render();
}
}
return m_this;
};

/**
* Initialize after the layer is added to the map.
*
* @returns {this}
*/
this._init = function () {

var map = m_this.map();
if (!map._updateAutoshareRenderers) {
/**
* Update all webgl autoshareRenderer layers so that appropriate groups
* of layers share renderers. Each group must (a) be continguous in
* z-space (not separated by a non-autoshare layer or a non-webgl layer),
* and (b) have the same opacity. The lowest layer in each group will
* contain the actual canvas and context. This rerenders as needed.
*/
map._updateAutoshareRenderers = function () {
var layers = map.sortedLayers(),
renderer,
used_canvases = [],
canvases = [],
rerender_list = [],
opacity;
layers.forEach(function (layer) {
if (!layer.autoshareRenderer() || !layer.renderer() || layer.renderer().api() !== webglRenderer.apiname) {
renderer = null;
} else if (!renderer || layer.opacity() !== opacity) {
if (!layer.node()[0].contains(layer.renderer().canvas()[0])) {
layer.switchRenderer(createRenderer(webglRenderer.apiname, layer), false);
rerender_list.push(layer.renderer());
}
renderer = layer.renderer();
used_canvases.push(renderer.canvas()[0]);
opacity = layer.opacity();
} else {
if (layer.renderer() !== renderer) {
rerender_list.push(layer.renderer());
canvases.push(layer.renderer().canvas()[0]);
layer.switchRenderer(renderer, false);
rerender_list.push(layer.renderer());
}
}
});
layers.forEach(function (layer) {
if (rerender_list.indexOf(layer.renderer()) >= 0) {
if (layer._update) {
layer._update();
}
}
});
layers.forEach(function (layer) {
if (rerender_list.indexOf(layer.renderer()) >= 0) {
layer.renderer()._render();
rerender_list = rerender_list.filter((val) => val !== layer.renderer());
}
});
canvases.forEach(function (canvas) {
if (used_canvases.indexOf(canvas) < 0) {
canvas.remove();
used_canvases.push(canvas);
}
});
};

map.geoOn(geo_event.layerAdd, () => map._updateAutoshareRenderers());
map.geoOn(geo_event.layerRemove, () => map._updateAutoshareRenderers());
}

return s_init.apply(m_this, arguments);
};

};

registerLayerAdjustment('webgl', 'all', webgl_layer);
Expand Down
9 changes: 3 additions & 6 deletions src/webgl/lineFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -734,15 +734,12 @@ var webgl_lineFeature = function (arg) {
};

/**
* Return list of vgl actors used for rendering.
* List vgl actors.
*
* @returns {vgl.actor[]}
* @returns {vgl.actor[]} The list of actors.
*/
this.actors = function () {
if (!m_actor) {
return [];
}
return [m_actor];
return m_actor ? [m_actor] : [];
};

/**
Expand Down
4 changes: 2 additions & 2 deletions src/webgl/pointFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -367,9 +367,9 @@ var webgl_pointFeature = function (arg) {
}

/**
* Return list of vgl actors used for rendering.
* List vgl actors.
*
* @returns {vgl.actor[]}
* @returns {vgl.actor[]} The list of actors.
*/
this.actors = function () {
if (!m_actor) {
Expand Down
9 changes: 9 additions & 0 deletions src/webgl/polygonFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,15 @@ var webgl_polygonFeature = function (arg) {
s_init.call(m_this, arg);
};

/**
* List vgl actors.
*
* @returns {vgl.actor[]} The list of actors.
*/
this.actors = function () {
return [m_actor];
};

/**
* Build.
*
Expand Down
Loading

0 comments on commit 0fe47be

Please sign in to comment.