mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
perf(weather): wake a staged frame on a 150 ms timer, not every render (#1345)
Wake staged weather imagery on a bounded timer instead of requesting every render. Contribution by @nu-stats.
This commit is contained in:
1 parent
2b2dad5bbe
commit
06c51d3438
4 files changed
+71
-3
No files matched your search
@@ -0,0 +1,3 @@
|
||||
- Wake staged weather imagery on a 150 ms timer while waiting for readiness,
|
||||
avoiding a continuous render loop and cancelling the wake when work ends
|
||||
(@nu-stats, #1345).
|
||||
@@ -306,6 +306,10 @@ catalog construction owns each instance.
|
||||
Wind animates one forecast without advancing forecast time. Separate Weather
|
||||
observation layers provide radar and satellite history; none claims measured cloud volume.
|
||||
Weather imagery releases its camera move listener on disable and registers it once on re-enable. Disabling also clears active imagery work.
|
||||
A staged weather frame with loaded tiles and no pending requests rechecks
|
||||
readiness on a single 150 ms wake timer instead of requesting every render.
|
||||
The existing readiness conditions and two settled frames still govern promotion;
|
||||
completion, cancellation, replacement and teardown clear the wake timer.
|
||||
Weather imagery drapes onto the globe basemap. On photorealistic 3D Tiles each
|
||||
observed product is instead a raised, translucent shell: one rectangle over the
|
||||
product bounds at a fixed height (global infrared 5.5 km, regional infrared
|
||||
|
||||
@@ -14,6 +14,8 @@ const GLOBAL_TILE_MAXIMUM_LEVEL = 3;
|
||||
// Six decoded 2048×1024 canvases (8 MiB each) bound the cache below 50 MiB.
|
||||
const MAX_MOSAICS = 6;
|
||||
const MAX_PREFETCH_TILES = 8;
|
||||
// How often a staged frame waiting out its quiet interval rechecks readiness.
|
||||
export const READY_WAKE_MS = 150;
|
||||
|
||||
/** Globe imagery: own at most a displayed and a staging frame. Use native Cesium
|
||||
* tile scheduling, projection and texture disposal; the application clock is never touched. */
|
||||
@@ -126,6 +128,7 @@ function createGlobeRendering({
|
||||
frame.offRender?.();
|
||||
frame.offCamera?.();
|
||||
clearTimeout(frame.timeout);
|
||||
clearTimeout(frame.wake);
|
||||
for (const request of frame.requests) request.cancel?.();
|
||||
frame.requests.clear();
|
||||
frame.retries.clear();
|
||||
@@ -277,6 +280,8 @@ function createGlobeRendering({
|
||||
frame.offCamera?.();
|
||||
frame.offCamera = null;
|
||||
clearTimeout(frame.timeout);
|
||||
clearTimeout(frame.wake);
|
||||
frame.wake = null;
|
||||
frame.offAbort?.();
|
||||
if (ok) {
|
||||
lastError = null;
|
||||
@@ -450,8 +455,20 @@ function createGlobeRendering({
|
||||
if (++settled >= 2) finish(true);
|
||||
else viewer.scene.requestRender();
|
||||
} else settled = 0;
|
||||
if (incoming === frame && frame.pending === 0 && frame.loaded > 0)
|
||||
viewer.scene.requestRender();
|
||||
// Waiting out the quiet interval needs another readiness check
|
||||
// later, not a render every frame: wake on a timer so the render
|
||||
// governor's frame cap still holds while a frame is staged.
|
||||
if (
|
||||
incoming === frame &&
|
||||
frame.pending === 0 &&
|
||||
frame.loaded > 0 &&
|
||||
!frame.wake
|
||||
)
|
||||
frame.wake = setTimeout(() => {
|
||||
frame.wake = null;
|
||||
if (!frame.closed && incoming === frame)
|
||||
viewer.scene.requestRender();
|
||||
}, READY_WAKE_MS);
|
||||
});
|
||||
viewer.scene.requestRender();
|
||||
onChange();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { createWeatherRendering } from './rendering.js';
|
||||
import { createWeatherRendering, READY_WAKE_MS } from './rendering.js';
|
||||
import { createWeatherLayer } from './index.js';
|
||||
import { createWeatherClock } from './clock.js';
|
||||
import * as Cesium from 'cesium';
|
||||
@@ -242,6 +242,50 @@ test('a ready weather frame does not wait for unrelated terrain, but requires su
|
||||
h.rendering.clear();
|
||||
});
|
||||
|
||||
test('a staged frame waiting out its quiet interval wakes on a timer, not every render', async (t) => {
|
||||
t.mock.timers.enable({ apis: ['setTimeout'] });
|
||||
let now = 0;
|
||||
const h = renderingHarness({ now: () => now });
|
||||
h.viewer.scene.globe.tilesLoaded = false;
|
||||
const loaded = h.rendering.setFrame(snapshot, times[0]);
|
||||
const tile = deferred();
|
||||
h.providers[0].response = tile;
|
||||
const request = h.providers[0].requestImage(0, 0, 0, {});
|
||||
tile.resolve({});
|
||||
await request;
|
||||
const before = h.renders();
|
||||
for (let i = 0; i < 10; i++) h.postRender.emit();
|
||||
assert.equal(
|
||||
h.renders(),
|
||||
before,
|
||||
'renders while waiting do not request further renders',
|
||||
);
|
||||
t.mock.timers.tick(READY_WAKE_MS - 1);
|
||||
assert.equal(h.renders(), before);
|
||||
t.mock.timers.tick(1);
|
||||
assert.equal(h.renders(), before + 1, 'one wake per interval');
|
||||
now = 200;
|
||||
h.postRender.emit();
|
||||
h.postRender.emit();
|
||||
assert.equal(await loaded, true);
|
||||
const settledRenders = h.renders();
|
||||
t.mock.timers.tick(READY_WAKE_MS * 4);
|
||||
assert.equal(h.renders(), settledRenders, 'commit clears the wake timer');
|
||||
|
||||
const abandoned = h.rendering.setFrame(snapshot, times[1]);
|
||||
const second = deferred();
|
||||
h.providers.at(-1).response = second;
|
||||
const secondRequest = h.providers.at(-1).requestImage(0, 0, 0, {});
|
||||
second.resolve({});
|
||||
await secondRequest;
|
||||
h.postRender.emit();
|
||||
h.rendering.clear();
|
||||
assert.equal(await abandoned, false);
|
||||
const clearedRenders = h.renders();
|
||||
t.mock.timers.tick(READY_WAKE_MS * 4);
|
||||
assert.equal(h.renders(), clearedRenders, 'clear cancels the wake timer');
|
||||
});
|
||||
|
||||
test('camera movement drops abandoned deferred tiles but still waits for admitted requests', async () => {
|
||||
let now = 0;
|
||||
const h = renderingHarness({ now: () => now });
|
||||
|
||||
Reference in new issue
Block a user