From 8ad07fa8de68151249c8cb315084fd7a82a18e54 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Fri, 13 Apr 2018 16:24:36 -0700 Subject: [PATCH 1/5] cleanup chart controllers --- src/plugins/plot/src/chart/MCTChartController.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/plot/src/chart/MCTChartController.js b/src/plugins/plot/src/chart/MCTChartController.js index 417e60c8b5..03e4b69a06 100644 --- a/src/plugins/plot/src/chart/MCTChartController.js +++ b/src/plugins/plot/src/chart/MCTChartController.js @@ -61,7 +61,7 @@ function ( this.alarmSets = []; this.offset = {}; this.config = $scope.config; - this.listenTo(this.$scope, '$destoy', this.destroy, this); + this.listenTo(this.$scope, '$destroy', this.destroy, this); this.draw = this.draw.bind(this); this.scheduleDraw = this.scheduleDraw.bind(this); this.seriesElements = new WeakMap(); From 1f86a5a131871999152543ca3cdd17b8a35964f9 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Fri, 13 Apr 2018 16:28:28 -0700 Subject: [PATCH 2/5] Fallback to 2d on context lost When webgl contexts are lost, fallback to 2d rendering context. Because it's not possible for a canvas to generate a different context after one has already been created, the canvas elements must be recreated from scratch, and event handlers must also be updated. This resolves https://github.com/nasa/openmct/issues/1976 --- .../plot/src/chart/MCTChartController.js | 20 ++++++++ .../plot/src/chart/MCTChartDirective.js | 1 + src/plugins/plot/src/draw/Draw2D.js | 11 ++++- src/plugins/plot/src/draw/DrawLoader.js | 14 +++++- src/plugins/plot/src/draw/DrawWebGL.js | 48 +++++++++++++++++-- .../plot/src/plot/MCTPlotController.js | 14 ++++++ 6 files changed, 102 insertions(+), 6 deletions(-) diff --git a/src/plugins/plot/src/chart/MCTChartController.js b/src/plugins/plot/src/chart/MCTChartController.js index 03e4b69a06..2fc73a8b59 100644 --- a/src/plugins/plot/src/chart/MCTChartController.js +++ b/src/plugins/plot/src/chart/MCTChartController.js @@ -197,9 +197,29 @@ function ( this.canvas = canvas; this.overlay = overlay; this.drawAPI = DrawLoader.getDrawAPI(canvas, overlay); + if (this.drawAPI) { + this.listenTo(this.drawAPI, 'error', this.fallbackToCanvas, this); + } return !!this.drawAPI; }; + MCTChartController.prototype.fallbackToCanvas = function () { + this.stopListening(this.drawAPI); + DrawLoader.releaseDrawAPI(this.drawAPI); + // Have to throw away the old canvas elements and replace with new + // canvas elements in order to get new drawing contexts. + var div = document.createElement('div'); + div.innerHTML = this.TEMPLATE; + var mainCanvas = div.querySelectorAll("canvas")[1]; + var overlayCanvas = div.querySelectorAll("canvas")[0]; + this.canvas.parentNode.replaceChild(mainCanvas, this.canvas); + this.canvas = mainCanvas; + this.overlay.parentNode.replaceChild(overlayCanvas, this.overlay); + this.overlay = overlayCanvas; + this.drawAPI = DrawLoader.getFallbackDrawAPI(this.canvas, this.overlay); + this.$scope.$emit('plot:reinitializeCanvas'); + }; + MCTChartController.prototype.removeChartElement = function (series) { var elements = this.seriesElements.get(series); diff --git a/src/plugins/plot/src/chart/MCTChartDirective.js b/src/plugins/plot/src/chart/MCTChartDirective.js index 5662465878..3f3574dc42 100644 --- a/src/plugins/plot/src/chart/MCTChartDirective.js +++ b/src/plugins/plot/src/chart/MCTChartDirective.js @@ -43,6 +43,7 @@ define([ restrict: "E", template: TEMPLATE, link: function ($scope, $element, attrs, ctrl) { + ctrl.TEMPLATE = TEMPLATE; var mainCanvas = $element.find("canvas")[1]; var overlayCanvas = $element.find("canvas")[0]; diff --git a/src/plugins/plot/src/draw/Draw2D.js b/src/plugins/plot/src/draw/Draw2D.js index a333529062..e5141345b3 100644 --- a/src/plugins/plot/src/draw/Draw2D.js +++ b/src/plugins/plot/src/draw/Draw2D.js @@ -22,9 +22,13 @@ define([ - + 'lodash', + 'EventEmitter', + '../lib/eventHelpers' ], function ( - + _, + EventEmitter, + eventHelpers ) { /** @@ -47,6 +51,9 @@ define([ } } + _.extend(Draw2D.prototype, EventEmitter.prototype); + eventHelpers.extend(Draw2D.prototype); + // Convert from logical to physical x coordinates Draw2D.prototype.x = function (v) { return ((v - this.origin[0]) / this.dimensions[0]) * this.width; diff --git a/src/plugins/plot/src/draw/DrawLoader.js b/src/plugins/plot/src/draw/DrawLoader.js index c4eb4e1fe6..bb1c3f61a1 100644 --- a/src/plugins/plot/src/draw/DrawLoader.js +++ b/src/plugins/plot/src/draw/DrawLoader.js @@ -35,7 +35,7 @@ define( ALLOCATIONS: [] }, { - MAX_INSTANCES: Number.MAX_INFINITY, + MAX_INSTANCES: Number.POSITIVE_INFINITY, API: Draw2D, ALLOCATIONS: [] } @@ -83,12 +83,24 @@ define( return api; }, + /** + * Returns a fallback draw api. + */ + getFallbackDrawAPI: function (canvas, overlay) { + var api = new CHARTS[1].API(canvas, overlay); + CHARTS[1].ALLOCATIONS.push(api); + return api; + }, + releaseDrawAPI: function (api) { CHARTS.forEach(function (CHART_TYPE) { if (api instanceof CHART_TYPE.API) { CHART_TYPE.ALLOCATIONS.splice(CHART_TYPE.ALLOCATIONS.indexOf(api), 1); } }); + if (api.destroy) { + api.destroy(); + } } }; } diff --git a/src/plugins/plot/src/draw/DrawWebGL.js b/src/plugins/plot/src/draw/DrawWebGL.js index 1e44a60a6a..2e145b0665 100644 --- a/src/plugins/plot/src/draw/DrawWebGL.js +++ b/src/plugins/plot/src/draw/DrawWebGL.js @@ -22,9 +22,13 @@ define([ - + 'lodash', + 'EventEmitter', + '../lib/eventHelpers' ], function ( - + _, + EventEmitter, + eventHelpers ) { // WebGL shader sources (for drawing plain colors) @@ -69,6 +73,21 @@ define([ throw new Error("WebGL unavailable."); } + this.initContext(); + + this.listenTo(this.canvas, "webglcontextlost", this.onContextLost, this); + } + + _.extend(DrawWebGL.prototype, EventEmitter.prototype); + eventHelpers.extend(DrawWebGL.prototype); + + DrawWebGL.prototype.onContextLost = function (event) { + this.emit('error'); + this.isContextLost = true; + this.destroy(); + }; + + DrawWebGL.prototype.initContext = function () { // Initialize shaders this.vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER); this.gl.shaderSource(this.vertexShader, VERTEX_SHADER); @@ -103,7 +122,12 @@ define([ // Enable blending, for smoothness this.gl.enable(this.gl.BLEND); this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA); - } + + }; + + DrawWebGL.prototype.destroy = function () { + this.stopListening(); + }; // Convert from logical to physical x coordinates DrawWebGL.prototype.x = function (v) { @@ -117,6 +141,9 @@ define([ }; DrawWebGL.prototype.doDraw = function (drawType, buf, color, points) { + if (this.isContextLost) { + return; + } this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); this.gl.bufferData(this.gl.ARRAY_BUFFER, buf, this.gl.DYNAMIC_DRAW); this.gl.vertexAttribPointer(this.aVertexPosition, 2, this.gl.FLOAT, false, 0, 0); @@ -125,6 +152,9 @@ define([ }; DrawWebGL.prototype.clear = function () { + if (this.isContextLost) { + return; + } this.height = this.canvas.height = this.canvas.offsetHeight; this.width = this.canvas.width = this.canvas.offsetWidth; this.overlay.height = this.overlay.offsetHeight; @@ -151,6 +181,9 @@ define([ DrawWebGL.prototype.setDimensions = function (dimensions, origin) { this.dimensions = dimensions; this.origin = origin; + if (this.isContextLost) { + return; + } if (dimensions && dimensions.length > 0 && origin && origin.length > 0) { this.gl.uniform2fv(this.uDimensions, dimensions); @@ -169,6 +202,9 @@ define([ * @param {number} points the number of points to draw */ DrawWebGL.prototype.drawLine = function (buf, color, points) { + if (this.isContextLost) { + return; + } this.doDraw(this.gl.LINE_STRIP, buf, color, points); }; @@ -177,6 +213,9 @@ define([ * */ DrawWebGL.prototype.drawPoints = function (buf, color, points, pointSize) { + if (this.isContextLost) { + return; + } this.gl.uniform1f(this.uPointSize, pointSize); this.doDraw(this.gl.POINTS, buf, color, points); }; @@ -191,6 +230,9 @@ define([ * is in the range of 0.0-1.0 */ DrawWebGL.prototype.drawSquare = function (min, max, color) { + if (this.isContextLost) { + return; + } this.doDraw(this.gl.TRIANGLE_FAN, new Float32Array( min.concat([min[0], max[1]]).concat(max).concat([max[0], min[1]]) ), color, 4); diff --git a/src/plugins/plot/src/plot/MCTPlotController.js b/src/plugins/plot/src/plot/MCTPlotController.js index 27268c62f2..3ce1b91cca 100644 --- a/src/plugins/plot/src/plot/MCTPlotController.js +++ b/src/plugins/plot/src/plot/MCTPlotController.js @@ -59,6 +59,19 @@ define([ eventHelpers.extend(MCTPlotController.prototype); + MCTPlotController.prototype.initCanvas = function () { + if (this.$canvas) { + this.stopListening(this.$canvas); + } + this.$canvas = this.$element.find('canvas'); + + this.listenTo(this.$canvas, 'mousemove', this.trackMousePosition, this); + this.listenTo(this.$canvas, 'mouseleave', this.untrackMousePosition, this); + this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this); + + this.watchForMarquee(); + } + MCTPlotController.prototype.initialize = function () { this.$canvas = this.$element.find('canvas'); @@ -82,6 +95,7 @@ define([ this.listenTo(this.$scope, '$destroy', this.destroy, this); this.listenTo(this.$scope, 'plot:tickWidth', this.onTickWidthChange, this); this.listenTo(this.$scope, 'plot:highlight:set', this.onPlotHighlightSet, this); + this.listenTo(this.$scope, 'plot:reinitializeCanvas', this.initCanvas, this); this.listenTo(this.config.xAxis, 'change:displayRange', this.onXAxisChange, this); this.listenTo(this.config.yAxis, 'change:displayRange', this.onYAxisChange, this); From a958055f502083769dba9db8aea7ac73ebb034b7 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Fri, 13 Apr 2018 16:46:10 -0700 Subject: [PATCH 3/5] Remove alarm set when removing series Remove alarm marker sets when removing series, so that they don't continue to display. Fixes https://github.com/nasa/openmct/issues/1935 --- src/plugins/plot/src/chart/MCTChartController.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugins/plot/src/chart/MCTChartController.js b/src/plugins/plot/src/chart/MCTChartController.js index 2fc73a8b59..54ca0e239a 100644 --- a/src/plugins/plot/src/chart/MCTChartController.js +++ b/src/plugins/plot/src/chart/MCTChartController.js @@ -231,6 +231,10 @@ function ( this.pointSets.splice(this.pointSets.indexOf(pointSet), 1); pointSet.destroy(); }, this); + if (elements.alarmSet) { + elements.alarmSet.destroy(); + this.alarmSets.splice(this.alarmSets.indexOf(elements.alarmSet), 1); + } this.seriesElements.delete(series); }; From 7d8dc00996cf3b30c1727ef86db8ebd0d40abdfc Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Fri, 13 Apr 2018 16:52:27 -0700 Subject: [PATCH 4/5] Only listen on overlay plots Only listen for mutations on overlay plots, as other mutations should be ignored by plot configuration. Fixes https://github.com/nasa/openmct/issues/1945 --- .../src/configuration/PlotConfigurationModel.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/plugins/plot/src/configuration/PlotConfigurationModel.js b/src/plugins/plot/src/configuration/PlotConfigurationModel.js index e00614ca5b..fc8563b17c 100644 --- a/src/plugins/plot/src/configuration/PlotConfigurationModel.js +++ b/src/plugins/plot/src/configuration/PlotConfigurationModel.js @@ -75,11 +75,14 @@ define([ openmct: options.openmct }); - this.removeMutationListener = this.openmct.objects.observe( - this.get('domainObject'), - '*', - this.updateDomainObject.bind(this) - ); + if (this.get('domainObject').type === 'telemetry.plot.overlay') { + this.removeMutationListener = this.openmct.objects.observe( + this.get('domainObject'), + '*', + this.updateDomainObject.bind(this) + ); + } + this.yAxis.listenToSeriesCollection(this.series); this.legend.listenToSeriesCollection(this.series); @@ -112,7 +115,9 @@ define([ this.yAxis.destroy(); this.series.destroy(); this.legend.destroy(); - this.removeMutationListener(); + if (this.removeMutationListener) { + this.removeMutationListener(); + } }, /** * Return defaults, which are extracted from the passed in domain From 1326dae27a1436b2f192fc1b7d04eb15bee21f3d Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Fri, 13 Apr 2018 16:54:18 -0700 Subject: [PATCH 5/5] Fix style --- src/plugins/plot/src/plot/MCTPlotController.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/plot/src/plot/MCTPlotController.js b/src/plugins/plot/src/plot/MCTPlotController.js index 3ce1b91cca..d050bd2b6b 100644 --- a/src/plugins/plot/src/plot/MCTPlotController.js +++ b/src/plugins/plot/src/plot/MCTPlotController.js @@ -70,7 +70,7 @@ define([ this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this); this.watchForMarquee(); - } + }; MCTPlotController.prototype.initialize = function () { this.$canvas = this.$element.find('canvas');