CesiumGS / CesiumGS/cesium-unity

Adding a CesiumWebMapTileServiceRasterOverlay or CesiumGeoJsonDocumentRasterOverlay at runtime reloads every loaded tile (flicker)

Open
#712 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
C#
Stars
535
Forks
132
Avg merge
6h 45m
Merged PRs (30d)
1

Description

## Summary

Adding a `CesiumRasterOverlay` to a `Cesium3DTileset` that has already loaded causes every loaded tile to be destroyed and rebuilt. The tileset visibly blinks. The same happens on remove and re-add, and it recurs on every add rather than being a one-time cost.

We reproduce this with both overlay types we use:

- `CesiumGeoJsonDocumentRasterOverlay`
- `CesiumWebMapTileServiceRasterOverlay`

The underlying mechanism lives in cesium-native and, having read through it, appears to be a sound
tradeoff rather than a defect (details below). We are raising it here because from the Unity side it
is neither discoverable nor, for GeoJSON overlays, avoidable:

1. Nothing in `CesiumRasterOverlay`, `CesiumGeoJsonDocumentRasterOverlay` or
`CesiumWebMapTileServiceRasterOverlay` suggests that adding an overlay can destroy tile content,
or that projection compatibility between overlays on the same tileset matters at all.
2. `CesiumGeoJsonDocumentRasterOverlay` exposes no projection setting, so even once you understand
the cause there is no way to avoid it from Unity.

So this is a documentation request plus a small API request, not a bug report.

Our use cases: hovering an item in a UI list draws that item's footprint as a GeoJSON overlay on the
base map, and users toggle WMTS imagery layers on and off. Both make the base map flicker, which
makes them unusable as interactive affordances.

## It is not RecreateTileset

Worth stating up front, since it is the natural first guess: nothing calls
`Cesium3DTileset.RecreateTileset()` on this path. We checked every caller.

- The `CesiumRasterOverlay` base class only ever calls `Refresh()` (`RemoveFromTileset()` +
`AddToTileset()`), never `RecreateTileset` -- see `Source/Runtime/CesiumRasterOverlay.cs`.
- The per-overlay `AddToTileset` / `RemoveFromTileset` implementations operate on the live
`Tileset`'s overlay collection only.
- `RecreateTileset` is called only from `Cesium3DTileset` property setters, the "Refresh Tileset"
inspector button, `CesiumGeoreference.ReloadEllipsoid`, and some editor-window paths
(`CesiumEditorWindowImpl.cpp`, `IonAssetsTreeViewImpl.cpp`, `SelectIonTokenWindowImpl.cpp`). None
are in play here, and our project never calls it.

What actually happens is per-tile content reload in
`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;
}
```

Adding an overlay whose projection is not among those a loaded tile generated texture coordinates
for forces that tile to reload. This looks justified: overlay texture coordinates are per-vertex
glTF attributes (`_CESIUMOVERLAY_n`) generated during load, and Cesium for Unity discards the glTF
once conversion is done -- `CesiumGltfGameObject`
(`native~/src/Runtime/UnityPrepareRendererResources.h`) retains only the `GameObject` and
`primitiveInfos`. Retrofitting a UV set onto an already-uploaded Unity mesh would mean
reconstructing that data and re-uploading to the GPU.

Filed against cesium-native: [
](https://github.com/CesiumGS/cesium-native/issues/1422)
## Why each overlay type hits it

**GeoJSON overlays always hit it.** `GeoJsonDocumentRasterOverlay` hardcodes `GeographicProjection`
with no override, while `CesiumWebMapTileServiceRasterOverlay.projection` defaults to `WebMercator`
(`Source/Runtime/CesiumWebMapTileServiceRasterOverlay.cs`). On a Web Mercator tileset the mismatch
is guaranteed.

**WMTS overlays hit it when projections are mixed.** They do expose `projection`, so an application
that keeps every overlay on one projection is fine. Ours mixes them -- our configuration sets
`Geographic` for some layers and leaves others at the `WebMercator` default -- and adding a
mismatched one produces the same flicker.

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, and confirmation of that would be valuable, since it is the property applications
would need to rely on to work around this.

## Steps to reproduce

With a GeoJSON overlay:

1. Create a `Cesium3DTileset` with a `CesiumWebMapTileServiceRasterOverlay` left at its default
`WebMercator` projection.
2. Enter play mode and let the tileset load.
3. At runtime, `AddComponent()` on the same GameObject and
assign a valid `document`.
4. Observe the tileset's tiles disappear and reload.

With a WMTS overlay:

1. Same tileset and `WebMercator` base overlay as above, fully loaded.
2. At runtime, `AddComponent()` on the same GameObject,
configure it, set `projection = Geographic`, and enable it.
3. Observe the same reload.

Expected: the new overlay is draped over the existing tiles.
Actual: all loaded tile GameObjects are destroyed and rebuilt; the tileset flickers.

## Workarounds we tried

- Reusing a single overlay component and swapping only `document` does not help. The document setter
calls `Refresh()`, which removes and re-adds the overlay, so the projection is reintroduced as
"missing" for any tile that loaded in the meantime.
- Keeping an overlay permanently attached so its projection is always registered does avoid the
reload, but for GeoJSON overlays only if it always holds a non-empty document -- an empty
`FeatureCollection` crashes the process (filed separately against cesium-native: ).
- Matching the projections (setting the WMTS overlay to `Geographic` where the service supports
EPSG:4326) removes the flicker completely, which confirms the diagnosis. This is only possible
when the imagery service actually serves that projection.

## What would help

- **Documentation.** A note on `CesiumRasterOverlay` -- the base class, since this affects every
subclass -- that adding an overlay whose projection is not already in use by the tileset will
force loaded tiles to reload, and that this makes such overlays unsuitable for frequently toggled
visuals. This alone would have saved us a lot of investigation. Ideally it would also state the
positive guarantee: that matching projections avoids the reload.
- **A projection setting on `CesiumGeoJsonDocumentRasterOverlay`,** once the corresponding option
exists in `GeoJsonDocumentRasterOverlayOptions` in cesium-native (see linked issue). WMTS
overlays already expose `projection`, so applications can avoid the mismatch there; GeoJSON
overlays are currently the only ones with no way out.

Contributor guide

Open the contributing guide

Research direction

Start with Source/Runtime/CesiumRasterOverlay.cs, CesiumGeoJsonDocumentRasterOverlay.cs, and CesiumWebMapTileServiceRasterOverlay.cs, then review the linked cesium-native issue about GeoJSON projection options. Reproduce the runtime add/remove steps and verify projection matching behavior. Done means documenting the reload constraint and providing GeoJSON projection control when the native option is available.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, unity
Domain
game-dev
Issue type
Documentation
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.