Summary
MosaicLayer does not support a non-zero debounceTime. Setting one causes source tiles to never load. The prop is therefore intentionally not exposed on MosaicLayer (removed in #557). It continues to work on COGLayer / MultiCOGLayer, which are normal hierarchical RasterTileLayer tile pyramids.
Motivation
For a mosaic of many COGs, panning sweeps the viewport across lots of sources and opens GeoTIFFs for every intermediate viewport. A debounce would let us open sources only for where the user stops, instead of churning through everything passed over. deck.gl's TileLayer exposes debounceTime for exactly this, but it's incompatible with MosaicLayer's tile model (see below).
What we found
MosaicTileset2D uses a flat, zoomless tile model: every source is a tile at zoom 0, and getParentIndex returns the tile itself, so there is no parent/child hierarchy.
- A single static
update() actually loads fine with a non-zero debounceTime — verified by tracing: _getTile enqueues the request, updateTileStates() sets isSelected = true, and when the debounce timer fires the scheduler sees priority isSelected ? 1 : -1 === 1 and grants it.
- The failure appears under repeated
update() calls (viewport churn from panning, map settle, and/or the interleaved overlay's per-frame repaints). deck.gl's RequestScheduler re-evaluates isSelected ? 1 : -1 at issue time and cancels any tile not selected at that instant; a cancelled tile's needsReload becomes true, so it's re-scheduleRequested as a new handle, which resets the debounce timer (clearTimeout + setTimeout). The net effect is that the debounce window never closes on a stable selected set, so requests are never issued.
- The flat model also removes deck.gl's normal cushion: with no ancestor/child tree, unloaded tiles never get
isVisible: true and there are no lower-res placeholders to show while debouncing.
Relevant source:
Tile2DHeader._loadData priority tile => tile.isSelected ? 1 : -1 (no token ⇒ cancelled)
RequestScheduler._issueNewRequests (clearTimeout/setTimeout(debounceTime) on every new enqueue)
Tileset2D._getTile re-loads tiles whose needsReload (incl. _isCancelled) is true
MosaicTileset2D (getTileZoom → 0, getParentIndex → self)
Caveat: the single-update path was traced from source; the exact dominant trigger under the live render loop (initial-settle churn vs. the cancel→reschedule loop) was not instrumented.
Possible directions
- Implement debouncing at the
MosaicLayer getSource level instead of forwarding to deck.gl's scheduler.
- Give
MosaicTileset2D a stable selection / a non-degenerate tree so deck.gl's debounced scheduler behaves.
- Upstream: a deck.gl scheduler mode that doesn't reset the debounce timer for already-queued handles, or doesn't cancel on transient deselection.
Context: split out of #557.
Summary
MosaicLayerdoes not support a non-zerodebounceTime. Setting one causes source tiles to never load. The prop is therefore intentionally not exposed onMosaicLayer(removed in #557). It continues to work onCOGLayer/MultiCOGLayer, which are normal hierarchicalRasterTileLayertile pyramids.Motivation
For a mosaic of many COGs, panning sweeps the viewport across lots of sources and opens GeoTIFFs for every intermediate viewport. A debounce would let us open sources only for where the user stops, instead of churning through everything passed over. deck.gl's
TileLayerexposesdebounceTimefor exactly this, but it's incompatible withMosaicLayer's tile model (see below).What we found
MosaicTileset2Duses a flat, zoomless tile model: every source is a tile at zoom 0, andgetParentIndexreturns the tile itself, so there is no parent/child hierarchy.update()actually loads fine with a non-zerodebounceTime— verified by tracing:_getTileenqueues the request,updateTileStates()setsisSelected = true, and when the debounce timer fires the scheduler sees priorityisSelected ? 1 : -1 === 1and grants it.update()calls (viewport churn from panning, map settle, and/or the interleaved overlay's per-frame repaints). deck.gl'sRequestSchedulerre-evaluatesisSelected ? 1 : -1at issue time and cancels any tile not selected at that instant; a cancelled tile'sneedsReloadbecomes true, so it's re-scheduleRequested as a new handle, which resets the debounce timer (clearTimeout+setTimeout). The net effect is that the debounce window never closes on a stable selected set, so requests are never issued.isVisible: trueand there are no lower-res placeholders to show while debouncing.Relevant source:
Tile2DHeader._loadDataprioritytile => tile.isSelected ? 1 : -1(no token ⇒ cancelled)RequestScheduler._issueNewRequests(clearTimeout/setTimeout(debounceTime)on every new enqueue)Tileset2D._getTilere-loads tiles whoseneedsReload(incl._isCancelled) is trueMosaicTileset2D(getTileZoom→ 0,getParentIndex→ self)Caveat: the single-update path was traced from source; the exact dominant trigger under the live render loop (initial-settle churn vs. the cancel→reschedule loop) was not instrumented.
Possible directions
MosaicLayergetSourcelevel instead of forwarding to deck.gl's scheduler.MosaicTileset2Da stable selection / a non-degenerate tree so deck.gl's debounced scheduler behaves.Context: split out of #557.