Merge branch 'main' into upstream/perimeters-stale

This commit is contained in:
quanticsoul4772 authored and GitHub committed 2026-10-25 19:45:35 -07:00
commit c53928ab06
27 files changed
+458 -20

No files matched your search

+19 -2
View File
@@ -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();
+45 -1
View File
@@ -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 });