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:
- 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.
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):
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:
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:
- Create a
Cesium3DTileset with a CesiumWebMapTileServiceRasterOverlay left at its default
WebMercator projection.
- Enter play mode and let the tileset load.
- At runtime,
AddComponent<CesiumGeoJsonDocumentRasterOverlay>() on the same GameObject and
assign a valid document.
- Observe the tileset's tiles disappear and reload.
With a WMTS overlay:
- Same tileset and
WebMercator base overlay as above, fully loaded.
- At runtime,
AddComponent<CesiumWebMapTileServiceRasterOverlay>() on the same GameObject,
configure it, set projection = Geographic, and enable it.
- 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.
Summary
Adding a
CesiumRasterOverlayto aCesium3DTilesetthat 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:
CesiumGeoJsonDocumentRasterOverlayCesiumWebMapTileServiceRasterOverlayThe 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:
CesiumRasterOverlay,CesiumGeoJsonDocumentRasterOverlayorCesiumWebMapTileServiceRasterOverlaysuggests that adding an overlay can destroy tile content,or that projection compatibility between overlays on the same tileset matters at all.
CesiumGeoJsonDocumentRasterOverlayexposes no projection setting, so even once you understandthe 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.CesiumRasterOverlaybase class only ever callsRefresh()(RemoveFromTileset()+AddToTileset()), neverRecreateTileset-- seeSource/Runtime/CesiumRasterOverlay.cs.AddToTileset/RemoveFromTilesetimplementations operate on the liveTileset's overlay collection only.RecreateTilesetis called only fromCesium3DTilesetproperty setters, the "Refresh Tileset"inspector button,
CesiumGeoreference.ReloadEllipsoid, and some editor-window paths(
CesiumEditorWindowImpl.cpp,IonAssetsTreeViewImpl.cpp,SelectIonTokenWindowImpl.cpp). Noneare in play here, and our project never calls it.
What actually happens is per-tile content reload in
Cesium3DTilesSelection/src/TilesetContentManager.cpp(~line 2126):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 glTFonce conversion is done --
CesiumGltfGameObject(
native~/src/Runtime/UnityPrepareRendererResources.h) retains only theGameObjectandprimitiveInfos. Retrofitting a UV set onto an already-uploaded Unity mesh would meanreconstructing that data and re-uploading to the GPU.
Filed against cesium-native:
Why each overlay type hits it
GeoJSON overlays always hit it.
GeoJsonDocumentRasterOverlayhardcodesGeographicProjectionwith no override, while
CesiumWebMapTileServiceRasterOverlay.projectiondefaults toWebMercator(
Source/Runtime/CesiumWebMapTileServiceRasterOverlay.cs). On a Web Mercator tileset the mismatchis guaranteed.
WMTS overlays hit it when projections are mixed. They do expose
projection, so an applicationthat keeps every overlay on one projection is fine. Ours mixes them -- our configuration sets
Geographicfor some layers and leaves others at theWebMercatordefault -- and adding amismatched 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:
Cesium3DTilesetwith aCesiumWebMapTileServiceRasterOverlayleft at its defaultWebMercatorprojection.AddComponent<CesiumGeoJsonDocumentRasterOverlay>()on the same GameObject andassign a valid
document.With a WMTS overlay:
WebMercatorbase overlay as above, fully loaded.AddComponent<CesiumWebMapTileServiceRasterOverlay>()on the same GameObject,configure it, set
projection = Geographic, and enable it.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
documentdoes not help. The document settercalls
Refresh(), which removes and re-adds the overlay, so the projection is reintroduced as"missing" for any tile that loaded in the meantime.
reload, but for GeoJSON overlays only if it always holds a non-empty document -- an empty
FeatureCollectioncrashes the process (filed separately against cesium-native: ).Geographicwhere the service supportsEPSG:4326) removes the flicker completely, which confirms the diagnosis. This is only possible
when the imagery service actually serves that projection.
What would help
CesiumRasterOverlay-- the base class, since this affects everysubclass -- 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.
CesiumGeoJsonDocumentRasterOverlay, once the corresponding optionexists in
GeoJsonDocumentRasterOverlayOptionsin cesium-native (see linked issue). WMTSoverlays already expose
projection, so applications can avoid the mismatch there; GeoJSONoverlays are currently the only ones with no way out.