mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-11 13:52:19 +00:00
Stop refining traffic surfaces that make no progress
An oblique view kept roads whose samples could not resolve from it, and the refinement retried them without end, leaving the layer loading with work pending. A refinement pass that resolves nothing new stops, and work still pending at the existing deadline is dropped.
This commit is contained in:
1 parent
ed6b6114cf
commit
00cb44ad2c
4 files changed
+120
-5
No files matched your search
@@ -552,12 +552,21 @@ export function createFlow({ state: layerState, services, parts, source }) {
|
|||||||
paint();
|
paint();
|
||||||
}
|
}
|
||||||
paint(true);
|
paint(true);
|
||||||
if (current()) layerState._surfacePending = prepared.pending.length;
|
// A completed-mesh pass with no new heights cannot improve this view.
|
||||||
|
// Keep the roads in _surfaceInput for a later view or surface change,
|
||||||
|
// without leaving offscreen or unavailable stations loading forever.
|
||||||
|
const retryPending =
|
||||||
|
prepared.pending.length > 0 &&
|
||||||
|
(!refining ||
|
||||||
|
!trafficSurfaceReady(engine) ||
|
||||||
|
prepared.metrics.resolvedSamples > 0);
|
||||||
|
if (current())
|
||||||
|
layerState._surfacePending = retryPending ? prepared.pending.length : 0;
|
||||||
if (
|
if (
|
||||||
current() &&
|
current() &&
|
||||||
!preview &&
|
!preview &&
|
||||||
((!refining && !trafficSurfaceReady(engine)) ||
|
((!refining && !trafficSurfaceReady(engine)) ||
|
||||||
prepared.pending.length ||
|
retryPending ||
|
||||||
(!refining && ready.some((road) => road.surfaceSettled === false)))
|
(!refining && ready.some((road) => road.surfaceSettled === false)))
|
||||||
) {
|
) {
|
||||||
layerState._surfaceRefineRemove?.();
|
layerState._surfaceRefineRemove?.();
|
||||||
@@ -579,7 +588,7 @@ export function createFlow({ state: layerState, services, parts, source }) {
|
|||||||
stop();
|
stop();
|
||||||
// Revalidate only local samples taken at a coarser rendered LOD. This
|
// Revalidate only local samples taken at a coarser rendered LOD. This
|
||||||
// background upgrade never holds road acquisition or first paint;
|
// background upgrade never holds road acquisition or first paint;
|
||||||
// missing samples keep retrying while refined heights stay cached.
|
// missing samples retry while heights make progress and stay cached.
|
||||||
layerState._surfaceRefining = true;
|
layerState._surfaceRefining = true;
|
||||||
applyFlowThenRender(
|
applyFlowThenRender(
|
||||||
roads,
|
roads,
|
||||||
@@ -600,7 +609,10 @@ export function createFlow({ state: layerState, services, parts, source }) {
|
|||||||
layerState._surfaceRefining = false;
|
layerState._surfaceRefining = false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const timer = setTimeout(stop, 10000);
|
const timer = setTimeout(() => {
|
||||||
|
stop();
|
||||||
|
if (current()) layerState._surfacePending = 0;
|
||||||
|
}, 10000);
|
||||||
layerState._surfaceRefineRemove = stop;
|
layerState._surfaceRefineRemove = stop;
|
||||||
engine.render.request('traffic-surface');
|
engine.render.request('traffic-surface');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -241,6 +241,101 @@ test('a still camera drains pending surface samples across bounded passes using
|
|||||||
assert.equal(engine.fake.renderRequests, finished, 'settled work is idle');
|
assert.equal(engine.fake.renderRequests, finished, 'settled work is idle');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('a completed surface defers unavailable roads until the next view instead of refining forever', async (t) => {
|
||||||
|
const surface = incomingGlobe(t);
|
||||||
|
surface.ready = true;
|
||||||
|
surface.frame();
|
||||||
|
const { engine } = surface;
|
||||||
|
const { state, flow } = fixture(surface);
|
||||||
|
t.after(() => {
|
||||||
|
state._activeFetchAbort.abort();
|
||||||
|
state._surfaceRefineRemove?.();
|
||||||
|
});
|
||||||
|
const roads = [road(0, GROUND), road(0.008, GROUND)];
|
||||||
|
let missing = true;
|
||||||
|
engine.fake.script('terrainHeight', (point) =>
|
||||||
|
missing && geo.math.toDegrees(point.latitude) > LAT + 0.004
|
||||||
|
? undefined
|
||||||
|
: GROUND,
|
||||||
|
);
|
||||||
|
await flow.applyFlowThenRender(
|
||||||
|
roads,
|
||||||
|
{ ...bounds, coverage },
|
||||||
|
1,
|
||||||
|
1800,
|
||||||
|
'Loaded full',
|
||||||
|
);
|
||||||
|
let rendered = engine.fake.renderRequests;
|
||||||
|
await settle(() => {
|
||||||
|
if (engine.fake.renderRequests > rendered || state._surfaceRefineRemove) {
|
||||||
|
rendered = engine.fake.renderRequests;
|
||||||
|
surface.frame();
|
||||||
|
}
|
||||||
|
return !state._surfacePending && !state._surfaceRefining;
|
||||||
|
});
|
||||||
|
assert.equal(
|
||||||
|
state._surfacePending,
|
||||||
|
0,
|
||||||
|
'unavailable roads are not outstanding work',
|
||||||
|
);
|
||||||
|
assert.equal(state._surfaceRefining, false);
|
||||||
|
assert.equal(state._surfaceRefineRemove, null);
|
||||||
|
assert.equal(roads[1].surfaceReady, false, 'no unmeasured road is displayed');
|
||||||
|
const finished = engine.fake.renderRequests;
|
||||||
|
surface.frame();
|
||||||
|
await nextTask();
|
||||||
|
assert.equal(
|
||||||
|
engine.fake.renderRequests,
|
||||||
|
finished,
|
||||||
|
'the completed view is idle',
|
||||||
|
);
|
||||||
|
|
||||||
|
missing = false;
|
||||||
|
await flow.applyFlowThenRender(
|
||||||
|
roads,
|
||||||
|
{ ...bounds, coverage },
|
||||||
|
1,
|
||||||
|
1800,
|
||||||
|
'Loaded full',
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
roads[1].surfaceReady,
|
||||||
|
true,
|
||||||
|
'another view retries the retained road',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the surface wait deadline defers pending roads when the mesh never settles', async (t) => {
|
||||||
|
const surface = incomingGlobe(t);
|
||||||
|
surface.frame();
|
||||||
|
const { state, flow } = fixture(surface);
|
||||||
|
t.after(() => {
|
||||||
|
state._activeFetchAbort.abort();
|
||||||
|
state._surfaceRefineRemove?.();
|
||||||
|
});
|
||||||
|
const schedule = globalThis.setTimeout;
|
||||||
|
let deadline;
|
||||||
|
t.mock.method(globalThis, 'setTimeout', (callback, delay, ...args) => {
|
||||||
|
if (delay === 10000) deadline = callback;
|
||||||
|
return schedule(callback, delay, ...args);
|
||||||
|
});
|
||||||
|
surface.engine.fake.script('terrainHeight', (point) =>
|
||||||
|
geo.math.toDegrees(point.latitude) > LAT + 0.004 ? undefined : GROUND,
|
||||||
|
);
|
||||||
|
await flow.applyFlowThenRender(
|
||||||
|
[road(0, GROUND), road(0.008, GROUND)],
|
||||||
|
{ ...bounds, coverage },
|
||||||
|
1,
|
||||||
|
1800,
|
||||||
|
'Loaded full',
|
||||||
|
);
|
||||||
|
assert.equal(state._surfacePending, 1);
|
||||||
|
assert.equal(typeof deadline, 'function');
|
||||||
|
deadline();
|
||||||
|
assert.equal(state._surfacePending, 0);
|
||||||
|
assert.equal(state._surfaceRefineRemove, null);
|
||||||
|
});
|
||||||
|
|
||||||
test('resuming the viewport republishes the retained roads as a pass that can still refine', () => {
|
test('resuming the viewport republishes the retained roads as a pass that can still refine', () => {
|
||||||
const labels = [];
|
const labels = [];
|
||||||
const state = createState({ services: {} });
|
const state = createState({ services: {} });
|
||||||
|
|||||||
@@ -258,6 +258,7 @@ export async function prepareRoadSurfaces(
|
|||||||
const metrics = {
|
const metrics = {
|
||||||
sampleCount: 0,
|
sampleCount: 0,
|
||||||
sampleMs: 0,
|
sampleMs: 0,
|
||||||
|
resolvedSamples: 0,
|
||||||
cacheHits: 0,
|
cacheHits: 0,
|
||||||
depthHits: 0,
|
depthHits: 0,
|
||||||
roads: roads.length,
|
roads: roads.length,
|
||||||
@@ -341,6 +342,7 @@ export async function prepareRoadSurfaces(
|
|||||||
height = validHeight(floor) ? floor : 0;
|
height = validHeight(floor) ? floor : 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
metrics.resolvedSamples++;
|
||||||
height = validHeight(floor) ? Math.max(sampled, floor) : sampled;
|
height = validHeight(floor) ? Math.max(sampled, floor) : sampled;
|
||||||
cache.heights.delete(key);
|
cache.heights.delete(key);
|
||||||
cache.heights.set(key, {
|
cache.heights.set(key, {
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ test('per-vertex mesh heights follow terrain, reject invalid samples, and leave
|
|||||||
onMetrics: (value) => (metrics = value),
|
onMetrics: (value) => (metrics = value),
|
||||||
});
|
});
|
||||||
assert.equal(metrics.sampleCount, 5);
|
assert.equal(metrics.sampleCount, 5);
|
||||||
|
assert.equal(metrics.resolvedSamples, 2);
|
||||||
assert.equal(prepared.pending.length, 1);
|
assert.equal(prepared.pending.length, 1);
|
||||||
assert.equal(samples, 5);
|
assert.equal(samples, 5);
|
||||||
const actual = r.waypoints.map((p) => Math.round(heightOf(p)));
|
const actual = r.waypoints.map((p) => Math.round(heightOf(p)));
|
||||||
@@ -108,7 +109,12 @@ test('per-vertex mesh heights follow terrain, reject invalid samples, and leave
|
|||||||
picks++;
|
picks++;
|
||||||
return 75;
|
return 75;
|
||||||
});
|
});
|
||||||
await prepareRoadSurfaces([r], engine, ground, []);
|
const cached = await prepareRoadSurfaces([r], engine, ground, []);
|
||||||
|
assert.equal(
|
||||||
|
cached.metrics.resolvedSamples,
|
||||||
|
0,
|
||||||
|
'cache hits are not progress',
|
||||||
|
);
|
||||||
assert.equal(picks, 0, 'validated coordinate heights survive cache hits');
|
assert.equal(picks, 0, 'validated coordinate heights survive cache hits');
|
||||||
const abort = new AbortController();
|
const abort = new AbortController();
|
||||||
abort.abort();
|
abort.abort();
|
||||||
|
|||||||
Reference in new issue
Block a user