diff --git a/src/ol/webgl/map.js b/src/ol/webgl/map.js index 1a1903a355..a0340c7b65 100644 --- a/src/ol/webgl/map.js +++ b/src/ol/webgl/map.js @@ -1,16 +1,26 @@ goog.provide('ol.webgl.Map'); +goog.require('goog.dispose'); goog.require('goog.dom'); goog.require('goog.dom.TagName'); goog.require('goog.style'); goog.require('goog.webgl'); goog.require('ol.Layer'); goog.require('ol.Map'); -goog.require('ol.TileStore'); +goog.require('ol.TileLayer'); goog.require('ol.webgl.IGLObject'); goog.require('ol.webgl.TileLayerRenderer'); +/** + * @enum {string} + */ +ol.webgl.WebGLContextEventType = { + LOST: 'webglcontextlost', + RESTORED: 'webglcontextrestored' +}; + + /** * @constructor @@ -37,24 +47,26 @@ ol.webgl.Map = function(target, opt_values) { * @private * @type {WebGLRenderingContext} */ - this.gl_ = null; - - /** @type {WebGLRenderingContext} */ - var gl = this.canvas_.getContext('experimental-webgl', { + this.gl_ = this.canvas_.getContext('experimental-webgl', { alpha: false, antialias: true, depth: false, preserveDrawingBuffer: false, stencil: false }); - goog.asserts.assert(!goog.isNull(gl)); - this.setGL(gl); + goog.asserts.assert(!goog.isNull(this.gl_)); + + goog.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.LOST, + this.handleWebGLContextLost, false, this); + goog.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.RESTORED, + this.handleWebGLContextRestored, false, this); if (goog.isDef(opt_values)) { this.setValues(opt_values); } this.handleViewportResize(); + this.handleWebGLContextRestored(); }; goog.inherits(ol.webgl.Map, ol.Map); @@ -64,9 +76,12 @@ goog.inherits(ol.webgl.Map, ol.Map); * @inheritDoc */ ol.webgl.Map.prototype.createLayerRenderer = function(layer) { - var store = layer.getStore(); - if (store instanceof ol.TileStore) { - return new ol.webgl.TileLayerRenderer(layer, this.getGL()); + var gl = this.getGL(); + if (gl.isContextLost()) { + return null; + } + if (layer instanceof ol.TileLayer) { + return new ol.webgl.TileLayerRenderer(this, layer); } else { goog.asserts.assert(false); return null; @@ -78,7 +93,8 @@ ol.webgl.Map.prototype.createLayerRenderer = function(layer) { * @inheritDoc */ ol.webgl.Map.prototype.disposeInternal = function() { - this.setGL(null); + this.forEachLayerRenderer(goog.dispose); + this.layerRenderers = {}; goog.base(this, 'disposeInternal'); }; @@ -152,6 +168,31 @@ ol.webgl.Map.prototype.handleSizeChanged = function() { }; +/** + * @param {goog.events.Event} event Event. + * @protected + */ +ol.webgl.Map.prototype.handleWebGLContextLost = function(event) { + event.preventDefault(); + this.forEachLayerRenderer(goog.dispose); + this.layerRenderers = {}; +}; + + +/** + * @protected + */ +ol.webgl.Map.prototype.handleWebGLContextRestored = function() { + var layers = this.getLayers(); + layers.forEach(function(layer) { + var layerRenderer = this.createLayerRenderer(layer); + var key = goog.getUid(layer); + goog.asserts.assert(!(key in this.layerRenderers)); + this.layerRenderers[key] = layerRenderer; + }, this); +}; + + /** * @private */ diff --git a/src/ol/webgl/tilelayerrenderer.js b/src/ol/webgl/tilelayerrenderer.js index dbdec85aa3..a5c9678f45 100644 --- a/src/ol/webgl/tilelayerrenderer.js +++ b/src/ol/webgl/tilelayerrenderer.js @@ -1,32 +1,109 @@ goog.provide('ol.webgl.TileLayerRenderer'); +goog.require('goog.asserts'); goog.require('goog.events.EventType'); -goog.require('ol.LayerRenderer'); -goog.require('ol.webgl.IGLObject'); +goog.require('goog.webgl'); +goog.require('ol.TileLayer'); +goog.require('ol.webgl.LayerRenderer'); /** * @constructor - * @extends {ol.LayerRenderer} - * @implements {ol.webgl.IGLObject} - * @param {ol.Layer} layer Layer. - * @param {WebGLRenderingContext} gl GL. + * @extends {ol.webgl.LayerRenderer} + * @param {ol.webgl.Map} map Map. + * @param {ol.TileLayer} tileLayer Tile layer. */ -ol.webgl.TileLayerRenderer = function(layer, gl) { +ol.webgl.TileLayerRenderer = function(map, tileLayer) { - goog.base(this, layer); + goog.base(this, map, tileLayer); /** - * @type {WebGLRenderingContext} + * @type {goog.math.Size} * @private */ - this.gl_ = null; + this.size_ = null; - this.setGL(gl); + /** + * @type {WebGLTexture} + * @private + */ + this.texture_ = null; + + /** + * @type {WebGLRenderbuffer} + * @private + */ + this.renderbuffer_ = null; + + /** + * @type {WebGLFramebuffer} + * @private + */ + this.framebuffer_ = null; + + /** + * @type {goog.math.Size} + * @private + */ + this.framebufferSize_ = null; + +}; +goog.inherits(ol.webgl.TileLayerRenderer, ol.webgl.LayerRenderer); + + +/** + * @private + */ +ol.webgl.TileLayerRenderer.prototype.bindFramebuffer_ = function() { + + var gl = this.getGL(); + + goog.asserts.assert(!goog.isNull(this.size_)); + + if (!goog.isNull(this.framebufferSize_) && + goog.math.Size.equals(this.framebufferSize_, this.size_)) { + gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, this.framebuffer_); + } else { + + var size = this.size_; + + var texture = gl.createTexture(); + gl.bindTexture(goog.webgl.TEXTURE_2D, texture); + gl.texImage2D(goog.webgl.TEXTURE_2D, 0, gl.RGBA, size.width, size.height, + 0, goog.webgl.RGBA, goog.webgl.UNSIGNED_BYTE, null); + gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, + goog.webgl.NEAREST); + gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MIN_FILTER, + goog.webgl.NEAREST); + gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_S, + goog.webgl.REPEAT); + gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_T, + goog.webgl.REPEAT); + + var renderbuffer = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); + gl.renderbufferStorage( + gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, size.width, size.height); + + var framebuffer = gl.createFramebuffer(); + gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, framebuffer); + gl.framebufferTexture2D(goog.webgl.FRAMEBUFFER, + goog.webgl.COLOR_ATTACHMENT0, goog.webgl.TEXTURE_2D, texture, 0); + gl.framebufferRenderbuffer(goog.webgl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, + gl.RENDERBUFFER, renderbuffer); + + gl.deleteFramebuffer(this.framebuffer_); + gl.deleteRenderbuffer(this.renderbuffer_); + gl.deleteTexture(this.texture_); + + this.texture_ = texture; + this.renderbuffer_ = renderbuffer; + this.framebuffer_ = framebuffer; + + } }; -goog.inherits(ol.webgl.TileLayerRenderer, ol.LayerRenderer); /** @@ -38,18 +115,23 @@ ol.webgl.TileLayerRenderer.prototype.dispatchChangeEvent = function() { /** - * @inheritDoc + * @protected */ -ol.webgl.TileLayerRenderer.prototype.getGL = function() { - return this.gl_; +ol.webgl.TileLayerRenderer.prototype.disposeInternal = function() { + var gl = this.getGL(); + if (!gl.isContextLost()) { + gl.deleteFramebuffer(this.framebuffer_); + gl.deleteRenderbuffer(this.renderbuffer_); + gl.deleteTexture(this.texture_); + } + goog.base(this, 'disposeInternal'); }; /** * @inheritDoc */ -ol.webgl.TileLayerRenderer.prototype.handleLayerOpacityChange = - function() { +ol.webgl.TileLayerRenderer.prototype.handleLayerOpacityChange = function() { this.dispatchChangeEvent(); }; @@ -57,15 +139,15 @@ ol.webgl.TileLayerRenderer.prototype.handleLayerOpacityChange = /** * @inheritDoc */ -ol.webgl.TileLayerRenderer.prototype.handleLayerVisibleChange = - function() { +ol.webgl.TileLayerRenderer.prototype.handleLayerVisibleChange = function() { this.dispatchChangeEvent(); }; /** - * @inheritDoc + * @param {goog.math.Size} size Size. + * @private */ -ol.webgl.TileLayerRenderer.prototype.setGL = function(gl) { - this.gl_ = gl; +ol.webgl.TileLayerRenderer.prototype.setSize_ = function(size) { + this.size_ = size; };