diff --git a/src/globe/surfacePlacement.js b/src/globe/surfacePlacement.js index c505c98d8..eceafaa34 100644 --- a/src/globe/surfacePlacement.js +++ b/src/globe/surfacePlacement.js @@ -176,6 +176,11 @@ export function createSurfacePoints( for (const { set, points, exclude = [set] } of groups()) { if (!set) continue; let changed = false; + const yieldPass = () => { + if (changed) set.commit(); + if (changed || moved) request(); + timer = setTimeout(place, 0); + }; for (let index = 0; index < points.length; index++) { const point = points[index]; if (point.surfaceRevision === revision) continue; @@ -183,12 +188,11 @@ export function createSurfacePoints( samples >= SAMPLES_PER_PASS || (visited > 0 && performance.now() - started >= SAMPLE_BUDGET_MS) ) { - if (changed) set.commit(); - if (changed || moved) request(); - timer = setTimeout(place, 0); + yieldPass(); return; } visited++; + const previousRevision = point.surfaceRevision; point.surfaceRevision = revision; if (point.show === false) continue; const offset = @@ -215,6 +219,13 @@ export function createSurfacePoints( ) : point.position; if (!onScreen(position, size)) continue; + // The camera estimate above counts too: leave this point for the + // next pass rather than go over the budget. + if (samples >= SAMPLES_PER_PASS) { + point.surfaceRevision = previousRevision; + yieldPass(); + return; + } samples++; const height = readSurface(engine, point.cartographic, { exclude, diff --git a/src/globe/surfacePlacement.test.mjs b/src/globe/surfacePlacement.test.mjs index 0ae8cf831..5bcd5c23d 100644 --- a/src/globe/surfacePlacement.test.mjs +++ b/src/globe/surfacePlacement.test.mjs @@ -211,6 +211,22 @@ test('initial placement projects high ground before testing the screen and retai assert.ok(Math.abs(heights()[0] - 2502) < 0.01); }); +test('the camera ground estimate counts against the per-pass sample budget', async (t) => { + const { engine, surface } = setup(t, 20, { estimateGround: true }); + await engine.maps.setPhotorealTiles({}); + // Fifteen points find plausible terrain; the sixteenth needs the camera + // estimate, which is a tile sample of its own. + let terrainReads = 0; + engine.fake.script('terrainHeight', () => (++terrainReads <= 15 ? 10 : 20000)); + engine.fake.script('sampleHeight', 50); + const sampled = t.mock.method(engine.surface, 'sampleHeight'); + surface.place(); + assert.ok( + sampled.mock.callCount() <= 16, + `${sampled.mock.callCount()} samples in one pass`, + ); +}); + test('a consumer with its own visibility gate can place a ground vertical whose base does not project', (t) => { const { engine, surface, heights } = setup(t, 1, { screenMargin: Infinity }); engine.fake.script('terrainHeight', 2500);