mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
fix(layers): release bundled GeoJSON entities on toggle-off instead of hiding them
Toggling datacenters, dams, or submarine cables off only set `show = false` on their data sources. Cesium's DataSourceDisplay still walks every visualizer of a hidden source each frame, and the ~11.5k hidden entities (hundreds of MB) stayed resident, so the whole scene rendered ~2x slower for the rest of the session after a single on/off round trip (owner field test 2026-09-13). disable() now removes the data sources from the viewer and drops the built records; the parsed dataset (NDJSON features / TeleGeography JSON) is cached so enable() rebuilds without a refetch or re-parse. A disable that lands while a load is in flight releases the finished build instead of parking it hidden. destroy() clears the cache. Measured with a headful Chrome harness (flights+military+satellites on, camera rotating, 250-frame windows), before -> after: - entities resident after OFF: 11,537 -> 0 - DataSourceDisplay.update per frame after OFF: 1.09 ms -> 0.02 ms - scene.render per frame after OFF: 13.9 ms (control 6.6 ms) -> 5.2 ms (fresh baseline 5.0 ms in the same run) - JS heap 30 s after OFF: 1.6 GB and climbing -> 342 MB after GC Tests: the GeoJSON lifecycle and cables suites assert disable leaves zero sources/contexts and re-enable rebuilds with fetch called once.
This commit is contained in:
1 parent
ca5f5fff93
commit
499e1168a7
4 files changed
+190
-32
No files matched your search
@@ -354,6 +354,17 @@ export function createLocalGeoJsonLayer(
|
||||
let _destroyed = false;
|
||||
let _loadPromise = null;
|
||||
let _loadController = null;
|
||||
/**
|
||||
* The parsed bundled dataset, kept across disable/enable so a re-enable
|
||||
* rebuilds entities without refetching. The Cesium entities themselves are
|
||||
* NOT kept: a hidden data source still costs every frame (DataSourceDisplay
|
||||
* walks every visualizer over every entity regardless of `show`), and the
|
||||
* ~11k entities of the three bundled layers hold hundreds of MB, so leaving
|
||||
* them parked after a toggle-off made the whole scene render ~2× slower for
|
||||
* the rest of the session (owner field test 2026-09-13).
|
||||
* @type {Array<object>|null}
|
||||
*/
|
||||
let _cachedFeatures = null;
|
||||
/**
|
||||
* Globe-LOD active set: the record ids allowed to carry a live stem right
|
||||
* now. This bounds geometry refreshes and ground-sample work to the
|
||||
@@ -432,6 +443,27 @@ export function createLocalGeoJsonLayer(
|
||||
host: overlayHost,
|
||||
});
|
||||
|
||||
/**
|
||||
* Take the built entities out of the scene entirely. The parsed features
|
||||
* stay cached, so the next enable() rebuilds without a fetch; what must not
|
||||
* survive a disable is the per-frame visualizer walk and the entity memory.
|
||||
* @param {object} viewer
|
||||
*/
|
||||
const releaseDataSource = (viewer) => {
|
||||
if (!_dataSource) return;
|
||||
const source = _dataSource;
|
||||
_dataSource = null;
|
||||
_stemRecords = [];
|
||||
_stemGeometryDirty = true;
|
||||
_lastVisibilityUpdate = Number.NEGATIVE_INFINITY;
|
||||
removeEntityContextsForLayer(id);
|
||||
try {
|
||||
viewer?.dataSources?.remove(source, true);
|
||||
} catch {
|
||||
/* already gone */
|
||||
}
|
||||
};
|
||||
|
||||
const disableLayer = (viewer) => {
|
||||
_enabled = false;
|
||||
clearGroundRetryRender();
|
||||
@@ -440,7 +472,7 @@ export function createLocalGeoJsonLayer(
|
||||
_lastLodBudgetLimit = 0;
|
||||
_lodComputed = false;
|
||||
_lastLodProbeMs = Number.NEGATIVE_INFINITY;
|
||||
if (_dataSource) _dataSource.show = false;
|
||||
releaseDataSource(viewer);
|
||||
_overlayPublisher.hide();
|
||||
clearSelectedEntityContextForLayer(id);
|
||||
if (viewer?.selectedEntity?.__localLayerId === id) {
|
||||
@@ -529,20 +561,26 @@ export function createLocalGeoJsonLayer(
|
||||
// windows (before vs after the add settles) need different cleanup.
|
||||
let addedToScene = false;
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
signal: _loadController.signal,
|
||||
});
|
||||
if (_destroyed) return;
|
||||
// A 404 returns an HTML body that would otherwise die in JSON.parse
|
||||
// one line later, reported as a parse error for a missing file.
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status ?? '?'}`);
|
||||
}
|
||||
const text = await response.text();
|
||||
if (_destroyed) return;
|
||||
const lines = text.split('\n').filter((l) => l.trim().length > 0);
|
||||
let features = _cachedFeatures;
|
||||
if (!features) {
|
||||
const response = await fetch(url, {
|
||||
signal: _loadController.signal,
|
||||
});
|
||||
if (_destroyed) return;
|
||||
// A 404 returns an HTML body that would otherwise die in JSON.parse
|
||||
// one line later, reported as a parse error for a missing file.
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status ?? '?'}`);
|
||||
}
|
||||
const text = await response.text();
|
||||
if (_destroyed) return;
|
||||
const lines = text
|
||||
.split('\n')
|
||||
.filter((l) => l.trim().length > 0);
|
||||
|
||||
const features = lines.map((line) => JSON.parse(line));
|
||||
features = lines.map((line) => JSON.parse(line));
|
||||
_cachedFeatures = features;
|
||||
}
|
||||
|
||||
const geojson = {
|
||||
type: 'FeatureCollection',
|
||||
@@ -991,9 +1029,14 @@ export function createLocalGeoJsonLayer(
|
||||
}
|
||||
|
||||
// Honor a disable() that landed while we were awaiting the fetch/parse:
|
||||
// disable() runs before _dataSource exists, so its show=false is a no-op —
|
||||
// reading _enabled here (rather than forcing true) respects the toggle-off.
|
||||
if (_dataSource) _dataSource.show = _enabled;
|
||||
// disable() runs before _dataSource exists, so its release is a no-op —
|
||||
// release the finished build here instead of parking it hidden in the
|
||||
// scene (the parsed features stay cached for the next enable).
|
||||
if (!_enabled) {
|
||||
releaseDataSource(viewer);
|
||||
return;
|
||||
}
|
||||
if (_dataSource) _dataSource.show = true;
|
||||
viewer.scene.requestRender?.();
|
||||
},
|
||||
|
||||
@@ -1016,6 +1059,7 @@ export function createLocalGeoJsonLayer(
|
||||
}
|
||||
_overlayPublisher.destroy();
|
||||
_dataSource = null;
|
||||
_cachedFeatures = null;
|
||||
_stemRecords = [];
|
||||
_count = 0;
|
||||
_lastUpdate = null;
|
||||
|
||||
Reference in new issue
Block a user