Skip to content

Commit

Permalink
fix adding from cache and symbol flickering
Browse files Browse the repository at this point in the history
  • Loading branch information
ansis committed Apr 2, 2018
1 parent 962fad3 commit 6fc31ec
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 19 deletions.
70 changes: 55 additions & 15 deletions src/source/source_cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class SourceCache extends Evented {
_sourceLoaded: boolean;
_sourceErrored: boolean;
_tiles: {[any]: Tile};
_prevTiles: {[any]: Array<Tile>};
_cache: Cache<Tile>;
_timers: {[any]: TimeoutID};
_cacheTimers: {[any]: TimeoutID};
Expand Down Expand Up @@ -359,7 +360,7 @@ class SourceCache extends Evented {
const parent = tileID.scaledTo(z);
if (!parent) return;
const id = String(parent.key);
const tile = this._tiles[id];
const tile = this._getPrevTile(id);
if (tile && tile.hasData()) {
retain[id] = parent;
return tile;
Expand Down Expand Up @@ -472,10 +473,12 @@ class SourceCache extends Evented {
retain[fadedParent] = parentsForFading[fadedParent];
}
// Remove the tiles we don't need anymore.
const remove = util.keysDifference(this._tiles, retain);
for (let i = 0; i < remove.length; i++) {
this._removeTile(remove[i]);
for (const key in this._prevTiles) {
for (const tile of this._prevTiles[key]) {
this._removeTile(tile);
}
}

}

_updateRetainedTiles(idealTileIDs: Array<OverscaledTileID>, zoom: number): { [string]: OverscaledTileID} {
Expand All @@ -484,6 +487,38 @@ class SourceCache extends Evented {
const minCoveringZoom = Math.max(zoom - SourceCache.maxOverzooming, this._source.minzoom);
const maxCoveringZoom = Math.max(zoom + SourceCache.maxUnderzooming, this._source.minzoom);

const prevTiles = this._tiles;
this._tiles = {};

// Wrapping longitude values can cause a jump in `wrap` values. Tiles that
// cover a similar area of the screen can have different wrap values. This
// calculates this difference in wrap values so that we can match tiles
// across frames where the longitude gets wrapped.
const prevCenter = this._prevCenter || this.transform.center;
const wrapDelta = Math.round((this.transform.center.lng - prevCenter.lng) / 360);
this._prevCenter = this.transform.center;

// Add all ideal tiles that were used for the previous frame.
// Use unwrapped keys to match on tiles that matche exact wraps.
for (const tileID of idealTileIDs) {
const matchedKey = tileID.unwrapTo(tileID.wrap - wrapDelta);
if (prevTiles[matchedKey]) {
this._tiles[tileID.key] = prevTiles[matchedKey];
this._tiles[tileID.key].tileID = tileID;
delete prevTiles[matchedKey];
}
}

// Index tiles by their wrapped coordinates
this._prevTiles = {};
for (const key in prevTiles) {
const wrappedKey = prevTiles[key].tileID.wrapped().key;
if (this._prevTiles[wrappedKey] === undefined) {
this._prevTiles[wrappedKey] = [];
}
this._prevTiles[wrappedKey].push(prevTiles[key])
}

for (let i = 0; i < idealTileIDs.length; i++) {
const tileID = idealTileIDs[i];
let tile = this._addTile(tileID);
Expand Down Expand Up @@ -559,6 +594,15 @@ class SourceCache extends Evented {
return retain;
}

_getPrevTile(tileID: OverscaledTileID) {
const tiles = this._prevTiles[tileID.wrapped().key];
const tile = tiles && tiles.pop();
if (tile) {
tile.tileID = tileID;
}
return tile;
}

/**
* Add a tile, given its coordinate, to the pyramid.
* @private
Expand All @@ -568,6 +612,11 @@ class SourceCache extends Evented {
if (tile)
return tile;

tile = this._getPrevTile(tileID);
if (tile) {
this._tiles[tileID.key] = tile;
return tile;
}

tile = this._cache.getAndRemove((tileID.wrapped().key: any));
if (tile) {
Expand All @@ -589,7 +638,6 @@ class SourceCache extends Evented {
// Impossible, but silence flow.
if (!tile) return (null: any);

tile.uses++;
this._tiles[tileID.key] = tile;
if (!cached) this._source.fire(new Event('dataloading', {tile: tile, coord: tile.tileID, dataType: 'source'}));

Expand Down Expand Up @@ -630,21 +678,13 @@ class SourceCache extends Evented {
* Remove a tile, given its id, from the pyramid
* @private
*/
_removeTile(id: string | number) {
const tile = this._tiles[id];
if (!tile)
return;

tile.uses--;
delete this._tiles[id];
_removeTile(tile: Tile) {
const id = tile.tileID.key;
if (this._timers[id]) {
clearTimeout(this._timers[id]);
delete this._timers[id];
}

if (tile.uses > 0)
return;

if (tile.hasData()) {
tile.tileID = tile.tileID.wrapped();
const wrappedId = tile.tileID.key;
Expand Down
2 changes: 0 additions & 2 deletions src/source/tile.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ export type TileState =
class Tile {
tileID: OverscaledTileID;
uid: number;
uses: number;
tileSize: number;
buckets: {[string]: Bucket};
iconAtlasImage: ?RGBAImage;
Expand Down Expand Up @@ -101,7 +100,6 @@ class Tile {
constructor(tileID: OverscaledTileID, size: number) {
this.tileID = tileID;
this.uid = util.uniqueId();
this.uses = 0;
this.tileSize = size;
this.buckets = {};
this.expirationTime = null;
Expand Down
8 changes: 8 additions & 0 deletions src/source/tile_id.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ class OverscaledTileID {
this.key = calculateKey(wrap, overscaledZ, x, y);
}

equals(id: OverscaledTileID) {
return this.overscaledZ === id.overscaledZ && this.wrap === id.wrap && this.canonical.equals(id.canonical);
}

scaledTo(targetZ: number) {
assert(targetZ <= this.overscaledZ);
const zDifference = this.canonical.z - targetZ;
Expand Down Expand Up @@ -121,6 +125,10 @@ class OverscaledTileID {
return new OverscaledTileID(this.overscaledZ, 0, this.canonical.z, this.canonical.x, this.canonical.y);
}

unwrapTo(wrap: number) {
return new OverscaledTileID(this.overscaledZ, wrap, this.canonical.z, this.canonical.x, this.canonical.y);
}

overscaleFactor() {
return Math.pow(2, this.overscaledZ - this.canonical.z);
}
Expand Down
2 changes: 1 addition & 1 deletion src/style/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -939,7 +939,7 @@ class Style extends Evented {
.sort((a, b) => (b.tileID.overscaledZ - a.tileID.overscaledZ) || (a.tileID.isLessThan(b.tileID) ? -1 : 1));
}

const layerBucketsChanged = this.crossTileSymbolIndex.addLayer(styleLayer, layerTiles[styleLayer.source]);
const layerBucketsChanged = this.crossTileSymbolIndex.addLayer(styleLayer, layerTiles[styleLayer.source], transform.center.lng);
symbolBucketsChanged = symbolBucketsChanged || layerBucketsChanged;
}
this.crossTileSymbolIndex.pruneUnusedLayers(this._order);
Expand Down
29 changes: 28 additions & 1 deletion src/symbol/cross_tile_symbol_index.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,35 @@ class CrossTileIDs {
class CrossTileSymbolLayerIndex {
indexes: {[zoom: string | number]: {[tileId: string | number]: TileLayerIndex}};
usedCrossTileIDs: {[zoom: string | number]: {[crossTileID: number]: boolean}};
lng: number;

constructor() {
this.indexes = {};
this.usedCrossTileIDs = {};
this.lng = 0;
}

/*
* Sometimes when a user pans across the antimeridian the longitude value gets wrapped.
* To prevent labels from flashing out and in we adjust the tileID values in the indexes
* so that they match the new wrapped version of the map.
*/
handleWrapJump(lng: number) {
const wrapDelta = Math.round((lng - this.lng) / 360);
if (wrapDelta !== 0) {
for (const zoom in this.indexes) {
const zoomIndexes = this.indexes[zoom];
const newZoomIndex = {};
for (const key in zoomIndexes) {
// change the tileID's wrap and add it to a new index
const index = zoomIndexes[key];
index.tileID = index.tileID.unwrapTo(index.tileID.wrap - wrapDelta);
newZoomIndex[index.tileID.key] = index;
}
this.indexes[zoom] = newZoomIndex;
}
}
this.lng = lng;
}

addBucket(tileID: OverscaledTileID, bucket: SymbolBucket, crossTileIDs: CrossTileIDs) {
Expand Down Expand Up @@ -220,7 +245,7 @@ class CrossTileSymbolIndex {
this.maxBucketInstanceId = 0;
}

addLayer(styleLayer: StyleLayer, tiles: Array<Tile>) {
addLayer(styleLayer: StyleLayer, tiles: Array<Tile>, lng: number) {
let layerIndex = this.layerIndexes[styleLayer.id];
if (layerIndex === undefined) {
layerIndex = this.layerIndexes[styleLayer.id] = new CrossTileSymbolLayerIndex();
Expand All @@ -229,6 +254,8 @@ class CrossTileSymbolIndex {
let symbolBucketsChanged = false;
const currentBucketIDs = {};

layerIndex.handleWrapJump(lng);

for (const tile of tiles) {
const symbolBucket = ((tile.getBucket(styleLayer): any): SymbolBucket);
if (!symbolBucket) continue;
Expand Down

0 comments on commit 6fc31ec

Please sign in to comment.