CesiumGS / CesiumGS/cesium-native

Adding a raster overlay to a loaded tileset reloads every loaded tile, causing a visible flicker

Open
#1,422 0 comments 1 reaction 0 assignees View on GitHub
Dominant language
C++
Stars
623
Forks
277
PR merge metrics
No merged PRs in 30d

Description

## Summary

Adding a `RasterOverlay` to a `Tileset` that has already loaded causes every affected tile to have its content unloaded and reloaded from scratch. In a real-time application this is very visible: the geometry disappears and reappears, i.e. the map flickers.

We reproduce this with two different overlay types, via Cesium for Unity:

- `GeoJsonDocumentRasterOverlay`
- WMTS overlays (`CesiumWebMapTileServiceRasterOverlay`, i.e. `WebMapTileServiceRasterOverlay`)

It also recurs on every add rather than being a one-time cost.

## Mechanism

`Cesium3DTilesSelection/src/TilesetContentManager.cpp` (~line 2126):

```cpp
if (status.firstIndexWithMissingProjection) {
// The mesh doesn't have the right texture coordinates for this
// overlay's projection, so we need to kick it back to the unloaded
// state to fix that.
// In the future, we could add the ability to add the required
// texture coordinates without starting over from scratch.
unloadTileContent(tile);
return;
}
```

The "missing projection" state is produced in
`Cesium3DTilesSelection/src/RasterMappedTo3DTile.cpp` (~line 344):

```cpp
} else {
// We don't have a precise rectangle for this projection, which means the
// tile was loaded before we knew we needed this projection. We'll need to
// reload the tile (later).
...
}
```

To be clear, we are not claiming the reload itself is a defect. Having read through it, it looks like a sound tradeoff: overlay texture coordinates are per-vertex glTF attributes (`_CESIUMOVERLAY_n`) generated by `createRasterOverlayTextureCoordinates` by projecting every vertex position, along with the per-projection rectangles in `RasterOverlayDetails`. They are produced in the load thread from the glTF, and downstream consumers do not necessarily keep the glTF afterwards -- Cesium for Unity discards it once the model has been converted to Unity meshes, retaining only the GameObject and a primitive index map. Retrofitting a new UV set onto an already-uploaded mesh would mean reconstructing that data and re-uploading to the GPU. The comment already notes the better answer as future work.

## Why it is painful in practice

**It recurs indefinitely.** A tile only generates texture coordinates for the projections present in the collection at the moment it loads. As the camera moves and tiles stream in and out, newly loaded tiles again lack the second projection's UVs, so the next add reloads them. There is no point at which the application has "paid" the cost.

**GeoJSON overlays cannot avoid it at all.** `GeoJsonDocumentRasterOverlay` hardcodes `GeographicProjection` and offers no override --
`CesiumVectorOverlays/src/GeoJsonDocumentRasterOverlay.cpp` (~line 690):

```cpp
GeoJsonDocumentRasterOverlayTileProvider(...)
: RasterOverlayTileProvider(
pCreator,
parameters,
GeographicProjection(geoJsonOptions.ellipsoid),
projectRectangleSimple(
GeographicProjection(geoJsonOptions.ellipsoid),
GlobeRectangle::MAXIMUM)),
```

`GeoJsonDocumentRasterOverlayOptions` carries only `defaultStyle`, `ellipsoid` and `mipLevels`. Since Web Mercator is the common case for imagery overlays, a GeoJSON overlay on such a tileset is *guaranteed* to introduce a new projection.

**WMTS overlays can avoid it, but only by accident of configuration.** They do expose a projection setting, so an application that keeps every overlay on one projection is fine. Ours mixes them (some layers are configured `Geographic`, others use the `WebMercator` default), and adding a
mismatched one produces the same flicker.

## Use case

Hovering an item in a UI list draws that item's footprint as a GeoJSON overlay over the base map; separately, users toggle WMTS imagery layers on and off. Both make the base map's loaded tiles reload, so the tileset visibly blinks and the interactions feel broken.

We confirmed the diagnosis by matching projections: setting our WMTS base overlay to `Geographic` where the service supports EPSG:4326 removes the flicker completely. That workaround is only available when the imagery service actually serves that projection.

## One thing we could not confirm

We have not isolated a case where an overlay is added with a projection that *is* already in use by the tileset and the reload still happens. Our reading of the code says that case should leave loaded tiles untouched. It would be useful to have that confirmed, since it is the property applications would need to rely on to work around this: **is adding a raster overlay whose projection already matches an existing overlay on the same tileset guaranteed not to reload tile content?**

## Possible resolutions (in rough order of preference)

1. Implement the TODO already noted in the comment: add the missing texture coordinates to an
already-loaded tile without discarding and re-fetching its content. This is the general fix and
covers every overlay type.

2. Allow projections to be pre-registered on the tileset (e.g. via `TilesetOptions`), so an
application that knows it will later add a Geographic overlay can have tiles generate those
texture coordinates up front. This also covers every overlay type and may be considerably simpler
than (1).

3. Expose the projection through `GeoJsonDocumentRasterOverlayOptions`, defaulting to
`GeographicProjection` so existing behaviour is unchanged. This is the smallest change, and it
closes the one case where an application currently has no way out at all. Implementation note:
the coverage rectangle in the tile provider constructor would need to use the selected projection
too; `loadTileImage` already goes through `this->getProjection()` and looks projection-agnostic.

Even without (1) or (2), documenting the constraint would help a great deal -- particularly the
positive guarantee that matching projections avoids the reload.

Contributor guide

Open the contributing guide

Research direction

Start by tracing the missing-projection path in Cesium3DTilesSelection/src/TilesetContentManager.cpp and Cesium3DTilesSelection/src/RasterMappedTo3DTile.cpp, then inspect GeoJsonDocumentRasterOverlay in CesiumVectorOverlays/src/GeoJsonDocumentRasterOverlay.cpp. Determine which proposed resolution is feasible and verify that adding an overlay does not unnecessarily reload already-loaded tiles, while preserving required texture coordinates for newly loaded tiles.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
computer-graphics, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.