diff --git a/src/ol/layer/WebGLTile.js b/src/ol/layer/WebGLTile.js index 5aca739168..2cf948e0ed 100644 --- a/src/ol/layer/WebGLTile.js +++ b/src/ol/layer/WebGLTile.js @@ -392,6 +392,9 @@ class WebGLTileLayer extends BaseTileLayer { * @private */ handleSourceUpdate_() { + if (this.hasRenderer()) { + this.getRenderer().clearCache(); + } if (this.getSource()) { this.setStyle(this.style_); } diff --git a/src/ol/renderer/webgl/TileLayer.js b/src/ol/renderer/webgl/TileLayer.js index b71820b4dd..13940d6cf9 100644 --- a/src/ol/renderer/webgl/TileLayer.js +++ b/src/ol/renderer/webgl/TileLayer.js @@ -248,6 +248,12 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { * @type {import("../../Map.js").FrameState|null} */ this.frameState_ = null; + + /** + * @private + * @type {import("../../proj/Projection.js").default} + */ + this.projection_ = undefined; } /** @@ -300,6 +306,13 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { * @return {boolean} Layer is ready to be rendered. */ prepareFrameInternal(frameState) { + if (!this.projection_) { + this.projection_ = frameState.viewState.projection; + } else if (frameState.viewState.projection !== this.projection_) { + this.clearCache(); + this.projection_ = frameState.viewState.projection; + } + const layer = this.getLayer(); const source = layer.getRenderSource(); if (!source) { @@ -855,11 +868,15 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { return covered; } + clearCache() { + const tileTextureCache = this.tileTextureCache_; + tileTextureCache.forEach((tileTexture) => tileTexture.dispose()); + tileTextureCache.clear(); + } + removeHelper() { if (this.helper) { - const tileTextureCache = this.tileTextureCache_; - tileTextureCache.forEach((tileTexture) => tileTexture.dispose()); - tileTextureCache.clear(); + this.clearCache(); } super.removeHelper(); diff --git a/test/rendering/cases/webgl-data-tile-reset-source/expected.png b/test/rendering/cases/webgl-data-tile-reset-source/expected.png new file mode 100644 index 0000000000..890747aca6 Binary files /dev/null and b/test/rendering/cases/webgl-data-tile-reset-source/expected.png differ diff --git a/test/rendering/cases/webgl-data-tile-reset-source/main.js b/test/rendering/cases/webgl-data-tile-reset-source/main.js new file mode 100644 index 0000000000..bc217d77d6 --- /dev/null +++ b/test/rendering/cases/webgl-data-tile-reset-source/main.js @@ -0,0 +1,66 @@ +import DataTile from '../../../../src/ol/source/DataTile.js'; +import Map from '../../../../src/ol/Map.js'; +import TileLayer from '../../../../src/ol/layer/WebGLTile.js'; +import View from '../../../../src/ol/View.js'; + +const size = [81, 99]; + +const canvas = document.createElement('canvas'); +canvas.width = size[0]; +canvas.height = size[1]; + +const context = canvas.getContext('2d'); +context.strokeStyle = 'white'; +context.textAlign = 'center'; +const lineHeight = 16; +context.font = `${lineHeight}px sans-serif`; + +const sourceRed = new DataTile({ + loader: function (z, x, y) { + const halfWidth = size[0] / 2; + const halfHeight = size[1] / 2; + context.fillStyle = '#FF0000'; + context.fillRect(0, 0, size[0], size[1]); + context.fillStyle = 'white'; + context.fillText(`z: ${z}`, halfWidth, halfHeight - lineHeight); + context.fillText(`x: ${x}`, halfWidth, halfHeight); + context.fillText(`y: ${y}`, halfWidth, halfHeight + lineHeight); + context.strokeRect(0, 0, size[0], size[1]); + return context.getImageData(0, 0, size[0], size[1]).data; + }, + tileSize: size, +}); + +const sourceBlue = new DataTile({ + loader: function (z, x, y) { + const halfWidth = size[0] / 2; + const halfHeight = size[1] / 2; + context.fillStyle = '#00AAFF'; + context.fillRect(0, 0, size[0], size[1]); + context.fillStyle = 'white'; + context.fillText(`z: ${z}`, halfWidth, halfHeight - lineHeight); + context.fillText(`x: ${x}`, halfWidth, halfHeight); + context.fillText(`y: ${y}`, halfWidth, halfHeight + lineHeight); + context.strokeRect(0, 0, size[0], size[1]); + return context.getImageData(0, 0, size[0], size[1]).data; + }, + tileSize: size, +}); + +const layer = new TileLayer({ + source: sourceRed, +}); + +const map = new Map({ + target: 'map', + layers: [layer], + view: new View({ + center: [0, 0], + zoom: 4, + }), +}); + +map.once(`rendercomplete`, function () { + layer.setSource(sourceBlue); + render({tolerance: 0.03}); +}); diff --git a/test/rendering/cases/webgl-tile-reset-projection/expected.png b/test/rendering/cases/webgl-tile-reset-projection/expected.png new file mode 100644 index 0000000000..732e9096b1 Binary files /dev/null and b/test/rendering/cases/webgl-tile-reset-projection/expected.png differ diff --git a/test/rendering/cases/webgl-tile-reset-projection/main.js b/test/rendering/cases/webgl-tile-reset-projection/main.js new file mode 100644 index 0000000000..bcc823d9ee --- /dev/null +++ b/test/rendering/cases/webgl-tile-reset-projection/main.js @@ -0,0 +1,35 @@ +import Map from '../../../../src/ol/Map.js'; +import TileLayer from '../../../../src/ol/layer/WebGLTile.js'; +import View from '../../../../src/ol/View.js'; +import XYZ from '../../../../src/ol/source/XYZ.js'; + +const map = new Map({ + layers: [ + new TileLayer({ + source: new XYZ({ + minZoom: 0, + maxZoom: 0, + url: '/data/tiles/osm/{z}/{x}/{y}.png', + }), + }), + ], + target: 'map', + view: new View({ + projection: 'EPSG:4326', + center: [0, 0], + zoom: 0, + multiWorld: true, + }), +}); + +map.once('rendercomplete', function () { + map.setView( + new View({ + projection: 'EPSG:3857', + center: [0, 0], + zoom: 0, + multiWorld: true, + }) + ); + render({tolerance: 0.03}); +});