Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/core/renderer/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ export * from './constants.js';

export { LRUCache } from './utilities/LRUCache.js';
export { PriorityQueue } from './utilities/PriorityQueue.js';
export { DelayQueue } from './utilities/DelayQueue.js';
export { BatchTable } from './utilities/BatchTable.js';
export { FeatureTable } from './utilities/FeatureTable.js';
1 change: 1 addition & 0 deletions src/core/renderer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ export * from './constants.js';

export { LRUCache } from './utilities/LRUCache.js';
export * from './utilities/PriorityQueue.js';
export * from './utilities/DelayQueue.js';
export * as TraversalUtils from './utilities/TraversalUtils.js';
export * as LoaderUtils from './utilities/LoaderUtils.js';
1 change: 1 addition & 0 deletions src/core/renderer/tiles/TilesRendererBase.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export class TilesRendererBase {
errorThreshold : number;
displayActiveTiles : boolean;
maxDepth : number;
downloadDelay : number;

loadProgress: number;

Expand Down
51 changes: 40 additions & 11 deletions src/core/renderer/tiles/TilesRendererBase.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { getUrlExtension } from '../utilities/urlExtension.js';
import { LRUCache } from '../utilities/LRUCache.js';
import { PriorityQueue } from '../utilities/PriorityQueue.js';
import { DelayQueue } from '../utilities/DelayQueue.js';
import { markUsedTiles, toggleTiles, markVisibleTiles, markUsedSetLeaves } from './traverseFunctions.js';
import { UNLOADED, QUEUED, LOADING, PARSING, LOADED, FAILED } from '../constants.js';
import { throttle } from '../utilities/throttle.js';
Expand Down Expand Up @@ -127,6 +128,18 @@ export class TilesRendererBase {

}

get downloadDelay() {

return this.delayQueue.delay;

}

set downloadDelay( value ) {

this.delayQueue.delay = value;

}

constructor( url = null ) {

// state
Expand All @@ -142,6 +155,8 @@ export class TilesRendererBase {
const lruCache = new LRUCache();
lruCache.unloadPriorityCallback = lruPriorityCallback;

const delayQueue = new DelayQueue();

const downloadQueue = new PriorityQueue();
downloadQueue.maxJobs = 25;
downloadQueue.priorityCallback = priorityCallback;
Expand All @@ -159,6 +174,7 @@ export class TilesRendererBase {
this.usedSet = new Set();
this.loadingTiles = new Set();
this.lruCache = lruCache;
this.delayQueue = delayQueue;
this.downloadQueue = downloadQueue;
this.parseQueue = parseQueue;
this.processNodeQueue = processNodeQueue;
Expand Down Expand Up @@ -341,7 +357,7 @@ export class TilesRendererBase {

update() {

const { lruCache, usedSet, stats, root, downloadQueue, parseQueue, processNodeQueue } = this;
const { lruCache, usedSet, stats, root, delayQueue, downloadQueue, parseQueue, processNodeQueue } = this;
if ( this.rootLoadingState === UNLOADED ) {

this.rootLoadingState = LOADING;
Expand Down Expand Up @@ -428,7 +444,7 @@ export class TilesRendererBase {
lruCache.scheduleUnload();

// if all tasks have finished and we've been marked as actively loading then fire the completion event
const runningTasks = downloadQueue.running || parseQueue.running || processNodeQueue.running;
const runningTasks = delayQueue.running || downloadQueue.running || parseQueue.running || processNodeQueue.running;
if ( runningTasks === false && this.isLoading === true ) {

this.cachedSinceLoadComplete.clear();
Expand Down Expand Up @@ -678,7 +694,7 @@ export class TilesRendererBase {

removeUnusedPendingTiles() {

const { lruCache, loadingTiles } = this;
const { lruCache, loadingTiles, delayQueue } = this;

// cannot delete items while iterating over a set
const toRemove = [];
Expand All @@ -696,7 +712,9 @@ export class TilesRendererBase {

for ( let i = 0; i < toRemove.length; i ++ ) {

lruCache.remove( toRemove[ i ] );
const tile = toRemove[ i ];
delayQueue.remove( tile );
lruCache.remove( tile );

}

Expand Down Expand Up @@ -915,6 +933,7 @@ export class TilesRendererBase {

const stats = this.stats;
const lruCache = this.lruCache;
const delayQueue = this.delayQueue;
const downloadQueue = this.downloadQueue;
const parseQueue = this.parseQueue;
const loadingTiles = this.loadingTiles;
Expand Down Expand Up @@ -999,21 +1018,31 @@ export class TilesRendererBase {
loadingTiles.add( tile );

// queue the download and parse
return downloadQueue.add( tile, downloadTile => {
return delayQueue.add( tile, () => {

if ( signal.aborted ) {

return Promise.resolve();

}

tile.__loadingState = LOADING;
stats.downloading ++;
stats.queued --;
return downloadQueue.add( tile, () => {

const res = this.invokeOnePlugin( plugin => plugin.fetchData && plugin.fetchData( uri, { ...this.fetchOptions, signal } ) );
this.dispatchEvent( { type: 'tile-download-start', tile, uri } );
return res;
if ( signal.aborted ) {

return Promise.resolve();

}

tile.__loadingState = LOADING;
stats.downloading ++;
stats.queued --;

const res = this.invokeOnePlugin( plugin => plugin.fetchData && plugin.fetchData( uri, { ...this.fetchOptions, signal } ) );
this.dispatchEvent( { type: 'tile-download-start', tile, uri } );
return res;

} );

} )
.then( res => {
Expand Down
9 changes: 9 additions & 0 deletions src/core/renderer/utilities/DelayQueue.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class DelayQueue {

delay: number;
readonly running: boolean;

add( item: any, callback: ( item: any ) => any ): Promise<any>;
remove( item: any ): void;

}
180 changes: 180 additions & 0 deletions src/core/renderer/utilities/DelayQueue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
export class DelayQueueItemRemovedError extends Error {

constructor() {

super( 'DelayQueue: Item removed' );
this.name = 'DelayQueueItemRemovedError';

}

}

export class DelayQueue {

get delay() {

return this._delay;

}

set delay( value ) {

this._delay = value;
this._scheduleTimeout( true );

}

get running() {

return this.items.length > 0;

}

constructor() {

this._delay = 500;
this.items = [];
this.itemSet = new Set();
this.timeoutId = null;

}

add( item, callback ) {

const data = {
item,
callback,
time: performance.now(),
reject: null,
resolve: null,
};

data.promise = new Promise( ( resolve, reject ) => {

data.resolve = resolve;
data.reject = reject;

this.items.push( data );
this.itemSet.add( item );
this._scheduleTimeout();

} );

return data.promise;

}

remove( item ) {

const { items, itemSet } = this;
if ( ! itemSet.has( item ) ) {

return;

}

const index = items.findIndex( data => data.item === item );
if ( index !== - 1 ) {

const data = items[ index ];
data.promise.catch( err => {

if ( ! ( err instanceof DelayQueueItemRemovedError ) ) {

throw err;

}

} );
data.reject( new DelayQueueItemRemovedError() );
items.splice( index, 1 );
itemSet.delete( item );

}

}

_scheduleTimeout( force = false ) {

if ( force && this.timeoutId !== null ) {

clearTimeout( this.timeoutId );
this.timeoutId = null;

}

if ( this.timeoutId !== null || this.items.length === 0 ) {

return;

}

const now = performance.now();
const firstItem = this.items[ 0 ];
const readyTime = firstItem.time + this.delay;
const remainingTime = Math.max( 0, readyTime - now );

this.timeoutId = setTimeout( () => {

this._processEntries();

}, remainingTime );

}

_processEntries() {

this.timeoutId = null;

const now = performance.now();
const { items, delay, itemSet } = this;

let toRemove = 0;
for ( let i = 0; i < items.length; i ++ ) {

const { item, callback, resolve, reject, time } = items[ i ];
const readyTime = time + delay;
if ( readyTime > now ) {

break;

}

toRemove ++;
itemSet.delete( item );

let result;
try {

result = callback( item );

} catch ( err ) {

reject( err );
continue;

}

if ( result instanceof Promise ) {

result.then( resolve ).catch( reject );

} else {

resolve( result );

}

}

if ( toRemove > 0 ) {

items.splice( 0, toRemove );

}

this._scheduleTimeout();

}

}
Loading
Loading