Skip to content

Commit

Permalink
Merge pull request #644 from OpenGeoscience/layer-lag
Browse files Browse the repository at this point in the history
Remove the lag between layers.
  • Loading branch information
manthey authored Nov 21, 2016
2 parents 73e8285 + 739e2ab commit b85183d
Show file tree
Hide file tree
Showing 13 changed files with 90 additions and 32 deletions.
10 changes: 2 additions & 8 deletions src/d3/d3Renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ var d3Renderer = function (arg) {
m_diagonal = null,
m_scale = 1,
m_transform = {dx: 0, dy: 0, rx: 0, ry: 0, rotation: 0},
m_renderAnimFrameRef = null,
m_renderIds = {},
m_removeIds = {},
m_svg = null,
Expand Down Expand Up @@ -509,9 +508,7 @@ var d3Renderer = function (arg) {
m_this._renderFeature(id, parentId);
} else {
m_renderIds[id] = true;
if (m_renderAnimFrameRef === null) {
m_renderAnimFrameRef = window.requestAnimationFrame(m_this._renderFrame);
}
m_this.layer().map().scheduleAnimationFrame(m_this._renderFrame);
}
};

Expand All @@ -524,7 +521,6 @@ var d3Renderer = function (arg) {
m_removeIds = {};
var ids = m_renderIds;
m_renderIds = {};
m_renderAnimFrameRef = null;
for (id in ids) {
if (ids.hasOwnProperty(id)) {
m_this._renderFeature(id);
Expand Down Expand Up @@ -578,9 +574,7 @@ var d3Renderer = function (arg) {
////////////////////////////////////////////////////////////////////////////
this._removeFeature = function (id) {
m_removeIds[id] = true;
if (m_renderAnimFrameRef === null) {
m_renderAnimFrameRef = window.requestAnimationFrame(m_this._renderFrame);
}
m_this.layer().map().scheduleAnimationFrame(m_this._renderFrame);
delete m_features[id];
if (m_renderIds[id]) {
delete m_renderIds[id];
Expand Down
4 changes: 2 additions & 2 deletions src/gl/polygonFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -340,11 +340,11 @@ var gl_polygonFeature = function (arg) {
////////////////////////////////////////////////////////////////////////////
this._update = function (opts) {
if (opts && opts.mayDelay) {
m_updateAnimFrameRef = window.requestAnimationFrame(this._update);
m_updateAnimFrameRef = m_this.layer().map().scheduleAnimationFrame(m_this._update);
return;
}
if (m_updateAnimFrameRef) {
window.cancelAnimationFrame(m_updateAnimFrameRef);
m_this.layer().map().scheduleAnimationFrame(m_this._update, 'remove');
m_updateAnimFrameRef = null;
}
s_update.call(m_this);
Expand Down
3 changes: 1 addition & 2 deletions src/gl/quadFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ var gl_quadFeature = function (arg) {
if (m_clrModelViewUniform) {
m_clrModelViewUniform.setOrigin(m_quads.origin);
}
m_this._updateTextures();
m_this.buildTime().modified();
};

Expand Down Expand Up @@ -330,8 +331,6 @@ var gl_quadFeature = function (arg) {
opacity = 1,
crop = {x: 1, y: 1}, quadcrop;

m_this._updateTextures();

context.bindBuffer(vgl.GL.ARRAY_BUFFER, m_glBuffers.imgQuadsPosition);
$.each(m_quads.imgQuads, function (idx, quad) {
if (!quad.image) {
Expand Down
25 changes: 16 additions & 9 deletions src/gl/vglRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ var vglRenderer = function (arg) {
m_viewer = null,
m_width = 0,
m_height = 0,
m_renderAnimFrameRef = null,
m_lastZoom,
m_updateCamera = false,
s_init = this._init,
s_exit = this._exit;

Expand Down Expand Up @@ -121,7 +121,7 @@ var vglRenderer = function (arg) {
m_this.canvas().attr('height', h);
renderWindow.positionAndResize(x, y, w, h);

m_this._updateRendererCamera();
m_updateCamera = true;
m_this._render();

return m_this;
Expand All @@ -133,18 +133,24 @@ var vglRenderer = function (arg) {
*/
////////////////////////////////////////////////////////////////////////////
this._render = function () {
if (m_renderAnimFrameRef) {
window.cancelAnimationFrame(m_renderAnimFrameRef);
}
m_renderAnimFrameRef = window.requestAnimationFrame(this._renderFrame);
/* If we are already scheduled to render, don't schedule again. Rather,
* mark that we should render after other animation frame requests occur.
* It would be nice if we could just reschedule the call by removing and
* readding the animation frame request, but this doesn't work for if the
* reschedule occurs during another animation frame callback (it then waits
* until a subsequent frame). */
m_this.layer().map().scheduleAnimationFrame(this._renderFrame, true);
return m_this;
};

/**
* This clears the render timer and actually renders.
*/
this._renderFrame = function () {
m_renderAnimFrameRef = null;
if (m_updateCamera) {
m_updateCamera = false;
m_this._updateRendererCamera();
}
m_viewer.render();
};

Expand Down Expand Up @@ -229,7 +235,7 @@ var vglRenderer = function (arg) {
// produce a pan
m_this.layer().geoOn(geo_event.pan, function (evt) {
void (evt);
m_this._updateRendererCamera();
m_updateCamera = true;
});

// Connect to parallelprojection event
Expand All @@ -242,10 +248,11 @@ var vglRenderer = function (arg) {
if (!vglRenderer || !vglRenderer.camera()) {
console.log('Parallel projection event triggered on unconnected VGL ' +
'renderer.');
return;
}
camera = vglRenderer.camera();
camera.setEnableParallelProjection(evt.parallelProjection);
m_this._updateRendererCamera();
m_updateCamera = true;
}
});

Expand Down
58 changes: 56 additions & 2 deletions src/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ var sceneObject = require('./sceneObject');
* @param {geo.camera?} camera The camera to control the view
* @param {geo.mapInteractor?} interactor The UI event handler
* @param {geo.clock?} clock The clock used to synchronize time events
* @param {array} [animationQueue] An array used to synchonize animations. If
* specified, this should be an empty array or the same array as passed to
* other map instances.
* @param {boolean} [autoResize=true] Adjust map size on window resize
* @param {boolean} [clampBoundsX=false] Prevent panning outside of the
* maximum bounds in the horizontal direction.
Expand Down Expand Up @@ -127,6 +130,7 @@ var map = function (arg) {
m_clampBoundsX,
m_clampBoundsY,
m_clampZoom,
m_animationQueue = arg.animationQueue || [],
m_origin,
m_scale = {x: 1, y: 1, z: 1}; // constant and ignored for the moment

Expand Down Expand Up @@ -1229,7 +1233,7 @@ var map = function (arg) {
}
m_this.rotation(p[3], undefined, true);

window.requestAnimationFrame(anim);
m_this.scheduleAnimationFrame(anim);
}

m_this.geoTrigger(geo_event.transitionstart, opts);
Expand All @@ -1245,7 +1249,7 @@ var map = function (arg) {
} else if (animTime) {
anim(animTime);
} else {
window.requestAnimationFrame(anim);
m_this.scheduleAnimationFrame(anim);
}
return m_this;
};
Expand Down Expand Up @@ -1566,6 +1570,56 @@ var map = function (arg) {
return m_this;
};

/**
* Instead of each function using window.requestAnimationFrame, schedule all
* such frames here. This allows the callbacks to be reordered or removed as
* needed and reduces overhead in Chrome a small amount. Also, if the
* animation queue is shared between map instances, the callbacks will be
* called as one, providing better synchronization.
*
* @param {function} callback: function to call during the animation frame.
* It is called with an animation epoch, exactly as requestAnimationFrame.
* @param {string|boolean} action: falsy to only add the callback if it is
* not already scheduled. 'remove' to remove the callback (use this
* instead of cancelAnimationFrame). Any other truthy value moves the
* callback to the end of the list.
* @returns {integer} An integer as returned by window.requestAnimationFrame.
*/
this.scheduleAnimationFrame = function (callback, action) {
if (!m_animationQueue.length) {
/* By refering to requestAnimationFrame as a property of window, versus
* explicitly using window.requestAnimationFrame, we prevent the
* stripping of 'window' off of the reference and allow our tests to
* override this if needed. */
m_animationQueue.push(window['requestAnimationFrame'](processAnimationFrame));
}
var pos = m_animationQueue.indexOf(callback, 1);
if (pos >= 0) {
if (!action) {
return;
}
m_animationQueue.splice(pos, 1);
if (action === 'remove') {
return;
}
}
m_animationQueue.push(callback);
return m_animationQueue[0];
};

/**
* Sevice the callback during an animation frame. This uses splice to modify
* the animationQueue to allow multiple map instances to share the queue.
*/
function processAnimationFrame() {
var queue = m_animationQueue.splice(0, m_animationQueue.length);

/* The first entry is the reference to the window.requestAnimationFrame. */
for (var i = 1; i < queue.length; i += 1) {
queue[i].apply(this, arguments);
}
}

////////////////////////////////////////////////////////////////////////////
//
// The following are some private methods for interacting with the camera.
Expand Down
4 changes: 2 additions & 2 deletions src/mapInteractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -1431,11 +1431,11 @@ var mapInteractor = function (args) {
}

if (m_state.handler) {
window.requestAnimationFrame(m_state.handler);
m_this.map().scheduleAnimationFrame(m_state.handler);
}
};
if (m_state.handler) {
window.requestAnimationFrame(m_state.handler);
m_this.map().scheduleAnimationFrame(m_state.handler);
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/util/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@
} else if (!stop && !m_originalRequestAnimationFrame) {
m_originalRequestAnimationFrame = window.requestAnimationFrame;
window.requestAnimationFrame = function (callback) {
m_originalRequestAnimationFrame.call(window, function (timestamp) {
return m_originalRequestAnimationFrame.call(window, function (timestamp) {
var track = m_timingData.requestAnimationFrame, recent;
/* Some environments have unsynchronized performance and time
* counters. The nowDelta factor compensates for this. For
Expand Down
2 changes: 1 addition & 1 deletion tests/cases/d3GraphFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ describe('d3 graph feature', function () {
var map, layer, feature;

it('Setup map', function () {
mockAnimationFrame();
map = geo.map({node: '#map-d3-graph-feature', center: [0, 0], zoom: 3});
layer = map.createLayer('feature', {'renderer': 'd3'});
});

it('Add features to a layer', function () {
mockAnimationFrame();
var selection, nodes;

nodes = [
Expand Down
2 changes: 1 addition & 1 deletion tests/cases/d3PointFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ describe('d3 point feature', function () {
var map, width = 800, height = 600, layer, feature1, feature2;

it('Setup map', function () {
mockAnimationFrame();
map = geo.map({node: '#map-d3-point-feature', center: [0, 0], zoom: 3});
layer = map.createLayer('feature', {'renderer': 'd3'});

map.resize(0, 0, width, height);
});

it('Add features to a layer', function () {
mockAnimationFrame();
var selection;
feature1 = layer.createFeature('point', {selectionAPI: true})
.data([{y: 0, x: 0}, {y: 10, x: 0}, {y: 0, x: 10}])
Expand Down
2 changes: 1 addition & 1 deletion tests/cases/d3VectorFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ describe('d3 vector feature', function () {
var map, layer, feature1;

it('Create a map with a d3 feature layer', function () {
mockAnimationFrame();
d3.select('body').append('div').attr('id', 'map-d3-vector');
map = geo.map({node: '#map-d3-vector',
center: [0, 0],
Expand All @@ -36,7 +37,6 @@ describe('d3 vector feature', function () {
});

it('Add features to a layer', function () {
mockAnimationFrame();
var vectorLines, featureGroup, markers;
feature1 = layer.createFeature('vector')
.data([{y: 0, x: 0}, {y: 10, x: 0}, {y: 0, x: 10}])
Expand Down
2 changes: 1 addition & 1 deletion tests/cases/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,8 @@ describe('geo.core.map', function () {
expect(closeToEqual(zc.center, {x: 0, y: 0})).toBe(true);
});
it('transition', function () {
var m = create_map(), start, wasCalled;
mockAnimationFrame();
var m = create_map(), start, wasCalled;
expect(m.transition()).toBe(null);
start = new Date().getTime();
m.transition({
Expand Down
7 changes: 5 additions & 2 deletions tests/cases/mapInteractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ describe('mapInteractor', function () {
map.gcs = function (arg) {
return 'EPSG:3857';
};
map.scheduleAnimationFrame = function (callback) {
return window['requestAnimationFrame'](callback);
};
return map;
}

Expand Down Expand Up @@ -1327,6 +1330,7 @@ describe('mapInteractor', function () {
});

it('Test momentum', function () {
mockAnimationFrame();
var map = mockedMap('#mapNode1'), start;

var interactor = geo.mapInteractor({
Expand All @@ -1338,7 +1342,6 @@ describe('mapInteractor', function () {
}],
throttle: false
});
mockAnimationFrame();
mockDate();
// initiate a pan and release
interactor.simulateEvent(
Expand Down Expand Up @@ -1380,6 +1383,7 @@ describe('mapInteractor', function () {
});

it('Test springback', function () {
mockAnimationFrame();
$('#mapNode1').css({width: '400px', height: '400px'});
var map = mockedMap('#mapNode1'), start;

Expand All @@ -1393,7 +1397,6 @@ describe('mapInteractor', function () {
}],
throttle: false
});
mockAnimationFrame();
mockDate();
// pan past the max bounds
interactor.simulateEvent(
Expand Down
1 change: 1 addition & 0 deletions tests/test-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ module.exports.mockAnimationFrame = function (mockDate) {
animFrameIndex += 1;
var id = animFrameIndex;
animFrameCallbacks.push({id: id, callback: callback});
return id;
}

/* Replace window.cancelAnimationFrame with this function.
Expand Down

0 comments on commit b85183d

Please sign in to comment.