Skip to content

Commit 6f4e0e7

Browse files
authored
GeoJSONOverlay, MVTOverlay: Improved redraw performance (#1588)
* Track visible regions * Fix tracking * Adjust redraw logic * Only redraw visible tiles * Add dirty tracking * Progressive updates * Add new priority queue function, flush * Cleanup * Adjust GeoJSONOverlay redraw logic * Update d.ts * Cleanup * Remove empty line
1 parent 13040e5 commit 6f4e0e7

8 files changed

Lines changed: 315 additions & 61 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export class PriorityQueue {
99
get running(): boolean;
1010

1111
sort() : void;
12+
flush( item : any ) : any;
1213
has( item : any ) : boolean;
1314
add( item : any, callback : ( item : any ) => any ) : Promise< any >;
1415
remove( item : any ) : void;

src/core/renderer/utilities/PriorityQueue.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,54 @@ export class PriorityQueue {
305305

306306
}
307307

308+
/**
309+
* Immediately runs the callback for the given item, removing it from the queue.
310+
* Does nothing if the item is not queued.
311+
* @param {any} item
312+
* @returns {Promise<any>|any}
313+
*/
314+
flush( item ) {
315+
316+
const { items, callbacks } = this;
317+
const index = items.indexOf( item );
318+
if ( ! callbacks.has( item ) ) {
319+
320+
return;
321+
322+
}
323+
324+
const { callback, resolve, reject } = callbacks.get( item );
325+
callbacks.delete( item );
326+
items.splice( index, 1 );
327+
328+
let result;
329+
try {
330+
331+
result = callback( item );
332+
333+
} catch ( err ) {
334+
335+
reject( err );
336+
return;
337+
338+
}
339+
340+
if ( result instanceof Promise ) {
341+
342+
result
343+
.then( resolve )
344+
.catch( reject );
345+
346+
} else {
347+
348+
resolve( result );
349+
350+
}
351+
352+
return result;
353+
354+
}
355+
308356
/**
309357
* Schedules a deferred call to `tryRunJobs` via `schedulingCallback`.
310358
*/

src/three/plugins/images/ImageOverlayPlugin.js

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export class ImageOverlayPlugin {
8787
this.processQueue = null;
8888
this._onUpdateAfter = null;
8989
this._onTileDownloadStart = null;
90+
this._onTileVisibilityChange = null;
9091
this._virtualChildResetId = 0;
9192
this._bytesUsed = new WeakMap();
9293

@@ -222,8 +223,20 @@ export class ImageOverlayPlugin {
222223

223224
};
224225

226+
this._onTileVisibilityChange = ( { tile, visible } ) => {
227+
228+
this.overlayInfo.forEach( ( { tileInfo }, overlay ) => {
229+
230+
const info = tileInfo.get( tile );
231+
overlay.setRegionVisible( info.range, visible );
232+
233+
} );
234+
235+
};
236+
225237
tiles.addEventListener( 'update-after', this._onUpdateAfter );
226238
tiles.addEventListener( 'tile-download-start', this._onTileDownloadStart );
239+
tiles.addEventListener( 'tile-visibility-change', this._onTileVisibilityChange );
227240

228241
this.overlays.forEach( overlay => {
229242

@@ -403,6 +416,8 @@ export class ImageOverlayPlugin {
403416
} );
404417

405418
tiles.removeEventListener( 'update-after', this._onUpdateAfter );
419+
tiles.removeEventListener( 'tile-download-start', this._onTileDownloadStart );
420+
tiles.removeEventListener( 'tile-visibility-change', this._onTileVisibilityChange );
406421

407422
this.resetVirtualChildren( true );
408423

@@ -1110,6 +1125,12 @@ export class ImageOverlayPlugin {
11101125

11111126
}
11121127

1128+
if ( tile.traversal.visible ) {
1129+
1130+
overlay.setRegionVisible( info.range, true );
1131+
1132+
}
1133+
11131134
// if the image projection is outside the 0, 1 uvw range or there are no textures to draw in
11141135
// the tiled image set the don't allocate a texture for it.
11151136
let target = null;
@@ -1306,6 +1327,7 @@ export class ImageOverlay {
13061327
this._whenReady = null;
13071328
this.isReady = false;
13081329
this.isInitialized = false;
1330+
this._visibleRegionCounts = new Map();
13091331

13101332
}
13111333

@@ -1383,6 +1405,32 @@ export class ImageOverlay {
13831405

13841406
}
13851407

1408+
setRegionVisible( range, visible ) {
1409+
1410+
const { _visibleRegionCounts } = this;
1411+
const key = range.join( '_' );
1412+
let entry = _visibleRegionCounts.get( key );
1413+
if ( ! entry ) {
1414+
1415+
entry = { range: [ ...range ], count: 0 };
1416+
_visibleRegionCounts.set( key, entry );
1417+
1418+
}
1419+
1420+
entry.count += visible ? 1 : - 1;
1421+
1422+
if ( entry.count < 0 ) {
1423+
1424+
throw new Error();
1425+
1426+
} else if ( entry.count === 0 ) {
1427+
1428+
_visibleRegionCounts.delete( key );
1429+
1430+
}
1431+
1432+
}
1433+
13861434
}
13871435

13881436
/**
@@ -1682,6 +1730,10 @@ export class GeoJSONOverlay extends ImageOverlay {
16821730
super( options );
16831731
this.imageSource = new GeoJSONImageSource( options );
16841732

1733+
this._redrawQueue = new PriorityQueue();
1734+
this._redrawQueue.maxJobs = 4;
1735+
this._redrawQueue.priorityCallback = () => 0;
1736+
16851737
}
16861738

16871739
_init() {
@@ -1727,9 +1779,52 @@ export class GeoJSONOverlay extends ImageOverlay {
17271779

17281780
}
17291781

1782+
setRegionVisible( range, visible ) {
1783+
1784+
super.setRegionVisible( range, visible );
1785+
1786+
if ( visible ) {
1787+
1788+
const { _redrawQueue } = this;
1789+
const key = range.join( '_' );
1790+
if ( _redrawQueue.has( key ) ) {
1791+
1792+
_redrawQueue.flush( key );
1793+
1794+
}
1795+
1796+
}
1797+
1798+
}
1799+
17301800
redraw() {
17311801

1732-
this.imageSource.redraw();
1802+
const {
1803+
imageSource,
1804+
_redrawQueue,
1805+
_visibleRegionCounts,
1806+
} = this;
1807+
1808+
for ( const { range } of _visibleRegionCounts.values() ) {
1809+
1810+
imageSource.redraw( ...range );
1811+
1812+
}
1813+
1814+
imageSource.forEachItem( ( _, args ) => {
1815+
1816+
const key = args.join( '_' );
1817+
if ( ! _visibleRegionCounts.has( key ) && ! _redrawQueue.has( key ) ) {
1818+
1819+
_redrawQueue.add( key, () => {
1820+
1821+
imageSource.redraw( ...args );
1822+
1823+
} );
1824+
1825+
}
1826+
1827+
} );
17331828

17341829
}
17351830

src/three/plugins/images/MVTOverlay.js

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { ImageOverlay } from './ImageOverlayPlugin.js';
33
import { MVTImageSource } from './sources/MVTImageSource.js';
44
import { PMTilesImageSource } from './sources/PMTilesImageSource.js';
5+
import { PriorityQueue } from '3d-tiles-renderer/core';
56

67
/**
78
* @callback MVTGetStyleCallback
@@ -64,6 +65,10 @@ export class MVTOverlay extends ImageOverlay {
6465
super( options );
6566
this.imageSource = options.imageSource ?? new MVTImageSource( options );
6667

68+
this._redrawQueue = new PriorityQueue();
69+
this._redrawQueue.maxJobs = 4;
70+
this._redrawQueue.priorityCallback = () => 0;
71+
6772
}
6873

6974
_init() {
@@ -140,9 +145,52 @@ export class MVTOverlay extends ImageOverlay {
140145

141146
}
142147

148+
setRegionVisible( range, visible ) {
149+
150+
super.setRegionVisible( range, visible );
151+
152+
if ( visible ) {
153+
154+
const { _redrawQueue } = this;
155+
const key = range.join( '_' ) + '_' + this.calculateLevel( range );
156+
if ( _redrawQueue.has( key ) ) {
157+
158+
_redrawQueue.flush( key );
159+
160+
}
161+
162+
}
163+
164+
}
165+
143166
redraw() {
144167

145-
this.imageSource.redraw();
168+
const {
169+
imageSource,
170+
_redrawQueue,
171+
_visibleRegionCounts,
172+
} = this;
173+
174+
for ( const { range } of _visibleRegionCounts.values() ) {
175+
176+
imageSource.redraw( ...range, this.calculateLevel( range ) );
177+
178+
}
179+
180+
imageSource.forEachItem( ( _, args ) => {
181+
182+
const key = args.join( '_' );
183+
if ( ! _visibleRegionCounts.has( key ) && ! _redrawQueue.has( key ) ) {
184+
185+
_redrawQueue.add( key, () => {
186+
187+
imageSource.redraw( ...args );
188+
189+
} );
190+
191+
}
192+
193+
} );
146194

147195
}
148196

src/three/plugins/images/sources/GeoJSONImageSource.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,17 @@ export class GeoJSONImageSource extends RegionImageSource {
122122

123123
}
124124

125-
redraw() {
125+
redraw( ...args ) {
126126

127-
this._updateCache( true );
128-
this.forEachItem( ( tex, args ) => {
127+
const tex = this.get( ...args );
128+
if ( ! tex ) {
129129

130-
this._drawToCanvas( tex.image, args );
131-
tex.needsUpdate = true;
130+
return;
132131

133-
} );
132+
}
133+
134+
this._drawToCanvas( tex.image, args );
135+
tex.needsUpdate = true;
134136

135137
}
136138

0 commit comments

Comments
 (0)