diff --git a/src/ol/renderer/webgl/TileLayer.js b/src/ol/renderer/webgl/TileLayer.js index e2622de247..9323554099 100644 --- a/src/ol/renderer/webgl/TileLayer.js +++ b/src/ol/renderer/webgl/TileLayer.js @@ -307,10 +307,10 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { /** * @param {import("../../PluggableMap.js").FrameState} frameState Frame state. * @param {import("../../extent.js").Extent} extent The extent to be rendered. - * @param {number} z The zoom level. + * @param {number} initialZ The zoom level. * @param {Object>} tileTexturesByZ The zoom level. */ - enqueueTiles(frameState, extent, z, tileTexturesByZ) { + enqueueTiles(frameState, extent, initialZ, tileTexturesByZ) { const viewState = frameState.viewState; const tileLayer = this.getLayer(); const tileSource = tileLayer.getRenderSource(); @@ -318,13 +318,6 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { const tilePixelRatio = tileSource.getTilePixelRatio(frameState.pixelRatio); const gutter = tileSource.getGutterForProjection(viewState.projection); - const tileTextureCache = this.tileTextureCache_; - const tileRange = tileGrid.getTileRangeForExtentAndZ( - extent, - z, - this.tempTileRange_ - ); - const tileSourceKey = getUid(tileSource); if (!(tileSourceKey in frameState.wantedTiles)) { frameState.wantedTiles[tileSourceKey] = {}; @@ -332,70 +325,80 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { const wantedTiles = frameState.wantedTiles[tileSourceKey]; - const tileResolution = tileGrid.getResolution(z); + const tileTextureCache = this.tileTextureCache_; + const minZ = Math.max( + initialZ - tileLayer.getPreload(), + tileGrid.getMinZoom(), + tileLayer.getMinZoom() + ); + for (let z = initialZ; z >= minZ; --z) { + const tileRange = tileGrid.getTileRangeForExtentAndZ( + extent, + z, + this.tempTileRange_ + ); - for (let x = tileRange.minX; x <= tileRange.maxX; ++x) { - for (let y = tileRange.minY; y <= tileRange.maxY; ++y) { - const tileCoord = createTileCoord(z, x, y, this.tempTileCoord_); - const cacheKey = getCacheKey(tileSource, tileCoord); + const tileResolution = tileGrid.getResolution(z); - /** - * @type {TileTexture} - */ - let tileTexture; + for (let x = tileRange.minX; x <= tileRange.maxX; ++x) { + for (let y = tileRange.minY; y <= tileRange.maxY; ++y) { + const tileCoord = createTileCoord(z, x, y, this.tempTileCoord_); + const cacheKey = getCacheKey(tileSource, tileCoord); - /** - * @type {import("../../webgl/TileTexture").TileType} - */ - let tile; + /** @type {TileTexture} */ + let tileTexture; - if (tileTextureCache.containsKey(cacheKey)) { - tileTexture = tileTextureCache.get(cacheKey); - tile = tileTexture.tile; - } - if (!tileTexture || tileTexture.tile.key !== tileSource.getKey()) { - tile = tileSource.getTile( - z, - x, - y, - frameState.pixelRatio, - viewState.projection - ); - if (!tileTexture) { - tileTexture = new TileTexture({ - tile: tile, - grid: tileGrid, - helper: this.helper, - tilePixelRatio: tilePixelRatio, - gutter: gutter, - }); - tileTextureCache.set(cacheKey, tileTexture); - } else { - if (this.isDrawableTile_(tile)) { - tileTexture.setTile(tile); + /** @type {import("../../webgl/TileTexture").TileType} */ + let tile; + + if (tileTextureCache.containsKey(cacheKey)) { + tileTexture = tileTextureCache.get(cacheKey); + tile = tileTexture.tile; + } + if (!tileTexture || tileTexture.tile.key !== tileSource.getKey()) { + tile = tileSource.getTile( + z, + x, + y, + frameState.pixelRatio, + viewState.projection + ); + if (!tileTexture) { + tileTexture = new TileTexture({ + tile: tile, + grid: tileGrid, + helper: this.helper, + tilePixelRatio: tilePixelRatio, + gutter: gutter, + }); + tileTextureCache.set(cacheKey, tileTexture); } else { - const interimTile = - /** @type {import("../../webgl/TileTexture").TileType} */ ( - tile.getInterimTile() - ); - tileTexture.setTile(interimTile); + if (this.isDrawableTile_(tile)) { + tileTexture.setTile(tile); + } else { + const interimTile = + /** @type {import("../../webgl/TileTexture").TileType} */ ( + tile.getInterimTile() + ); + tileTexture.setTile(interimTile); + } } } - } - addTileTextureToLookup(tileTexturesByZ, tileTexture, z); + addTileTextureToLookup(tileTexturesByZ, tileTexture, z); - const tileQueueKey = tile.getKey(); - wantedTiles[tileQueueKey] = true; + const tileQueueKey = tile.getKey(); + wantedTiles[tileQueueKey] = true; - if (tile.getState() === TileState.IDLE) { - if (!frameState.tileQueue.isKeyQueued(tileQueueKey)) { - frameState.tileQueue.enqueue([ - tile, - tileSourceKey, - tileGrid.getTileCoordCenter(tileCoord), - tileResolution, - ]); + if (tile.getState() === TileState.IDLE) { + if (!frameState.tileQueue.isKeyQueued(tileQueueKey)) { + frameState.tileQueue.enqueue([ + tile, + tileSourceKey, + tileGrid.getTileCoordCenter(tileCoord), + tileResolution, + ]); + } } } } diff --git a/test/browser/spec/ol/renderer/webgl/TileLayer.test.js b/test/browser/spec/ol/renderer/webgl/TileLayer.test.js index 887b20dab2..fc7da75410 100644 --- a/test/browser/spec/ol/renderer/webgl/TileLayer.test.js +++ b/test/browser/spec/ol/renderer/webgl/TileLayer.test.js @@ -6,6 +6,7 @@ import {VOID} from '../../../../../../src/ol/functions.js'; import {create} from '../../../../../../src/ol/transform.js'; import {createCanvasContext2D} from '../../../../../../src/ol/dom.js'; import {get} from '../../../../../../src/ol/proj.js'; +import {getUid} from '../../../../../../src/ol/util.js'; describe('ol/renderer/webgl/TileLayer', function () { /** @type {import("../../../../../../src/ol/renderer/webgl/TileLayer.js").default} */ @@ -105,4 +106,81 @@ describe('ol/renderer/webgl/TileLayer', function () { tileLayer.setUseInterimTilesOnError(true); expect(renderer.isDrawableTile_(errorTile)).to.be(false); }); + + describe('enqueueTiles()', () => { + it('enqueues tiles at a single zoom level (preload: 0)', () => { + renderer.prepareFrame(frameState); + const extent = [-1, -1, 1, 1]; + renderer.enqueueTiles(frameState, extent, 10, {}); + + const source = tileLayer.getSource(); + const sourceKey = getUid(source); + expect(frameState.wantedTiles[sourceKey]).to.be.an(Object); + + const wantedTiles = frameState.wantedTiles[sourceKey]; + + const expected = { + '/10,511,511': true, + '/10,511,512': true, + '/10,512,511': true, + '/10,512,512': true, + }; + expect(wantedTiles).to.eql(expected); + }); + + it('enqueues tiles at multiple zoom levels (preload: 2)', () => { + tileLayer.setPreload(2); + renderer.prepareFrame(frameState); + const extent = [-1, -1, 1, 1]; + renderer.enqueueTiles(frameState, extent, 10, {}); + + const source = tileLayer.getSource(); + const sourceKey = getUid(source); + expect(frameState.wantedTiles[sourceKey]).to.be.an(Object); + + const wantedTiles = frameState.wantedTiles[sourceKey]; + + const expected = { + '/10,511,511': true, + '/10,511,512': true, + '/10,512,511': true, + '/10,512,512': true, + '/9,255,255': true, + '/9,255,256': true, + '/9,256,255': true, + '/9,256,256': true, + '/8,127,127': true, + '/8,127,128': true, + '/8,128,127': true, + '/8,128,128': true, + }; + expect(wantedTiles).to.eql(expected); + }); + + it('does not go below layer min zoom', () => { + tileLayer.setPreload(Infinity); + tileLayer.setMinZoom(9); + renderer.prepareFrame(frameState); + const extent = [-1, -1, 1, 1]; + renderer.enqueueTiles(frameState, extent, 10, {}); + + const source = tileLayer.getSource(); + const sourceKey = getUid(source); + expect(frameState.wantedTiles[sourceKey]).to.be.an(Object); + + const wantedTiles = frameState.wantedTiles[sourceKey]; + + const expected = { + '/10,511,511': true, + '/10,511,512': true, + '/10,512,511': true, + '/10,512,512': true, + '/9,255,255': true, + '/9,255,256': true, + '/9,256,255': true, + '/9,256,256': true, + }; + expect(wantedTiles).to.eql(expected); + }); + }); });