From 24afe771be78bfaa055d44d7eeb64ae0a07a05a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Fri, 7 Sep 2018 18:07:08 -0400 Subject: [PATCH 1/6] emit 'plotly_webglcontextlost' event on webgl context lost - with event data containing the original event and the canvas 'key' to determine which canvas lost its context. --- src/lib/prepare_regl.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/lib/prepare_regl.js b/src/lib/prepare_regl.js index 13931e4eaa4..dbeda83bb90 100644 --- a/src/lib/prepare_regl.js +++ b/src/lib/prepare_regl.js @@ -48,6 +48,15 @@ module.exports = function prepareRegl(gd, extensions) { } catch(e) { success = false; } + + if(success) { + this.addEventListener('webglcontextlost', function(event) { + gd.emit('plotly_webglcontextlost', { + event: event, + layer: d.key + }); + }, false); + } }); if(!success) { From 23647bdd53e861dd95e5ff30ea5bb0cb1af842e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Mon, 10 Sep 2018 12:09:19 -0400 Subject: [PATCH 2/6] guard against cases where gd.emit got purged ... before native 'webgl_contextlost' is triggered. --- src/lib/prepare_regl.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib/prepare_regl.js b/src/lib/prepare_regl.js index dbeda83bb90..d262a510aa9 100644 --- a/src/lib/prepare_regl.js +++ b/src/lib/prepare_regl.js @@ -51,10 +51,12 @@ module.exports = function prepareRegl(gd, extensions) { if(success) { this.addEventListener('webglcontextlost', function(event) { - gd.emit('plotly_webglcontextlost', { - event: event, - layer: d.key - }); + if(gd && gd.emit) { + gd.emit('plotly_webglcontextlost', { + event: event, + layer: d.key + }); + } }, false); } }); From 4265baba0818ea8d986cc445063197f09d5e698f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Mon, 10 Sep 2018 15:19:56 -0400 Subject: [PATCH 3/6] emit 'plotly_webglcontextlost' for gl3d canvas --- src/plots/gl3d/scene.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/plots/gl3d/scene.js b/src/plots/gl3d/scene.js index 2a3c9a9d489..99dc45152ec 100644 --- a/src/plots/gl3d/scene.js +++ b/src/plots/gl3d/scene.js @@ -170,6 +170,8 @@ function render(scene) { } function initializeGLPlot(scene, fullLayout, canvas, gl) { + var gd = scene.graphDiv; + var glplotOptions = { canvas: canvas, gl: gl, @@ -220,7 +222,7 @@ function initializeGLPlot(scene, fullLayout, canvas, gl) { var update = {}; update[scene.id + '.camera'] = getLayoutCamera(scene.camera); - scene.saveCamera(scene.graphDiv.layout); + scene.saveCamera(gd.layout); scene.graphDiv.emit('plotly_relayout', update); }; @@ -228,10 +230,14 @@ function initializeGLPlot(scene, fullLayout, canvas, gl) { scene.glplot.canvas.addEventListener('wheel', relayoutCallback.bind(null, scene), passiveSupported ? {passive: false} : false); if(!scene.staticMode) { - scene.glplot.canvas.addEventListener('webglcontextlost', function(ev) { - Lib.warn('Lost WebGL context.'); - ev.preventDefault(); - }); + scene.glplot.canvas.addEventListener('webglcontextlost', function(event) { + if(gd && gd.emit) { + gd.emit('plotly_webglcontextlost', { + event: event, + layer: scene.id + }); + } + }, false); } if(!scene.camera) { From 7e282c37fb082f6f7b42dbfda437ffdeeaa8a7dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Mon, 10 Sep 2018 16:40:49 -0400 Subject: [PATCH 4/6] add gl2d and gl3d plotly_webglcontextlost evt handler tests --- test/jasmine/tests/gl2d_plot_interact_test.js | 21 +++++++++++++++++++ test/jasmine/tests/gl3d_plot_interact_test.js | 21 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/test/jasmine/tests/gl2d_plot_interact_test.js b/test/jasmine/tests/gl2d_plot_interact_test.js index 2982a653529..cb8594636f3 100644 --- a/test/jasmine/tests/gl2d_plot_interact_test.js +++ b/test/jasmine/tests/gl2d_plot_interact_test.js @@ -214,6 +214,27 @@ describe('Test gl plot side effects', function() { .catch(failTest) .then(done); }); + + it('@gl should fire *plotly_webglcontextlost* when on webgl context lost', function(done) { + var _mock = Lib.extendDeep({}, require('@mocks/gl2d_12.json')); + + Plotly.plot(gd, _mock).then(function() { + return new Promise(function(resolve, reject) { + gd.on('plotly_webglcontextlost', resolve); + setTimeout(reject, 10); + + var ev = new window.WebGLContextEvent('webglcontextlost'); + var canvas = gd.querySelector('.gl-canvas-context'); + canvas.dispatchEvent(ev); + }); + }) + .then(function(eventData) { + expect((eventData || {}).event).toBeDefined(); + expect((eventData || {}).layer).toBe('contextLayer'); + }) + .catch(failTest) + .then(done); + }); }); describe('Test gl2d plots', function() { diff --git a/test/jasmine/tests/gl3d_plot_interact_test.js b/test/jasmine/tests/gl3d_plot_interact_test.js index 64b22e447bc..89d00c3ac11 100644 --- a/test/jasmine/tests/gl3d_plot_interact_test.js +++ b/test/jasmine/tests/gl3d_plot_interact_test.js @@ -1425,4 +1425,25 @@ describe('Test removal of gl contexts', function() { }) .then(done); }); + + it('@gl should fire *plotly_webglcontextlost* when on webgl context lost', function(done) { + var _mock = Lib.extendDeep({}, require('@mocks/gl3d_marker-arrays.json')); + + Plotly.plot(gd, _mock).then(function() { + return new Promise(function(resolve, reject) { + gd.on('plotly_webglcontextlost', resolve); + setTimeout(reject, 10); + + var ev = new window.WebGLContextEvent('webglcontextlost'); + var canvas = gd.querySelector('div#scene > canvas'); + canvas.dispatchEvent(ev); + }); + }) + .then(function(eventData) { + expect((eventData || {}).event).toBeDefined(); + expect((eventData || {}).layer).toBe('scene'); + }) + .catch(failTest) + .then(done); + }); }); From 905956a62560d92cfc5b7b92e93b1b9cb80da546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Mon, 10 Sep 2018 19:31:56 -0400 Subject: [PATCH 5/6] improve plotly_webglcontextlost tests - test gl2d 'focus' canvas - test the 3 parcoords canvases! --- test/jasmine/tests/gl2d_plot_interact_test.js | 16 +++++++++- test/jasmine/tests/parcoords_test.js | 30 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/test/jasmine/tests/gl2d_plot_interact_test.js b/test/jasmine/tests/gl2d_plot_interact_test.js index cb8594636f3..b3a82467415 100644 --- a/test/jasmine/tests/gl2d_plot_interact_test.js +++ b/test/jasmine/tests/gl2d_plot_interact_test.js @@ -220,7 +220,7 @@ describe('Test gl plot side effects', function() { Plotly.plot(gd, _mock).then(function() { return new Promise(function(resolve, reject) { - gd.on('plotly_webglcontextlost', resolve); + gd.once('plotly_webglcontextlost', resolve); setTimeout(reject, 10); var ev = new window.WebGLContextEvent('webglcontextlost'); @@ -232,6 +232,20 @@ describe('Test gl plot side effects', function() { expect((eventData || {}).event).toBeDefined(); expect((eventData || {}).layer).toBe('contextLayer'); }) + .then(function() { + return new Promise(function(resolve, reject) { + gd.once('plotly_webglcontextlost', resolve); + setTimeout(reject, 10); + + var ev = new window.WebGLContextEvent('webglcontextlost'); + var canvas = gd.querySelector('.gl-canvas-focus'); + canvas.dispatchEvent(ev); + }); + }) + .then(function(eventData) { + expect((eventData || {}).event).toBeDefined(); + expect((eventData || {}).layer).toBe('focusLayer'); + }) .catch(failTest) .then(done); }); diff --git a/test/jasmine/tests/parcoords_test.js b/test/jasmine/tests/parcoords_test.js index fe05a10bf67..d1dea69c844 100644 --- a/test/jasmine/tests/parcoords_test.js +++ b/test/jasmine/tests/parcoords_test.js @@ -1052,6 +1052,36 @@ describe('parcoords basic use', function() { .catch(failTest) .then(done); }); + + it('@gl should fire *plotly_webglcontextlost* when on webgl context lost', function() { + var eventData; + var cnt = 0; + gd.on('plotly_webglcontextlost', function(d) { + eventData = d; + cnt++; + }); + + function trigger(name) { + var ev = new window.WebGLContextEvent('webglcontextlost'); + var canvas = gd.querySelector('.gl-canvas-' + name); + canvas.dispatchEvent(ev); + } + + function _assert(d, c) { + expect((eventData || {}).event).toBeDefined(); + expect((eventData || {}).layer).toBe(d); + expect(cnt).toBe(c); + } + + trigger('context'); + _assert('contextLayer', 1); + + trigger('focus'); + _assert('focusLayer', 2); + + trigger('pick'); + _assert('pickLayer', 3); + }); }); describe('@noCI parcoords constraint interactions', function() { From 3a38d739b7e33415e883f1599f062cd7c3163f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 11 Sep 2018 10:06:10 -0400 Subject: [PATCH 6/6] test 'pick' canvas non-events on gl2d graphs --- test/jasmine/tests/gl2d_plot_interact_test.js | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/test/jasmine/tests/gl2d_plot_interact_test.js b/test/jasmine/tests/gl2d_plot_interact_test.js index b3a82467415..ecc46e7968b 100644 --- a/test/jasmine/tests/gl2d_plot_interact_test.js +++ b/test/jasmine/tests/gl2d_plot_interact_test.js @@ -218,14 +218,17 @@ describe('Test gl plot side effects', function() { it('@gl should fire *plotly_webglcontextlost* when on webgl context lost', function(done) { var _mock = Lib.extendDeep({}, require('@mocks/gl2d_12.json')); + function _trigger(name) { + var ev = new window.WebGLContextEvent('webglcontextlost'); + var canvas = gd.querySelector('.gl-canvas-' + name); + canvas.dispatchEvent(ev); + } + Plotly.plot(gd, _mock).then(function() { return new Promise(function(resolve, reject) { gd.once('plotly_webglcontextlost', resolve); setTimeout(reject, 10); - - var ev = new window.WebGLContextEvent('webglcontextlost'); - var canvas = gd.querySelector('.gl-canvas-context'); - canvas.dispatchEvent(ev); + _trigger('context'); }); }) .then(function(eventData) { @@ -236,16 +239,25 @@ describe('Test gl plot side effects', function() { return new Promise(function(resolve, reject) { gd.once('plotly_webglcontextlost', resolve); setTimeout(reject, 10); - - var ev = new window.WebGLContextEvent('webglcontextlost'); - var canvas = gd.querySelector('.gl-canvas-focus'); - canvas.dispatchEvent(ev); + _trigger('focus'); }); }) .then(function(eventData) { expect((eventData || {}).event).toBeDefined(); expect((eventData || {}).layer).toBe('focusLayer'); }) + .then(function() { + return new Promise(function(resolve, reject) { + gd.once('plotly_webglcontextlost', reject); + setTimeout(resolve, 10); + _trigger('pick'); + }); + }) + .then(function(eventData) { + // should add event listener on pick canvas which + // isn't used for scattergl traces + expect(eventData).toBeUndefined(); + }) .catch(failTest) .then(done); });