Decouple executor group creation from rendering

This commit is contained in:
ahocevar
2018-11-20 20:02:34 +01:00
parent 85abe3695a
commit 43759fd846
5 changed files with 59 additions and 42 deletions

View File

@@ -146,9 +146,10 @@ class Tile extends EventTarget {
* Get the interim tile most suitable for rendering using the chain of interim * Get the interim tile most suitable for rendering using the chain of interim
* tiles. This corresponds to the most recent tile that has been loaded, if no * tiles. This corresponds to the most recent tile that has been loaded, if no
* such tile exists, the original tile is returned. * such tile exists, the original tile is returned.
* @param {import("./layer/Layer").default} layer Layer.
* @return {!Tile} Best tile for rendering. * @return {!Tile} Best tile for rendering.
*/ */
getInterimTile() { getInterimTile(layer) {
if (!this.interimTile) { if (!this.interimTile) {
//empty chain //empty chain
return this; return this;

View File

@@ -72,6 +72,12 @@ class VectorImageTile extends Tile {
*/ */
this.sourceTiles_ = sourceTiles; this.sourceTiles_ = sourceTiles;
/**
* @private
* @type {boolean}
*/
this.sourceTilesLoaded = false;
/** /**
* Keys of source tiles used by this tile. Use with {@link #getTile}. * Keys of source tiles used by this tile. Use with {@link #getTile}.
* @type {Array<string>} * @type {Array<string>}
@@ -107,13 +113,13 @@ class VectorImageTile extends Tile {
* Use only source tiles that are loaded already * Use only source tiles that are loaded already
* @type {boolean} * @type {boolean}
*/ */
this.useLoadedOnly = zoom != tileCoord[0]; this.isInterimTile = zoom != tileCoord[0];
if (urlTileCoord) { if (urlTileCoord) {
const extent = this.extent = tileGrid.getTileCoordExtent(urlTileCoord); const extent = this.extent = tileGrid.getTileCoordExtent(urlTileCoord);
const resolution = tileGrid.getResolution(zoom); const resolution = tileGrid.getResolution(zoom);
const sourceZ = sourceTileGrid.getZForResolution(resolution); const sourceZ = sourceTileGrid.getZForResolution(resolution);
const useLoadedOnly = this.useLoadedOnly; const isInterimTile = this.isInterimTile;
let loadCount = 0; let loadCount = 0;
sourceTileGrid.forEachTileCoord(extent, sourceZ, function(sourceTileCoord) { sourceTileGrid.forEachTileCoord(extent, sourceZ, function(sourceTileCoord) {
let sharedExtent = getIntersection(extent, let sharedExtent = getIntersection(extent,
@@ -128,7 +134,7 @@ class VectorImageTile extends Tile {
++loadCount; ++loadCount;
const sourceTileKey = sourceTileCoord.toString(); const sourceTileKey = sourceTileCoord.toString();
let sourceTile = sourceTiles[sourceTileKey]; let sourceTile = sourceTiles[sourceTileKey];
if (!sourceTile && !useLoadedOnly) { if (!sourceTile && !isInterimTile) {
const tileUrl = tileUrlFunction(sourceTileCoord, pixelRatio, projection); const tileUrl = tileUrlFunction(sourceTileCoord, pixelRatio, projection);
sourceTile = sourceTiles[sourceTileKey] = new tileClass(sourceTileCoord, sourceTile = sourceTiles[sourceTileKey] = new tileClass(sourceTileCoord,
tileUrl == undefined ? TileState.EMPTY : TileState.IDLE, tileUrl == undefined ? TileState.EMPTY : TileState.IDLE,
@@ -137,19 +143,19 @@ class VectorImageTile extends Tile {
this.sourceTileListenerKeys_.push( this.sourceTileListenerKeys_.push(
listen(sourceTile, EventType.CHANGE, handleTileChange)); listen(sourceTile, EventType.CHANGE, handleTileChange));
} }
if (sourceTile && (!useLoadedOnly || sourceTile.getState() == TileState.LOADED)) { if (sourceTile && (!isInterimTile || sourceTile.getState() == TileState.LOADED)) {
sourceTile.consumers++; sourceTile.consumers++;
this.tileKeys.push(sourceTileKey); this.tileKeys.push(sourceTileKey);
} }
} }
}.bind(this)); }.bind(this));
if (useLoadedOnly && loadCount == this.tileKeys.length) { if (isInterimTile && loadCount == this.tileKeys.length) {
this.finishLoading_(); this.finishLoading_();
} }
this.createInterimTile_ = function() { this.createInterimTile_ = function(layerId) {
if (this.getState() !== TileState.LOADED && !useLoadedOnly) { if (this.getState() !== TileState.LOADED && !isInterimTile) {
let bestZoom = -1; let bestZoom = -1;
for (const key in sourceTiles) { for (const key in sourceTiles) {
const sourceTile = sourceTiles[key]; const sourceTile = sourceTiles[key];
@@ -157,16 +163,20 @@ class VectorImageTile extends Tile {
const sourceTileCoord = sourceTile.tileCoord; const sourceTileCoord = sourceTile.tileCoord;
const sourceTileExtent = sourceTileGrid.getTileCoordExtent(sourceTileCoord); const sourceTileExtent = sourceTileGrid.getTileCoordExtent(sourceTileCoord);
if (containsExtent(sourceTileExtent, extent) && sourceTileCoord[0] > bestZoom) { if (containsExtent(sourceTileExtent, extent) && sourceTileCoord[0] > bestZoom) {
const lowResExecutorGroup = sourceTile.getLowResExecutorGroup(layerId, zoom, extent);
if (lowResExecutorGroup) {
// reuse existing replay if we're rendering an interim tile
sourceTile.setExecutorGroup(layerId, this.tileCoord.toString(), lowResExecutorGroup);
bestZoom = sourceTileCoord[0]; bestZoom = sourceTileCoord[0];
} }
} }
} }
}
if (bestZoom !== -1) { if (bestZoom !== -1) {
const tile = new VectorImageTile(tileCoord, state, sourceRevision, this.interimTile = new VectorImageTile(tileCoord, state, sourceRevision,
format, tileLoadFunction, urlTileCoord, tileUrlFunction, format, tileLoadFunction, urlTileCoord, tileUrlFunction,
sourceTileGrid, tileGrid, sourceTiles, pixelRatio, projection, sourceTileGrid, tileGrid, sourceTiles, pixelRatio, projection,
tileClass, VOID, bestZoom); tileClass, VOID, bestZoom);
this.interimTile = tile;
} }
} }
}; };
@@ -174,11 +184,11 @@ class VectorImageTile extends Tile {
} }
getInterimTile() { getInterimTile(layer) {
if (!this.interimTile) { if (!this.interimTile) {
this.createInterimTile_(); this.createInterimTile_(getUid(layer));
} }
return super.getInterimTile(); return super.getInterimTile(layer);
} }
/** /**
@@ -331,7 +341,7 @@ class VectorImageTile extends Tile {
this.loadListenerKeys_.forEach(unlistenByKey); this.loadListenerKeys_.forEach(unlistenByKey);
this.loadListenerKeys_.length = 0; this.loadListenerKeys_.length = 0;
this.sourceTilesLoaded = true; this.sourceTilesLoaded = true;
this.setState(TileState.LOADED); this.changed();
} else { } else {
this.setState(empty == this.tileKeys.length ? TileState.EMPTY : TileState.ERROR); this.setState(empty == this.tileKeys.length ? TileState.EMPTY : TileState.ERROR);
} }

View File

@@ -2,7 +2,6 @@
* @module ol/VectorTile * @module ol/VectorTile
*/ */
import {containsExtent} from './extent.js'; import {containsExtent} from './extent.js';
import {getUid} from './util.js';
import Tile from './Tile.js'; import Tile from './Tile.js';
import TileState from './TileState.js'; import TileState from './TileState.js';
@@ -140,23 +139,22 @@ class VectorTile extends Tile {
} }
/** /**
* @param {import("./layer/Layer.js").default} layer Layer. * @param {string} layerId UID of the layer.
* @param {string} key Key. * @param {string} key Key.
* @return {import("./render/canvas/ExecutorGroup.js").default} Executor group. * @return {import("./render/canvas/ExecutorGroup.js").default} Executor group.
*/ */
getExecutorGroup(layer, key) { getExecutorGroup(layerId, key) {
return this.executorGroups_[getUid(layer) + ',' + key]; return this.executorGroups_[layerId + ',' + key];
} }
/** /**
* Get the best matching lower resolution replay group for a given zoom and extent. * Get the best matching lower resolution replay group for a given zoom and extent.
* @param {import("./layer/Layer").default} layer Layer. * @param {string} layerId UID of the layer.
* @param {number} zoom Zoom. * @param {number} zoom Zoom.
* @param {import("./extent").Extent} extent Extent. * @param {import("./extent").Extent} extent Extent.
* @return {import("./render/canvas/ExecutorGroup.js").default} Executor groups. * @return {import("./render/canvas/ExecutorGroup.js").default} Executor groups.
*/ */
getLowResExecutorGroup(layer, zoom, extent) { getLowResExecutorGroup(layerId, zoom, extent) {
const layerId = getUid(layer);
let bestZoom = 0; let bestZoom = 0;
let replayGroup = null; let replayGroup = null;
for (const key in this.executorGroups_) { for (const key in this.executorGroups_) {
@@ -242,12 +240,12 @@ class VectorTile extends Tile {
} }
/** /**
* @param {import("./layer/Layer.js").default} layer Layer. * @param {string} layerId UID of the layer.
* @param {string} key Key. * @param {string} key Key.
* @param {import("./render/canvas/ExecutorGroup.js").default} executorGroup Executor group. * @param {import("./render/canvas/ExecutorGroup.js").default} executorGroup Executor group.
*/ */
setExecutorGroup(layer, key, executorGroup) { setExecutorGroup(layerId, key, executorGroup) {
this.executorGroups_[getUid(layer) + ',' + key] = executorGroup; this.executorGroups_[layerId + ',' + key] = executorGroup;
} }
/** /**

View File

@@ -100,7 +100,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
} }
} }
if (!this.isDrawableTile_(tile)) { if (!this.isDrawableTile_(tile)) {
tile = tile.getInterimTile(); tile = tile.getInterimTile(tileLayer);
} }
return tile; return tile;
} }

View File

@@ -5,7 +5,7 @@ import {getUid} from '../../util.js';
import {createCanvasContext2D} from '../../dom.js'; import {createCanvasContext2D} from '../../dom.js';
import TileState from '../../TileState.js'; import TileState from '../../TileState.js';
import ViewHint from '../../ViewHint.js'; import ViewHint from '../../ViewHint.js';
import {listen, unlisten} from '../../events.js'; import {listen, unlisten, unlistenByKey} from '../../events.js';
import EventType from '../../events/EventType.js'; import EventType from '../../events/EventType.js';
import rbush from 'rbush'; import rbush from 'rbush';
import {buffer, containsCoordinate, equals, getIntersection, getTopLeft, intersects} from '../../extent.js'; import {buffer, containsCoordinate, equals, getIntersection, getTopLeft, intersects} from '../../extent.js';
@@ -147,11 +147,20 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
* @inheritDoc * @inheritDoc
*/ */
getTile(z, x, y, pixelRatio, projection) { getTile(z, x, y, pixelRatio, projection) {
const tile = super.getTile(z, x, y, pixelRatio, projection); const tile = /** @type {import("../../VectorImageTile.js").default} */ (super.getTile(z, x, y, pixelRatio, projection));
if (tile.getState() === TileState.IDLE) {
const key = listen(tile, EventType.CHANGE, function() {
if (tile.sourceTilesLoaded) {
this.createExecutorGroup_(tile, pixelRatio, projection);
unlistenByKey(key);
tile.setState(TileState.LOADED);
}
}.bind(this));
}
if (tile.getState() === TileState.LOADED) { if (tile.getState() === TileState.LOADED) {
this.createExecutorGroup_(/** @type {import("../../VectorImageTile.js").default} */ (tile), pixelRatio, projection); this.createExecutorGroup_(tile, pixelRatio, projection);
if (this.context) { if (this.context) {
this.renderTileImage_(/** @type {import("../../VectorImageTile.js").default} */ (tile), pixelRatio, projection); this.renderTileImage_(tile, pixelRatio, projection);
} }
} }
return tile; return tile;
@@ -162,7 +171,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
*/ */
getTileImage(tile) { getTileImage(tile) {
const tileLayer = /** @type {import("../../layer/Tile.js").default} */ (this.getLayer()); const tileLayer = /** @type {import("../../layer/Tile.js").default} */ (this.getLayer());
return /** @type {import("../../VectorImageTile.js").default} */ (tile).getImage(tileLayer); return tile.getImage(tileLayer);
} }
/** /**
@@ -186,6 +195,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
*/ */
createExecutorGroup_(tile, pixelRatio, projection) { createExecutorGroup_(tile, pixelRatio, projection) {
const layer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer()); const layer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer());
const layerId = getUid(layer);
const revision = layer.getRevision(); const revision = layer.getRevision();
const renderOrder = /** @type {import("../../render.js").OrderFunction} */ (layer.getRenderOrder()) || null; const renderOrder = /** @type {import("../../render.js").OrderFunction} */ (layer.getRenderOrder()) || null;
@@ -207,14 +217,12 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
if (sourceTile.getState() != TileState.LOADED) { if (sourceTile.getState() != TileState.LOADED) {
continue; continue;
} }
if (tile.useLoadedOnly) { if (tile.isInterimTile) {
const lowResExecutorGroup = sourceTile.getLowResExecutorGroup(layer, zoom, tileExtent);
if (lowResExecutorGroup) {
// reuse existing replay if we're rendering an interim tile // reuse existing replay if we're rendering an interim tile
sourceTile.setExecutorGroup(layer, tile.tileCoord.toString(), lowResExecutorGroup); sourceTile.setExecutorGroup(layerId, tile.tileCoord.toString(),
sourceTile.getLowResExecutorGroup(layerId, zoom, tileExtent));
continue; continue;
} }
}
const sourceTileCoord = sourceTile.tileCoord; const sourceTileCoord = sourceTile.tileCoord;
const sourceTileExtent = sourceTileGrid.getTileCoordExtent(sourceTileCoord); const sourceTileExtent = sourceTileGrid.getTileCoordExtent(sourceTileCoord);
@@ -271,7 +279,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const executorGroupInstructions = builderGroup.finish(); const executorGroupInstructions = builderGroup.finish();
const renderingReplayGroup = new CanvasExecutorGroup(sharedExtent, resolution, const renderingReplayGroup = new CanvasExecutorGroup(sharedExtent, resolution,
pixelRatio, source.getOverlaps(), this.declutterTree_, executorGroupInstructions, layer.getRenderBuffer()); pixelRatio, source.getOverlaps(), this.declutterTree_, executorGroupInstructions, layer.getRenderBuffer());
sourceTile.setExecutorGroup(layer, tile.tileCoord.toString(), renderingReplayGroup); sourceTile.setExecutorGroup(getUid(layer), tile.tileCoord.toString(), renderingReplayGroup);
} }
builderState.renderedRevision = revision; builderState.renderedRevision = revision;
builderState.renderedRenderOrder = renderOrder; builderState.renderedRenderOrder = renderOrder;
@@ -303,7 +311,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
if (sourceTile.getState() != TileState.LOADED) { if (sourceTile.getState() != TileState.LOADED) {
continue; continue;
} }
const executorGroup = /** @type {CanvasExecutorGroup} */ (sourceTile.getExecutorGroup(layer, const executorGroup = /** @type {CanvasExecutorGroup} */ (sourceTile.getExecutorGroup(getUid(layer),
tile.tileCoord.toString())); tile.tileCoord.toString()));
found = found || executorGroup.forEachFeatureAtCoordinate(coordinate, resolution, rotation, hitTolerance, {}, found = found || executorGroup.forEachFeatureAtCoordinate(coordinate, resolution, rotation, hitTolerance, {},
/** /**
@@ -431,7 +439,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
if (sourceTile.getState() != TileState.LOADED) { if (sourceTile.getState() != TileState.LOADED) {
continue; continue;
} }
const executorGroup = /** @type {CanvasExecutorGroup} */ (sourceTile.getExecutorGroup(layer, tileCoord.toString())); const executorGroup = /** @type {CanvasExecutorGroup} */ (sourceTile.getExecutorGroup(getUid(layer), tileCoord.toString()));
if (!executorGroup || !executorGroup.hasExecutors(replayTypes)) { if (!executorGroup || !executorGroup.hasExecutors(replayTypes)) {
// sourceTile was not yet loaded when this.createReplayGroup_() was // sourceTile was not yet loaded when this.createReplayGroup_() was
// called, or it has no replays of the types we want to render // called, or it has no replays of the types we want to render
@@ -536,7 +544,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const transform = resetTransform(this.tmpTransform_); const transform = resetTransform(this.tmpTransform_);
scaleTransform(transform, pixelScale, -pixelScale); scaleTransform(transform, pixelScale, -pixelScale);
translateTransform(transform, -tileExtent[0], -tileExtent[3]); translateTransform(transform, -tileExtent[0], -tileExtent[3]);
const executorGroup = /** @type {CanvasExecutorGroup} */ (sourceTile.getExecutorGroup(layer, const executorGroup = /** @type {CanvasExecutorGroup} */ (sourceTile.getExecutorGroup(getUid(layer),
tile.tileCoord.toString())); tile.tileCoord.toString()));
executorGroup.execute(context, transform, 0, {}, true, replays); executorGroup.execute(context, transform, 0, {}, true, replays);
} }