Skip to content
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

Make all gl traces show "no webgl message" and fail gracefully #2697

Merged
merged 6 commits into from
Jun 6, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 23 additions & 9 deletions src/lib/prepare_regl.js
Original file line number Diff line number Diff line change
@@ -8,6 +8,8 @@

'use strict';

var showNoWebGlMsg = require('./show_no_webgl_msg');

// Note that this module should be ONLY required into
// files corresponding to regl trace modules
// so that bundles with non-regl only don't include
@@ -21,23 +23,35 @@ var createRegl = require('regl');
*
* @param {DOM node or object} gd : graph div object
* @param {array} extensions : list of extension to pass to createRegl
*
* @return {boolean} true if all createRegl calls succeeded, false otherwise
*/
module.exports = function prepareRegl(gd, extensions) {
var fullLayout = gd._fullLayout;
var success = true;

fullLayout._glcanvas.each(function(d) {
if(d.regl) return;
// only parcoords needs pick layer
if(d.pick && !fullLayout._has('parcoords')) return;

d.regl = createRegl({
canvas: this,
attributes: {
antialias: !d.pick,
preserveDrawingBuffer: true
},
pixelRatio: gd._context.plotGlPixelRatio || global.devicePixelRatio,
extensions: extensions || []
});
try {
d.regl = createRegl({
canvas: this,
attributes: {
antialias: !d.pick,
preserveDrawingBuffer: true
},
pixelRatio: gd._context.plotGlPixelRatio || global.devicePixelRatio,
extensions: extensions || []
});
} catch(e) {
success = false;
}
});

if(!success) {
showNoWebGlMsg({container: fullLayout._glcontainer.node()});
}
return success;
};
21 changes: 18 additions & 3 deletions src/lib/show_no_webgl_msg.js
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@ var noop = function() {};
* Expects 'scene' to have property 'container'
*
*/
module.exports = function showWebGlMsg(scene) {
module.exports = function showNoWebGlMsg(scene) {
for(var prop in scene) {
if(typeof scene[prop] === 'function') scene[prop] = noop;
}
@@ -31,11 +31,26 @@ module.exports = function showWebGlMsg(scene) {
};

var div = document.createElement('div');
div.textContent = 'Webgl is not supported by your browser - visit https://get.webgl.org for more info';
div.className = 'no-webgl';
div.style.cursor = 'pointer';
div.style.fontSize = '24px';
div.style.color = Color.defaults[0];

div.style.position = 'absolute';
div.style.left = div.style.top = '0px';
div.style.width = div.style.height = '100%';
div.style['background-color'] = Color.lightLine;
div.style['z-index'] = 30;

var p = document.createElement('p');
p.textContent = 'WebGL is not supported by your browser - visit https://get.webgl.org for more info';
p.style.position = 'relative';
p.style.top = '50%';
p.style.left = '50%';
p.style.height = '30%';
p.style.width = '50%';
p.style.margin = '-15% 0 0 -25%';

div.appendChild(p);
scene.container.appendChild(div);
scene.container.style.background = '#FFFFFF';
scene.container.onclick = function() {
7 changes: 6 additions & 1 deletion src/plots/gl2d/scene2d.js
Original file line number Diff line number Diff line change
@@ -45,6 +45,7 @@ function Scene2D(options, fullLayout) {
this.updateRefs(fullLayout);

this.makeFramework();
if(this.stopped) return;

// update options
this.glplotOptions = createOptions(this);
@@ -121,7 +122,11 @@ proto.makeFramework = function() {
premultipliedAlpha: true
});

if(!gl) showNoWebGlMsg(this);
if(!gl) {
showNoWebGlMsg(this);
this.stopped = true;
return;
}

this.canvas = liveCanvas;
this.gl = gl;
2 changes: 1 addition & 1 deletion src/plots/gl3d/scene.js
Original file line number Diff line number Diff line change
@@ -204,7 +204,7 @@ function initializeGLPlot(scene, fullLayout, canvas, gl) {
* The destroy method - which will remove the container from the DOM
* is overridden with a function that removes the container only.
*/
showNoWebGlMsg(scene);
return showNoWebGlMsg(scene);
}

var relayoutCallback = function(scene) {
1 change: 1 addition & 0 deletions src/plots/plots.js
Original file line number Diff line number Diff line change
@@ -682,6 +682,7 @@ plots.cleanPlot = function(newFullData, newFullLayout, oldFullData, oldFullLayou
if(hadGl && !hasGl) {
if(oldFullLayout._glcontainer !== undefined) {
oldFullLayout._glcontainer.selectAll('.gl-canvas').remove();
oldFullLayout._glcontainer.selectAll('.no-webgl').remove();
oldFullLayout._glcanvas = null;
}
}
3 changes: 2 additions & 1 deletion src/traces/parcoords/plot.js
Original file line number Diff line number Diff line change
@@ -17,7 +17,8 @@ module.exports = function plot(gd, cdparcoords) {
var root = fullLayout._paperdiv;
var container = fullLayout._glcontainer;

prepareRegl(gd);
var success = prepareRegl(gd);
if(!success) return;

var gdDimensions = {};
var gdDimensionsOriginalOrder = {};
21 changes: 16 additions & 5 deletions src/traces/scattergl/index.js
Original file line number Diff line number Diff line change
@@ -166,7 +166,7 @@ function sceneUpdate(gd, subplot) {
var scene = subplot._scene;
var fullLayout = gd._fullLayout;

var reset = {
var resetOpts = {
// number of traces in subplot, since scene:subplot → 1:1
count: 0,
// whether scene requires init hook in plot call (dirty plot call)
@@ -181,7 +181,7 @@ function sceneUpdate(gd, subplot) {
errorYOptions: []
};

var first = {
var initOpts = {
selectBatch: null,
unselectBatch: null,
// regl- component stubs, initialized in dirty plot call
@@ -193,7 +193,13 @@ function sceneUpdate(gd, subplot) {
};

if(!subplot._scene) {
scene = subplot._scene = Lib.extendFlat({}, reset, first);
scene = subplot._scene = {};

scene.init = function init() {
Lib.extendFlat(scene, initOpts, resetOpts);
};

scene.init();

// apply new option to all regl components (used on drag)
scene.update = function update(opt) {
@@ -306,7 +312,7 @@ function sceneUpdate(gd, subplot) {

// In case if we have scene from the last calc - reset data
if(!scene.dirty) {
Lib.extendFlat(scene, reset);
Lib.extendFlat(scene, resetOpts);
}

return scene;
@@ -326,7 +332,12 @@ function plot(gd, subplot, cdata) {
var width = fullLayout.width;
var height = fullLayout.height;

prepareRegl(gd, ['ANGLE_instanced_arrays', 'OES_element_index_uint']);
var success = prepareRegl(gd, ['ANGLE_instanced_arrays', 'OES_element_index_uint']);
if(!success) {
scene.init();
return;
}

var regl = fullLayout._glcanvas.data()[0].regl;

// that is needed for fills
8 changes: 6 additions & 2 deletions src/traces/splom/base_plot.js
Original file line number Diff line number Diff line change
@@ -24,7 +24,8 @@ function plot(gd) {
var _module = Registry.getModule(SPLOM);
var splomCalcData = getModuleCalcData(gd.calcdata, _module)[0];

prepareRegl(gd, ['ANGLE_instanced_arrays', 'OES_element_index_uint']);
var success = prepareRegl(gd, ['ANGLE_instanced_arrays', 'OES_element_index_uint']);
if(!success) return;

if(fullLayout._hasOnlyLargeSploms) {
drawGrid(gd);
@@ -209,7 +210,10 @@ function clean(newFullData, newFullLayout, oldFullData, oldFullLayout, oldCalcda
var trace = cd0.trace;
var scene = cd0.t._scene;

if(trace.type === 'splom' && scene && scene.matrix) {
if(
trace.type === 'splom' &&
scene && scene.matrix && scene.matrix.destroy
) {
scene.matrix.destroy();
cd0.t._scene = null;
}
122 changes: 122 additions & 0 deletions test/jasmine/bundle_tests/no_webgl_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
var Plotly = require('@lib');

var createGraphDiv = require('../assets/create_graph_div');
var destroyGraphDiv = require('../assets/destroy_graph_div');
var failTest = require('../assets/fail_test');

describe('Plotly w/o WebGL support:', function() {
var gd;

beforeEach(function() {
gd = createGraphDiv();
});

afterEach(function() {
Plotly.purge(gd);
destroyGraphDiv();
});

function checkNoWebGLMsg(visible) {
var msg = gd.querySelector('div.no-webgl > p');
if(visible) {
expect(msg.innerHTML.substr(0, 22)).toBe('WebGL is not supported');
} else {
expect(msg).toBe(null);
}
}

it('gl3d subplots', function(done) {
Plotly.react(gd, require('@mocks/gl3d_autocolorscale.json'))
.then(function() {
checkNoWebGLMsg(true);
return Plotly.react(gd, require('@mocks/10.json'));
})
.then(function() {
checkNoWebGLMsg(false);
})
.catch(failTest)
.then(done);
});

it('gl2d subplots', function(done) {
Plotly.react(gd, require('@mocks/gl2d_pointcloud-basic.json'))
.then(function() {
checkNoWebGLMsg(true);
return Plotly.react(gd, require('@mocks/10.json'));
})
.then(function() {
checkNoWebGLMsg(false);
})
.catch(failTest)
.then(done);
});

it('scattergl subplots', function(done) {
Plotly.react(gd, require('@mocks/gl2d_12.json'))
.then(function() {
checkNoWebGLMsg(true);
return Plotly.react(gd, require('@mocks/10.json'));
})
.then(function() {
checkNoWebGLMsg(false);

// one with all regl2d modules
return Plotly.react(gd, [{
type: 'scattergl',
mode: 'lines+markers',
fill: 'tozerox',
y: [1, 2, 1],
error_x: { value: 10 },
error_y: { value: 10 }
}]);
})
.then(function() {
checkNoWebGLMsg(true);
return Plotly.react(gd, require('@mocks/10.json'));
})
.then(function() {
checkNoWebGLMsg(false);
})
.catch(failTest)
.then(done);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this gives:

peek 2018-06-05 17-26

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new stylez

peek 2018-06-06 15-10

});

it('scatterpolargl subplots', function(done) {
Plotly.react(gd, require('@mocks/glpolar_scatter.json'))
.then(function() {
checkNoWebGLMsg(true);
return Plotly.react(gd, require('@mocks/10.json'));
})
.then(function() {
checkNoWebGLMsg(false);
})
.catch(failTest)
.then(done);
});

it('splom subplots', function(done) {
Plotly.react(gd, require('@mocks/splom_0.json'))
.then(function() {
checkNoWebGLMsg(true);
return Plotly.react(gd, require('@mocks/10.json'));
})
.then(function() {
checkNoWebGLMsg(false);
})
.catch(failTest)
.then(done);
});

it('parcoords subplots', function(done) {
Plotly.react(gd, require('@mocks/gl2d_parcoords_2.json'))
.then(function() {
checkNoWebGLMsg(true);
return Plotly.react(gd, require('@mocks/10.json'));
})
.then(function() {
checkNoWebGLMsg(false);
})
.catch(failTest)
.then(done);
});
});
3 changes: 2 additions & 1 deletion test/jasmine/karma.conf.js
Original file line number Diff line number Diff line change
@@ -188,7 +188,8 @@ func.defaultConfig = {
flags: [
'--touch-events',
'--window-size=' + argv.width + ',' + argv.height,
isCI ? '--ignore-gpu-blacklist' : ''
isCI ? '--ignore-gpu-blacklist' : '',
(isBundleTest && basename(testFileGlob) === 'no_webgl') ? '--disable-webgl' : ''
]
},
_Firefox: {