Merge pull request #9133 from ahocevar/smart-tile-cache

Reduce memory footprint of tiles and labels
This commit is contained in:
Andreas Hocevar
2019-01-22 10:46:38 +01:00
committed by GitHub
23 changed files with 64 additions and 43 deletions

View File

@@ -47,7 +47,7 @@ import {create as createTransform, apply as applyTransform} from './transform.js
* @property {import("./size.js").Size} size
* @property {!Object<string, boolean>} skippedFeatureUids
* @property {TileQueue} tileQueue
* @property {Object<string, Object<string, import("./TileRange.js").default>>} usedTiles
* @property {!Object<string, Object<string, boolean>>} usedTiles
* @property {Array<number>} viewHints
* @property {!Object<string, Object<string, boolean>>} wantedTiles
*/

View File

@@ -21,8 +21,7 @@ class TileCache extends LRUCache {
expireCache(usedTiles) {
while (this.canExpireCache()) {
const tile = this.peekLast();
const zKey = tile.tileCoord[0].toString();
if (zKey in usedTiles && usedTiles[zKey].contains(tile.tileCoord)) {
if (tile.getKey() in usedTiles) {
break;
} else {
this.pop().dispose();

View File

@@ -103,6 +103,10 @@ class VectorRenderTile extends Tile {
*/
disposeInternal() {
this.removeSourceTiles_(this);
for (const key in this.context_) {
const canvas = this.context_[key].canvas;
canvas.width = canvas.height = 0;
}
this.setState(TileState.ABORT);
super.disposeInternal();
}

View File

@@ -169,6 +169,17 @@ export const defaultLineWidth = 1;
export const labelCache = new LRUCache();
/**
* Prune the label cache.
*/
export function pruneLabelCache() {
while (labelCache.canExpireCache()) {
const canvas = labelCache.pop();
canvas.width = canvas.height = 0;
}
}
/**
* @type {!Object<string, number>}
*/

View File

@@ -6,7 +6,7 @@ import {asColorLike} from '../../colorlike.js';
import {intersects} from '../../extent.js';
import {matchingChunk} from '../../geom/flat/straightchunk.js';
import GeometryType from '../../geom/GeometryType.js';
import {labelCache, defaultTextAlign, defaultPadding, defaultLineCap, defaultLineDashOffset, defaultLineDash, defaultLineJoin, defaultFillStyle, checkFont, defaultFont, defaultLineWidth, defaultMiterLimit, defaultStrokeStyle, defaultTextBaseline} from '../canvas.js';
import {pruneLabelCache, defaultTextAlign, defaultPadding, defaultLineCap, defaultLineDashOffset, defaultLineDash, defaultLineJoin, defaultFillStyle, checkFont, defaultFont, defaultLineWidth, defaultMiterLimit, defaultStrokeStyle, defaultTextBaseline} from '../canvas.js';
import CanvasInstruction from './Instruction.js';
import CanvasBuilder from './Builder.js';
import TextPlacement from '../../style/TextPlacement.js';
@@ -131,7 +131,7 @@ class CanvasTextBuilder extends CanvasBuilder {
*/
this.strokeKey_ = '';
labelCache.prune();
pruneLabelCache();
}
/**

View File

@@ -192,26 +192,18 @@ class LayerRenderer extends Observable {
}
/**
* @param {!Object<string, !Object<string, import("../TileRange.js").default>>} usedTiles Used tiles.
* @param {!Object<string, !Object<string, boolean>>} usedTiles Used tiles.
* @param {import("../source/Tile.js").default} tileSource Tile source.
* @param {number} z Z.
* @param {import("../TileRange.js").default} tileRange Tile range.
* @param {import('../Tile.js').default} tile Tile.
* @protected
*/
updateUsedTiles(usedTiles, tileSource, z, tileRange) {
updateUsedTiles(usedTiles, tileSource, tile) {
// FIXME should we use tilesToDrawByZ instead?
const tileSourceKey = getUid(tileSource);
const zKey = z.toString();
if (tileSourceKey in usedTiles) {
if (zKey in usedTiles[tileSourceKey]) {
usedTiles[tileSourceKey][zKey].extend(tileRange);
} else {
usedTiles[tileSourceKey][zKey] = tileRange;
}
} else {
if (!(tileSourceKey in usedTiles)) {
usedTiles[tileSourceKey] = {};
usedTiles[tileSourceKey][zKey] = tileRange;
}
usedTiles[tileSourceKey][tile.getKey()] = true;
}
/**

View File

@@ -283,6 +283,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
this.drawTileImage(tile, frameState, layerState, x, y, w, h, tileGutter, z === currentZ);
this.renderedTiles.push(tile);
this.updateUsedTiles(frameState.usedTiles, tileSource, tile);
}
}
@@ -291,7 +292,6 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
this.renderedResolution = tileResolution;
this.renderedExtent_ = canvasExtent;
this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange);
this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio,
projection, extent, z, tileLayer.getPreload());
this.scheduleExpireCache(frameState, tileSource);

View File

@@ -50,7 +50,7 @@ const TOS_ATTRIBUTION = '<a class="ol-attribution-bing-tos" ' +
/**
* @typedef {Object} Options
* @property {number} [cacheSize=2048] Cache size.
* @property {number} [cacheSize] Tile cache size. Default is twice as many tiles as a fullscreen map needs.
* @property {boolean} [hidpi=false] If `true` hidpi tiles will be requested.
* @property {string} [culture='en-us'] Culture code.
* @property {string} key Bing Maps API key. Get yours at http://www.bingmapsportal.com/.

View File

@@ -9,7 +9,7 @@ import XYZ from './XYZ.js';
/**
* @typedef {Object} Options
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
* @property {number} [cacheSize=2048] Cache size.
* @property {number} [cacheSize] Tile cache size. Default is twice as many tiles as a fullscreen map needs.
* @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that
* you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer.
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.

View File

@@ -20,7 +20,7 @@ export const ATTRIBUTION = '&#169; ' +
/**
* @typedef {Object} Options
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
* @property {number} [cacheSize=2048] Cache size.
* @property {number} [cacheSize] Tile cache size. Default is twice as many tiles as a fullscreen map needs.
* @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that
* you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer.
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.

View File

@@ -90,7 +90,7 @@ const ProviderConfig = {
/**
* @typedef {Object} Options
* @property {number} [cacheSize=2048] Cache size.
* @property {number} [cacheSize] Tile cache size. Default is twice as many tiles as a fullscreen map needs.
* @property {string} layer Layer name.
* @property {number} [minZoom] Minimum zoom.
* @property {number} [maxZoom] Maximum zoom.

View File

@@ -68,11 +68,23 @@ class TileSource extends Source {
*/
this.tileGrid = options.tileGrid !== undefined ? options.tileGrid : null;
let cacheSize = options.cacheSize;
if (cacheSize === undefined) {
const tileSize = [256, 256];
const tileGrid = options.tileGrid;
if (tileGrid) {
toSize(tileGrid.getTileSize(tileGrid.getMinZoom()), tileSize);
}
const width = screen ? (screen.availWidth || screen.width) : 1920;
const height = screen ? (screen.availHeight || screen.height) : 1080;
cacheSize = 2 * Math.ceil(width / tileSize[0]) * Math.ceil(height / tileSize[1]);
}
/**
* @protected
* @type {import("../TileCache.js").default}
*/
this.tileCache = new TileCache(options.cacheSize);
this.tileCache = new TileCache(cacheSize);
/**
* @protected

View File

@@ -13,7 +13,7 @@ import {appendParams} from '../uri.js';
/**
* @typedef {Object} Options
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
* @property {number} [cacheSize=2048] Cache size.
* @property {number} [cacheSize] Tile cache size. Default is twice as many tiles as a fullscreen map needs.
* @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that
* you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer.
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.

View File

@@ -18,7 +18,7 @@ import {getForProjection as getTileGridForProjection} from '../tilegrid.js';
* @typedef {Object} Options
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
* @property {boolean} [attributionsCollapsible=true] Attributions are collapsible.
* @property {number} [cacheSize=2048] Cache size.
* @property {number} [cacheSize] Tile cache size. Default is twice as many tiles as a fullscreen map needs.
* @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that
* you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer.
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.

View File

@@ -39,7 +39,7 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js';
/**
* @typedef {Object} Options
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
* @property {number} [cacheSize=2048] Cache size.
* @property {number} [cacheSize] Tile cache size. Default is twice as many tiles as a fullscreen map needs.
* @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that
* you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer.
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.

View File

@@ -20,7 +20,7 @@ import {appendParams} from '../uri.js';
/**
* @typedef {Object} Options
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
* @property {number} [cacheSize=2048] Cache size.
* @property {number} [cacheSize] Tile cache size. Default is twice as many tiles as a fullscreen map needs.
* @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that
* you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer.
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.

View File

@@ -96,7 +96,7 @@ class VectorTile extends UrlTile {
super({
attributions: options.attributions,
cacheSize: options.cacheSize !== undefined ? options.cacheSize : 128,
cacheSize: options.cacheSize,
opaque: false,
projection: projection,
state: options.state,

View File

@@ -15,7 +15,7 @@ import {appendParams} from '../uri.js';
/**
* @typedef {Object} Options
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
* @property {number} [cacheSize=2048] Cache size.
* @property {number} [cacheSize] Tile cache size. Default is twice as many tiles as a fullscreen map needs.
* @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that
* you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer.
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.

View File

@@ -9,7 +9,7 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js';
* @typedef {Object} Options
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
* @property {boolean} [attributionsCollapsible=true] Attributions are collapsible.
* @property {number} [cacheSize=2048] Cache size.
* @property {number} [cacheSize] Tile cache size. Default is twice as many tiles as a fullscreen map needs.
* @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that
* you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer.
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.

View File

@@ -82,7 +82,7 @@ export class CustomTile extends ImageTile {
/**
* @typedef {Object} Options
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
* @property {number} [cacheSize=2048] Cache size.
* @property {number} [cacheSize] Tile cache size. Default is twice as many tiles as a fullscreen map needs.
* @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that
* you must provide a `crossOrigin` value you want to access pixel data with the Canvas renderer.
* See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail.

View File

@@ -287,16 +287,6 @@ class LRUCache extends EventTarget {
this.highWaterMark = size;
}
/**
* Prune the cache.
*/
prune() {
while (this.canExpireCache()) {
this.pop();
}
}
}
export default LRUCache;

View File

@@ -10,6 +10,17 @@ describe('ol.render.canvas', function() {
font.rel = 'stylesheet';
const head = document.getElementsByTagName('head')[0];
it('pruneLabelCache()', function() {
const highWaterMark = render.labelCache.highWaterMark;
render.labelCache.highWaterMark = 1;
render.labelCache.set('foo', document.createElement('canvas'));
render.labelCache.set('bar', document.createElement('canvas'));
render.pruneLabelCache();
expect(render.labelCache.getCount()).to.be(1);
render.labelCache.highWaterMark = highWaterMark;
render.labelCache.clear();
});
describe('ol.render.canvas.checkFont()', function() {
beforeEach(function() {

View File

@@ -285,7 +285,9 @@ describe('ol.structs.LRUCache', function() {
lruCache.setSize(2);
expect(lruCache.highWaterMark).to.be(2);
fillLRUCache(lruCache);
lruCache.prune();
while (lruCache.canExpireCache()) {
lruCache.pop();
}
expect(lruCache.getKeys().length).to.be(2);
});
});