Skip to content

Commit a77e5b5

Browse files
authored
TilesRenderer: Remove loading tiles that are no longer used (#1422)
* Remove loading unused tiles * Adjust comments * Add "QUEUED" loading status * Add "queued" to stats * Update demos
1 parent 67a61df commit a77e5b5

7 files changed

Lines changed: 74 additions & 13 deletions

File tree

example/three/googleMapsExample.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ function updateHtml() {
351351

352352
const lruCache = tiles.lruCache;
353353
const cacheFullness = lruCache.cachedBytes / lruCache.maxBytesSize;
354-
str += `Downloading: ${ tiles.stats.downloading } Parsing: ${ tiles.stats.parsing } Visible: ${ tiles.visibleTiles.size }<br/>`;
354+
str += `Queued: ${ tiles.stats.queued } Downloading: ${ tiles.stats.downloading } Parsing: ${ tiles.stats.parsing } Visible: ${ tiles.visibleTiles.size }<br/>`;
355355
str += `Cache: ${ ( 100 * cacheFullness ).toFixed( 2 ) }% ~${ ( lruCache.cachedBytes / 1000 / 1000 ).toFixed( 2 ) }mb<br/>`;
356356

357357
}

example/three/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -654,7 +654,7 @@ function render() {
654654
}
655655

656656
const cacheFullness = tiles.lruCache.itemList.length / tiles.lruCache.maxSize;
657-
let str = `Downloading: ${ tiles.stats.downloading } Parsing: ${ tiles.stats.parsing } Visible: ${ tiles.visibleTiles.size }`;
657+
let str = `Queued: ${ tiles.stats.queued } Downloading: ${ tiles.stats.downloading } Parsing: ${ tiles.stats.parsing } Visible: ${ tiles.visibleTiles.size }`;
658658

659659
if ( params.enableCacheDisplay ) {
660660

example/three/quantMeshOverlays.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ function updateHtml() {
226226

227227
const lruCache = tiles.lruCache;
228228
const cacheFullness = lruCache.cachedBytes / lruCache.maxBytesSize;
229-
str += `Downloading: ${ tiles.stats.downloading } Parsing: ${ tiles.stats.parsing } Visible: ${ tiles.visibleTiles.size }<br/>`;
229+
str += `Queued: ${ tiles.stats.queued } Downloading: ${ tiles.stats.downloading } Parsing: ${ tiles.stats.parsing } Visible: ${ tiles.visibleTiles.size }<br/>`;
230230
str += `Cache: ${ ( 100 * cacheFullness ).toFixed( 2 ) }% ~${ ( lruCache.cachedBytes / 1000 / 1000 ).toFixed( 2 ) }mb<br/>`;
231231

232232
}

src/core/renderer/constants.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
// FAILED is negative so lru cache priority sorting will unload it first
22
export const FAILED = - 1;
33
export const UNLOADED = 0;
4-
export const LOADING = 1;
5-
export const PARSING = 2;
6-
export const LOADED = 3;
4+
export const QUEUED = 1;
5+
export const LOADING = 2;
6+
export const PARSING = 3;
7+
export const LOADED = 4;
78

89
// https://en.wikipedia.org/wiki/World_Geodetic_System
910
// https://en.wikipedia.org/wiki/Flattening

src/core/renderer/tiles/TilesRendererBase.js

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { getUrlExtension } from '../utilities/urlExtension.js';
22
import { LRUCache } from '../utilities/LRUCache.js';
33
import { PriorityQueue } from '../utilities/PriorityQueue.js';
44
import { markUsedTiles, toggleTiles, markVisibleTiles, markUsedSetLeaves } from './traverseFunctions.js';
5-
import { UNLOADED, LOADING, PARSING, LOADED, FAILED } from '../constants.js';
5+
import { UNLOADED, QUEUED, LOADING, PARSING, LOADED, FAILED } from '../constants.js';
66
import { throttle } from '../utilities/throttle.js';
77
import { traverseSet } from '../utilities/TraversalUtils.js';
88

@@ -108,7 +108,7 @@ export class TilesRendererBase {
108108
get loadProgress() {
109109

110110
const { stats, isLoading } = this;
111-
const loading = stats.downloading + stats.parsing;
111+
const loading = stats.queued + stats.downloading + stats.parsing;
112112
const total = stats.inCacheSinceLoad + ( isLoading ? 1 : 0 );
113113
return total === 0 ? 1.0 : 1.0 - loading / total;
114114

@@ -157,16 +157,20 @@ export class TilesRendererBase {
157157
this.visibleTiles = new Set();
158158
this.activeTiles = new Set();
159159
this.usedSet = new Set();
160+
this.loadingTiles = new Set();
160161
this.lruCache = lruCache;
161162
this.downloadQueue = downloadQueue;
162163
this.parseQueue = parseQueue;
163164
this.processNodeQueue = processNodeQueue;
164165
this.stats = {
165166
inCacheSinceLoad: 0,
166167
inCache: 0,
167-
parsing: 0,
168+
169+
queued: 0,
168170
downloading: 0,
171+
parsing: 0,
169172
failed: 0,
173+
170174
inFrustum: 0,
171175
used: 0,
172176
active: 0,
@@ -404,6 +408,9 @@ export class TilesRendererBase {
404408
markVisibleTiles( root, this );
405409
toggleTiles( root, this );
406410

411+
// remove any tiles that are loading but no longer used
412+
this.removeUnusedPendingTiles();
413+
407414
// TODO: This will only sort for one tileset. We may want to store this queue on the
408415
// LRUCache so multiple tilesets can use it at once
409416
// start the downloads of the tiles as needed
@@ -492,6 +499,7 @@ export class TilesRendererBase {
492499
}
493500

494501
this.stats = {
502+
queued: 0,
495503
parsing: 0,
496504
downloading: 0,
497505
failed: 0,
@@ -501,6 +509,7 @@ export class TilesRendererBase {
501509
visible: 0,
502510
};
503511
this.frameCount = 0;
512+
this.loadingTiles.clear();
504513

505514
}
506515

@@ -667,6 +676,32 @@ export class TilesRendererBase {
667676

668677
}
669678

679+
removeUnusedPendingTiles() {
680+
681+
const { lruCache, loadingTiles } = this;
682+
683+
// cannot delete items while iterating over a set
684+
const toRemove = [];
685+
for ( const tile of loadingTiles ) {
686+
687+
// we only remove tiles that are QUEUED to avoid cancelling tiles that may already be nearly downloaded
688+
// as the camera moves
689+
if ( ! lruCache.isUsed( tile ) && tile.__loadingState === QUEUED ) {
690+
691+
toRemove.push( tile );
692+
693+
}
694+
695+
}
696+
697+
for ( let i = 0; i < toRemove.length; i ++ ) {
698+
699+
lruCache.remove( toRemove[ i ] );
700+
701+
}
702+
703+
}
704+
670705
// Private Functions
671706
queueTileForDownload( tile ) {
672707

@@ -882,6 +917,7 @@ export class TilesRendererBase {
882917
const lruCache = this.lruCache;
883918
const downloadQueue = this.downloadQueue;
884919
const parseQueue = this.parseQueue;
920+
const loadingTiles = this.loadingTiles;
885921
const extension = getUrlExtension( uri );
886922

887923
// track an abort controller and pass-through the below conditions if aborted
@@ -917,7 +953,11 @@ export class TilesRendererBase {
917953

918954
}
919955

920-
if ( t.__loadingState === LOADING ) {
956+
if ( t.__loadingState === QUEUED ) {
957+
958+
stats.queued --;
959+
960+
} else if ( t.__loadingState === LOADING ) {
921961

922962
stats.downloading --;
923963

@@ -931,6 +971,7 @@ export class TilesRendererBase {
931971

932972
parseQueue.remove( t );
933973
downloadQueue.remove( t );
974+
loadingTiles.delete( t );
934975

935976
} );
936977

@@ -953,8 +994,9 @@ export class TilesRendererBase {
953994
this.cachedSinceLoadComplete.add( tile );
954995
stats.inCacheSinceLoad ++;
955996
stats.inCache ++;
956-
stats.downloading ++;
957-
tile.__loadingState = LOADING;
997+
stats.queued ++;
998+
tile.__loadingState = QUEUED;
999+
loadingTiles.add( tile );
9581000

9591001
// queue the download and parse
9601002
return downloadQueue.add( tile, downloadTile => {
@@ -965,6 +1007,10 @@ export class TilesRendererBase {
9651007

9661008
}
9671009

1010+
tile.__loadingState = LOADING;
1011+
stats.downloading ++;
1012+
stats.queued --;
1013+
9681014
const res = this.invokeOnePlugin( plugin => plugin.fetchData && plugin.fetchData( uri, { ...this.fetchOptions, signal } ) );
9691015
this.dispatchEvent( { type: 'tile-download-start', tile, uri } );
9701016
return res;
@@ -1043,6 +1089,7 @@ export class TilesRendererBase {
10431089

10441090
stats.parsing --;
10451091
tile.__loadingState = LOADED;
1092+
loadingTiles.delete( tile );
10461093
lruCache.setLoaded( tile, true );
10471094

10481095
// If the memory of the item hasn't been registered yet then that means the memory usage hasn't
@@ -1100,7 +1147,11 @@ export class TilesRendererBase {
11001147
parseQueue.remove( tile );
11011148
downloadQueue.remove( tile );
11021149

1103-
if ( tile.__loadingState === PARSING ) {
1150+
if ( tile.__loadingState === QUEUED ) {
1151+
1152+
stats.queued --;
1153+
1154+
} else if ( tile.__loadingState === PARSING ) {
11041155

11051156
stats.parsing --;
11061157

@@ -1115,6 +1166,7 @@ export class TilesRendererBase {
11151166
console.error( `TilesRenderer : Failed to load tile at url "${ tile.content.uri }".` );
11161167
console.error( error );
11171168
tile.__loadingState = FAILED;
1169+
loadingTiles.delete( tile );
11181170
lruCache.setLoaded( tile, true );
11191171

11201172
this.dispatchEvent( {

src/core/renderer/utilities/LRUCache.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ export class LRUCache {
99

1010
unloadPriorityCallback: ( item: any ) => number;
1111

12+
isUsed( item: any ): boolean;
13+
1214
}

src/core/renderer/utilities/LRUCache.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,12 @@ class LRUCache {
200200

201201
}
202202

203+
isUsed( item ) {
204+
205+
return this.usedSet.has( item );
206+
207+
}
208+
203209
// TODO: this should be renamed because it's not necessarily unloading all unused content
204210
// Maybe call it "cleanup" or "unloadToMinSize"
205211
unloadUnusedContent() {

0 commit comments

Comments
 (0)