diff --git a/src/ol/layer/Layer.js b/src/ol/layer/Layer.js index 24ca2ae8d0..31e442e82b 100644 --- a/src/ol/layer/Layer.js +++ b/src/ol/layer/Layer.js @@ -348,6 +348,11 @@ class Layer extends BaseLayer { * Clean up. */ disposeInternal() { + if (this.renderer_) { + this.renderer_.dispose(); + delete this.renderer_; + } + this.setSource(null); super.disposeInternal(); } diff --git a/src/ol/layer/WebGLTile.js b/src/ol/layer/WebGLTile.js index 04ad68e0a0..51d15fe80d 100644 --- a/src/ol/layer/WebGLTile.js +++ b/src/ol/layer/WebGLTile.js @@ -255,6 +255,9 @@ function parseStyle(style, bandCount) { * property on the layer object; for example, setting `title: 'My Title'` in the * options means that `title` is observable, and has get/set accessors. * + * **Important**: after removing a `WebGLTile` layer from your map, call `layer.dispose()` + * to clean up underlying resources. + * * @extends BaseTileLayer * @api */ @@ -320,4 +323,11 @@ class WebGLTileLayer extends BaseTileLayer { } } +/** + * Clean up underlying WebGL resources. + * @function + * @api + */ +WebGLTileLayer.prototype.dispose; + export default WebGLTileLayer; diff --git a/src/ol/renderer/webgl/Layer.js b/src/ol/renderer/webgl/Layer.js index 59822fe1df..a492f9f11c 100644 --- a/src/ol/renderer/webgl/Layer.js +++ b/src/ol/renderer/webgl/Layer.js @@ -77,6 +77,8 @@ class WebGLLayerRenderer extends LayerRenderer { */ disposeInternal() { this.helper.dispose(); + delete this.helper; + super.disposeInternal(); } diff --git a/src/ol/renderer/webgl/TileLayer.js b/src/ol/renderer/webgl/TileLayer.js index 3565df7117..81f8f77bb3 100644 --- a/src/ol/renderer/webgl/TileLayer.js +++ b/src/ol/renderer/webgl/TileLayer.js @@ -181,6 +181,11 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { this.indices_ = indices; const cacheSize = options.cacheSize !== undefined ? options.cacheSize : 512; + + /** + * @type {import("../../structs/LRUCache.js").default} + * @private + */ this.tileTextureCache_ = new LRUCache(cacheSize); this.renderedOpacity_ = NaN; @@ -533,6 +538,29 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { } return covered; } + + /** + * Clean up. + */ + disposeInternal() { + const helper = this.helper; + const gl = helper.getGL(); + + helper.deleteBuffer(this.indices_); + delete this.indices_; + + gl.deleteProgram(this.program_); + delete this.program_; + + const tileTextureCache = this.tileTextureCache_; + tileTextureCache.forEach(function (tileTexture) { + tileTexture.dispose(); + }); + tileTextureCache.clear(); + delete this.tileTextureCache_; + + super.disposeInternal(); + } } /** diff --git a/src/ol/webgl/Helper.js b/src/ol/webgl/Helper.js index 2cd4072e6c..0036feeaa8 100644 --- a/src/ol/webgl/Helper.js +++ b/src/ol/webgl/Helper.js @@ -442,6 +442,13 @@ class WebGLHelper extends Disposable { ContextEventType.RESTORED, this.boundHandleWebGLContextRestored_ ); + + const extension = this.gl_.getExtension('WEBGL_lose_context'); + if (extension) { + extension.loseContext(); + } + delete this.gl_; + delete this.canvas_; } /** diff --git a/test/browser/spec/ol/layer/webgltile.test.js b/test/browser/spec/ol/layer/WebGLTile.test.js similarity index 94% rename from test/browser/spec/ol/layer/webgltile.test.js rename to test/browser/spec/ol/layer/WebGLTile.test.js index edd5aab452..32cf772507 100644 --- a/test/browser/spec/ol/layer/webgltile.test.js +++ b/test/browser/spec/ol/layer/WebGLTile.test.js @@ -50,6 +50,15 @@ describe('ol/layer/WebGLTile', function () { document.body.removeChild(target); }); + describe('dispose()', () => { + it('calls dispose on the renderer', () => { + const renderer = layer.getRenderer(); + const spy = sinon.spy(renderer, 'dispose'); + layer.dispose(); + expect(spy.called).to.be(true); + }); + }); + it('creates fragment and vertex shaders', function () { const compileShaderSpy = sinon.spy(WebGLHelper.prototype, 'compileShader'); layer.createRenderer();