mirror of
https://github.com/nasa/openmct.git
synced 2024-12-19 05:07:52 +00:00
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
This commit is contained in:
parent
8ad07fa8de
commit
1f86a5a131
@ -197,9 +197,29 @@ function (
|
|||||||
this.canvas = canvas;
|
this.canvas = canvas;
|
||||||
this.overlay = overlay;
|
this.overlay = overlay;
|
||||||
this.drawAPI = DrawLoader.getDrawAPI(canvas, overlay);
|
this.drawAPI = DrawLoader.getDrawAPI(canvas, overlay);
|
||||||
|
if (this.drawAPI) {
|
||||||
|
this.listenTo(this.drawAPI, 'error', this.fallbackToCanvas, this);
|
||||||
|
}
|
||||||
return !!this.drawAPI;
|
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) {
|
MCTChartController.prototype.removeChartElement = function (series) {
|
||||||
var elements = this.seriesElements.get(series);
|
var elements = this.seriesElements.get(series);
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ define([
|
|||||||
restrict: "E",
|
restrict: "E",
|
||||||
template: TEMPLATE,
|
template: TEMPLATE,
|
||||||
link: function ($scope, $element, attrs, ctrl) {
|
link: function ($scope, $element, attrs, ctrl) {
|
||||||
|
ctrl.TEMPLATE = TEMPLATE;
|
||||||
var mainCanvas = $element.find("canvas")[1];
|
var mainCanvas = $element.find("canvas")[1];
|
||||||
var overlayCanvas = $element.find("canvas")[0];
|
var overlayCanvas = $element.find("canvas")[0];
|
||||||
|
|
||||||
|
@ -22,9 +22,13 @@
|
|||||||
|
|
||||||
|
|
||||||
define([
|
define([
|
||||||
|
'lodash',
|
||||||
|
'EventEmitter',
|
||||||
|
'../lib/eventHelpers'
|
||||||
], function (
|
], function (
|
||||||
|
_,
|
||||||
|
EventEmitter,
|
||||||
|
eventHelpers
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,6 +51,9 @@ define([
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_.extend(Draw2D.prototype, EventEmitter.prototype);
|
||||||
|
eventHelpers.extend(Draw2D.prototype);
|
||||||
|
|
||||||
// Convert from logical to physical x coordinates
|
// Convert from logical to physical x coordinates
|
||||||
Draw2D.prototype.x = function (v) {
|
Draw2D.prototype.x = function (v) {
|
||||||
return ((v - this.origin[0]) / this.dimensions[0]) * this.width;
|
return ((v - this.origin[0]) / this.dimensions[0]) * this.width;
|
||||||
|
@ -35,7 +35,7 @@ define(
|
|||||||
ALLOCATIONS: []
|
ALLOCATIONS: []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
MAX_INSTANCES: Number.MAX_INFINITY,
|
MAX_INSTANCES: Number.POSITIVE_INFINITY,
|
||||||
API: Draw2D,
|
API: Draw2D,
|
||||||
ALLOCATIONS: []
|
ALLOCATIONS: []
|
||||||
}
|
}
|
||||||
@ -83,12 +83,24 @@ define(
|
|||||||
return api;
|
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) {
|
releaseDrawAPI: function (api) {
|
||||||
CHARTS.forEach(function (CHART_TYPE) {
|
CHARTS.forEach(function (CHART_TYPE) {
|
||||||
if (api instanceof CHART_TYPE.API) {
|
if (api instanceof CHART_TYPE.API) {
|
||||||
CHART_TYPE.ALLOCATIONS.splice(CHART_TYPE.ALLOCATIONS.indexOf(api), 1);
|
CHART_TYPE.ALLOCATIONS.splice(CHART_TYPE.ALLOCATIONS.indexOf(api), 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (api.destroy) {
|
||||||
|
api.destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -22,9 +22,13 @@
|
|||||||
|
|
||||||
|
|
||||||
define([
|
define([
|
||||||
|
'lodash',
|
||||||
|
'EventEmitter',
|
||||||
|
'../lib/eventHelpers'
|
||||||
], function (
|
], function (
|
||||||
|
_,
|
||||||
|
EventEmitter,
|
||||||
|
eventHelpers
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// WebGL shader sources (for drawing plain colors)
|
// WebGL shader sources (for drawing plain colors)
|
||||||
@ -69,6 +73,21 @@ define([
|
|||||||
throw new Error("WebGL unavailable.");
|
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
|
// Initialize shaders
|
||||||
this.vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
|
this.vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
|
||||||
this.gl.shaderSource(this.vertexShader, VERTEX_SHADER);
|
this.gl.shaderSource(this.vertexShader, VERTEX_SHADER);
|
||||||
@ -103,7 +122,12 @@ define([
|
|||||||
// Enable blending, for smoothness
|
// Enable blending, for smoothness
|
||||||
this.gl.enable(this.gl.BLEND);
|
this.gl.enable(this.gl.BLEND);
|
||||||
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
|
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
|
// Convert from logical to physical x coordinates
|
||||||
DrawWebGL.prototype.x = function (v) {
|
DrawWebGL.prototype.x = function (v) {
|
||||||
@ -117,6 +141,9 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
DrawWebGL.prototype.doDraw = function (drawType, buf, color, points) {
|
DrawWebGL.prototype.doDraw = function (drawType, buf, color, points) {
|
||||||
|
if (this.isContextLost) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer);
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer);
|
||||||
this.gl.bufferData(this.gl.ARRAY_BUFFER, buf, this.gl.DYNAMIC_DRAW);
|
this.gl.bufferData(this.gl.ARRAY_BUFFER, buf, this.gl.DYNAMIC_DRAW);
|
||||||
this.gl.vertexAttribPointer(this.aVertexPosition, 2, this.gl.FLOAT, false, 0, 0);
|
this.gl.vertexAttribPointer(this.aVertexPosition, 2, this.gl.FLOAT, false, 0, 0);
|
||||||
@ -125,6 +152,9 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
DrawWebGL.prototype.clear = function () {
|
DrawWebGL.prototype.clear = function () {
|
||||||
|
if (this.isContextLost) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.height = this.canvas.height = this.canvas.offsetHeight;
|
this.height = this.canvas.height = this.canvas.offsetHeight;
|
||||||
this.width = this.canvas.width = this.canvas.offsetWidth;
|
this.width = this.canvas.width = this.canvas.offsetWidth;
|
||||||
this.overlay.height = this.overlay.offsetHeight;
|
this.overlay.height = this.overlay.offsetHeight;
|
||||||
@ -151,6 +181,9 @@ define([
|
|||||||
DrawWebGL.prototype.setDimensions = function (dimensions, origin) {
|
DrawWebGL.prototype.setDimensions = function (dimensions, origin) {
|
||||||
this.dimensions = dimensions;
|
this.dimensions = dimensions;
|
||||||
this.origin = origin;
|
this.origin = origin;
|
||||||
|
if (this.isContextLost) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (dimensions && dimensions.length > 0 &&
|
if (dimensions && dimensions.length > 0 &&
|
||||||
origin && origin.length > 0) {
|
origin && origin.length > 0) {
|
||||||
this.gl.uniform2fv(this.uDimensions, dimensions);
|
this.gl.uniform2fv(this.uDimensions, dimensions);
|
||||||
@ -169,6 +202,9 @@ define([
|
|||||||
* @param {number} points the number of points to draw
|
* @param {number} points the number of points to draw
|
||||||
*/
|
*/
|
||||||
DrawWebGL.prototype.drawLine = function (buf, color, points) {
|
DrawWebGL.prototype.drawLine = function (buf, color, points) {
|
||||||
|
if (this.isContextLost) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.doDraw(this.gl.LINE_STRIP, buf, color, points);
|
this.doDraw(this.gl.LINE_STRIP, buf, color, points);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -177,6 +213,9 @@ define([
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
DrawWebGL.prototype.drawPoints = function (buf, color, points, pointSize) {
|
DrawWebGL.prototype.drawPoints = function (buf, color, points, pointSize) {
|
||||||
|
if (this.isContextLost) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.gl.uniform1f(this.uPointSize, pointSize);
|
this.gl.uniform1f(this.uPointSize, pointSize);
|
||||||
this.doDraw(this.gl.POINTS, buf, color, points);
|
this.doDraw(this.gl.POINTS, buf, color, points);
|
||||||
};
|
};
|
||||||
@ -191,6 +230,9 @@ define([
|
|||||||
* is in the range of 0.0-1.0
|
* is in the range of 0.0-1.0
|
||||||
*/
|
*/
|
||||||
DrawWebGL.prototype.drawSquare = function (min, max, color) {
|
DrawWebGL.prototype.drawSquare = function (min, max, color) {
|
||||||
|
if (this.isContextLost) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.doDraw(this.gl.TRIANGLE_FAN, new Float32Array(
|
this.doDraw(this.gl.TRIANGLE_FAN, new Float32Array(
|
||||||
min.concat([min[0], max[1]]).concat(max).concat([max[0], min[1]])
|
min.concat([min[0], max[1]]).concat(max).concat([max[0], min[1]])
|
||||||
), color, 4);
|
), color, 4);
|
||||||
|
@ -59,6 +59,19 @@ define([
|
|||||||
|
|
||||||
eventHelpers.extend(MCTPlotController.prototype);
|
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 () {
|
MCTPlotController.prototype.initialize = function () {
|
||||||
this.$canvas = this.$element.find('canvas');
|
this.$canvas = this.$element.find('canvas');
|
||||||
|
|
||||||
@ -82,6 +95,7 @@ define([
|
|||||||
this.listenTo(this.$scope, '$destroy', this.destroy, this);
|
this.listenTo(this.$scope, '$destroy', this.destroy, this);
|
||||||
this.listenTo(this.$scope, 'plot:tickWidth', this.onTickWidthChange, 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: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.xAxis, 'change:displayRange', this.onXAxisChange, this);
|
||||||
this.listenTo(this.config.yAxis, 'change:displayRange', this.onYAxisChange, this);
|
this.listenTo(this.config.yAxis, 'change:displayRange', this.onYAxisChange, this);
|
||||||
|
Loading…
Reference in New Issue
Block a user