mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
fix(infrastructure): stop idle republishing and respect settled terrain
Skip unchanged overlay cohorts while detecting moved positions and lifecycle changes. Defer ground sampling until visible terrain settles, retain the loaded surface as a floor, and preserve roof heights and bounded retries. Keep regression and idle-render gates intact.
This commit is contained in:
1 parent
6dd00d1e76
commit
b5568de9fa
6 files changed
+122
-5
No files matched your search
@@ -13,6 +13,10 @@ of current runtime behavior, see [`docs/CURRENT-STATE.md`](docs/CURRENT-STATE.md
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- Unchanged local infrastructure overlays no longer sustain idle rendering.
|
||||
Ground samples wait for visible terrain to settle and cannot place a marker
|
||||
below its loaded surface; roofs and valid below-sea-level heights are retained.
|
||||
|
||||
- Datacenter and dam marker stems use bounded, zoom-dependent active sets with
|
||||
stable selection during camera motion. Close-up stems scale to the actual
|
||||
camera distance; source totals and submarine cables remain unchanged.
|
||||
|
||||
@@ -8,6 +8,11 @@ and 420 below 200 km. Selection favors existing label priority and nearby
|
||||
features, retains stable choices across small camera movements, and refreshes
|
||||
during continuous motion. Disable/re-enable resets selection cleanly.
|
||||
Close-up stem sizing uses camera-to-feature distance without a 5 km minimum.
|
||||
Unchanged overlay entries do not request another frame; moved positions,
|
||||
changed membership and re-enabling still publish. Ground sampling waits for
|
||||
visible globe tiles to settle and uses valid loaded terrain as a floor while
|
||||
retaining rooftop and below-sea-level elevations. A hidden globe does not
|
||||
constrain photoreal geometry, and retry work remains bounded.
|
||||
The submarine-cable renderer is unchanged. These limits bound active stem work;
|
||||
they do not reduce the materialized entity count or establish an FPS gain.
|
||||
|
||||
|
||||
@@ -296,12 +296,27 @@ try {
|
||||
|
||||
// ── parked-idle honesty: the LOD walk must not force continuous render ─
|
||||
await new Promise((r) => setTimeout(r, 4_000));
|
||||
const idle = await page.evaluate(() => new Promise((resolve) => {
|
||||
const idle = await page.evaluate(async () => {
|
||||
const { getRenderGovernorDiagnostics } = await import('/src/renderGovernor.js');
|
||||
return new Promise((resolve) => {
|
||||
const scene = window.__godsEyeView.viewer.scene;
|
||||
const startedAt = Date.now();
|
||||
const tilesLoadedBefore = scene.globe.tilesLoaded;
|
||||
let renders = 0;
|
||||
const remove = scene.postRender.addEventListener(() => { renders += 1; });
|
||||
setTimeout(() => { remove(); resolve({ renders }); }, 5_000);
|
||||
}));
|
||||
let framesWithPendingTiles = 0;
|
||||
const remove = scene.postRender.addEventListener(() => {
|
||||
renders += 1;
|
||||
if (!scene.globe.tilesLoaded) framesWithPendingTiles += 1;
|
||||
});
|
||||
setTimeout(() => {
|
||||
remove();
|
||||
const governor = getRenderGovernorDiagnostics();
|
||||
resolve({ renders, tilesLoadedBefore, tilesLoadedAfter: scene.globe.tilesLoaded,
|
||||
framesWithPendingTiles, mode: governor.mode, holds: governor.holds,
|
||||
requests: governor.recentRequests.filter(request => request.at >= startedAt) });
|
||||
}, 5_000);
|
||||
});
|
||||
});
|
||||
report(CONTROL
|
||||
? 'parked idle, control (postRender fires / 5s)'
|
||||
: 'parked idle with infra ON (postRender fires / 5s)', idle);
|
||||
|
||||
@@ -225,6 +225,11 @@ export function createLocalInfrastructureOverlayPublisher({
|
||||
let visible = false;
|
||||
let published = false;
|
||||
let destroyed = false;
|
||||
// Local entries have immutable metadata and a mutable Cartesian position.
|
||||
// Snapshot coordinates: retaining only the entry reference would miss a
|
||||
// stem tip moving in place. Republishing an unchanged cohort invalidates
|
||||
// the host and can sustain a render loop when frames exceed the 450 ms walk.
|
||||
let lastPublication = null;
|
||||
const sourceOptions = {
|
||||
cohortLimit: LOCAL_OVERLAY_COHORT_LIMIT,
|
||||
collisionCapacity: LOCAL_OVERLAY_COLLISION_CAPACITY,
|
||||
@@ -239,7 +244,18 @@ export function createLocalInfrastructureOverlayPublisher({
|
||||
},
|
||||
publish(entries) {
|
||||
if (destroyed || !visible) return;
|
||||
if (lastPublication && entries.length === lastPublication.length
|
||||
&& entries.every((entry, index) => {
|
||||
const previous = lastPublication[index];
|
||||
return entry === previous.entry
|
||||
&& entry.position?.x === previous.x
|
||||
&& entry.position?.y === previous.y
|
||||
&& entry.position?.z === previous.z;
|
||||
})) return;
|
||||
host.setEntries(sourceId, entries, sourceOptions);
|
||||
lastPublication = entries.map(entry => ({
|
||||
entry, x: entry.position?.x, y: entry.position?.y, z: entry.position?.z,
|
||||
}));
|
||||
published = entries.length > 0;
|
||||
},
|
||||
hide() {
|
||||
@@ -248,6 +264,7 @@ export function createLocalInfrastructureOverlayPublisher({
|
||||
if (visible) host.setVisible(sourceId, false);
|
||||
visible = false;
|
||||
published = false;
|
||||
lastPublication = null;
|
||||
},
|
||||
destroy() {
|
||||
if (destroyed) return;
|
||||
@@ -255,6 +272,7 @@ export function createLocalInfrastructureOverlayPublisher({
|
||||
if (visible) host.setVisible(sourceId, false);
|
||||
visible = false;
|
||||
published = false;
|
||||
lastPublication = null;
|
||||
destroyed = true;
|
||||
},
|
||||
};
|
||||
@@ -897,6 +915,8 @@ function sampleLocalGroundHeight(viewer, record, now) {
|
||||
if (record.groundSampled || !viewer.scene.sampleHeightSupported) return false;
|
||||
if (now - record.lastGroundSampleMs < GROUND_SAMPLE_RETRY_MS) return false;
|
||||
record.lastGroundSampleMs = now;
|
||||
const globe = viewer.scene.globe;
|
||||
if (globe?.show && globe.tilesLoaded === false) return false;
|
||||
let sampled;
|
||||
try {
|
||||
sampled = viewer.scene.sampleHeight(record.carto, [record.entity]);
|
||||
@@ -904,6 +924,14 @@ function sampleLocalGroundHeight(viewer, record, now) {
|
||||
return false; // tiles not ready; retry on a later bounded update
|
||||
}
|
||||
if (!Number.isFinite(sampled) || Math.abs(sampled) > GROUND_SAMPLE_MAX_ABS_HEIGHT_M) return false;
|
||||
// A coarse scene-depth sample can lie below the terrain already loaded by
|
||||
// the visible globe. Keep that terrain as a floor while allowing roofs and
|
||||
// valid below-sea-level elevations. Photoreal scenes hide the globe, so its
|
||||
// inactive terrain must not constrain their geometry.
|
||||
const terrainHeight = globe?.show ? globe.getHeight?.(record.carto) : undefined;
|
||||
if (Number.isFinite(terrainHeight) && Math.abs(terrainHeight) <= GROUND_SAMPLE_MAX_ABS_HEIGHT_M) {
|
||||
sampled = Math.max(sampled, terrainHeight);
|
||||
}
|
||||
record.groundSampled = true;
|
||||
record.groundHeight = sampled;
|
||||
Cesium.Cartesian3.fromRadians(
|
||||
|
||||
@@ -314,6 +314,33 @@ test('local overlay publisher owns add/remove/visibility lifecycle and becomes i
|
||||
assert.deepEqual(calls[7], ['visible', 'local-datacenters', false]);
|
||||
});
|
||||
|
||||
test('slow parked frames do not keep republishing the same local overlay cohort', async (t) => {
|
||||
const env = await createRealLocalLayerHarness();
|
||||
const clock = installFakeClock(t);
|
||||
t.after(() => { env.layer.destroy(env.viewer); env.cleanup(); });
|
||||
const publications = () => env.hostCalls.filter(([type]) => type === 'entries');
|
||||
env.preRender.raise();
|
||||
assert.equal(publications().length, 1);
|
||||
assert.ok(publications()[0][2].length > 0, 'the initial cohort must be populated');
|
||||
for (let i = 0; i < 6; i++) {
|
||||
clock.advance(1_000); // Every slow frame passes the 450 ms source throttle.
|
||||
env.preRender.raise();
|
||||
}
|
||||
assert.equal(publications().length, 1,
|
||||
'unchanged publication invalidates the real host and requests another slow frame');
|
||||
|
||||
const camera = env.viewer.camera.positionWC;
|
||||
env.viewer.camera.positionWC = Cesium.Cartesian3.add(camera,
|
||||
new Cesium.Cartesian3(10_000, 0, 0), new Cesium.Cartesian3());
|
||||
env.moveEnd.raise();
|
||||
env.preRender.raise();
|
||||
assert.equal(publications().length, 2, 'changed stem positions must still reach the host');
|
||||
env.layer.disable(env.viewer);
|
||||
await env.layer.enable(env.viewer);
|
||||
env.preRender.raise();
|
||||
assert.equal(publications().length, 3, 're-enable must republish after clearing the host');
|
||||
});
|
||||
|
||||
test('real layer disable clears its published host entries and balances settle listeners', async () => {
|
||||
const env = await createRealLocalLayerHarness();
|
||||
env.preRender.raise();
|
||||
@@ -683,6 +710,44 @@ function governorReasons() {
|
||||
return getRenderGovernorDiagnostics().recentRequests.map((entry) => entry.reason);
|
||||
}
|
||||
|
||||
test('ground sampling waits for visible globe tiles before caching a height', async (t) => {
|
||||
const env = await createRealLocalLayerHarness({
|
||||
sampleHeightSupported: true, sampleHeight: () => 117,
|
||||
});
|
||||
const clock = installFakeClock(t);
|
||||
t.after(() => { env.layer.destroy(env.viewer); env.cleanup(); });
|
||||
env.viewer.scene.globe = { show: true, tilesLoaded: false, getHeight: () => 117 };
|
||||
setCameraAltitude(env, 20_000);
|
||||
env.preRender.raise();
|
||||
assert.equal(env.sampleCalls.count, 0, 'streaming terrain must not become a permanent coarse sample');
|
||||
assert.ok(Math.abs(baseHeightM(env)) < 0.01);
|
||||
env.viewer.scene.globe.tilesLoaded = true;
|
||||
clock.advance(2_100);
|
||||
env.preRender.raise();
|
||||
assert.equal(env.sampleCalls.count, 1, 'the existing retry must sample the settled scene');
|
||||
assert.ok(Math.abs(baseHeightM(env) - 117) < 0.01);
|
||||
});
|
||||
|
||||
for (const [terrainHeight, sampledHeight, expectedHeight, globeShown] of [
|
||||
[117, -2238, 117, true],
|
||||
[-400, -410, -400, true],
|
||||
[117, 180, 180, true],
|
||||
[117, -20, -20, false],
|
||||
]) {
|
||||
test(`ground sampling respects terrain ${terrainHeight} and geometry ${sampledHeight} with globe ${globeShown}`, async (t) => {
|
||||
const env = await createRealLocalLayerHarness({
|
||||
sampleHeightSupported: true, sampleHeight: () => sampledHeight,
|
||||
});
|
||||
installFakeClock(t);
|
||||
t.after(() => { env.layer.destroy(env.viewer); env.cleanup(); });
|
||||
env.viewer.scene.globe = { show: globeShown, getHeight: () => terrainHeight };
|
||||
setCameraAltitude(env, 20_000);
|
||||
env.preRender.raise();
|
||||
assert.ok(Math.abs(baseHeightM(env) - expectedHeight) < 0.01,
|
||||
`base ${baseHeightM(env)} must respect the visible terrain without flattening roofs or below-sea-level terrain`);
|
||||
});
|
||||
}
|
||||
|
||||
test('a failed ground sample schedules the retry frame the idle governor would never produce', async (t) => {
|
||||
const env = await createRealLocalLayerHarness({ sampleHeightSupported: true });
|
||||
_resetRenderGovernorForTest();
|
||||
|
||||
@@ -58,7 +58,7 @@ test('infraLodBudget band boundaries are inclusive at the lower edge', () => {
|
||||
assert.deepEqual(infraLodBudget(INFRA_LOD_REGIONAL_HEIGHT_M - 1), { activeLimit: INFRA_LOD_ACTIVE_MAX });
|
||||
});
|
||||
|
||||
test('infraLodBudget treats non-finite / negative height as the global (cheapest) band', () => {
|
||||
test('infraLodBudget uses the global band for non-finite height and clamps negative height to zero', () => {
|
||||
assert.deepEqual(infraLodBudget(undefined), { activeLimit: INFRA_LOD_ACTIVE_MIN });
|
||||
assert.deepEqual(infraLodBudget(NaN), { activeLimit: INFRA_LOD_ACTIVE_MIN });
|
||||
assert.deepEqual(infraLodBudget(Number.POSITIVE_INFINITY), { activeLimit: INFRA_LOD_ACTIVE_MIN });
|
||||
|
||||
Reference in new issue
Block a user