gods-eye-view/scripts/qa-cables-render-probe.mjs

117 lines
4.4 KiB
JavaScript

#!/usr/bin/env node
/**
* qa-cables-render-probe — replicates the owner-side perf probe methodology
* for the cables layer (relative numbers; SwiftShader when run headless):
*
* 1. PARKED: timed full `viewer.render()` loop (40 calls) with cables ON,
* then OFF, at the fixed mid-Atlantic camera. Reports mean/max.
* 2. PAN-STOP: 3 cycles of { setView to an offset, raise camera.moveEnd,
* wait 650 ms, 20 timed `viewer.render()` calls }. Reports per-cycle
* mean/max and the worst frame across cycles, cables ON vs OFF.
*
* Usage: node scripts/qa-cables-render-probe.mjs [--url http://localhost:4216]
*/
import puppeteer from 'puppeteer';
const argv = process.argv;
const url = argv.includes('--url') ? argv[argv.indexOf('--url') + 1] : 'http://localhost:4216';
const LAYER_ID = 'telegeography-submarine-cables';
const browser = await puppeteer.launch({
headless: 'new',
protocolTimeout: 300_000,
args: [
'--no-sandbox', '--disable-setuid-sandbox', '--window-size=1440,900',
'--disable-backgrounding-occluded-windows', '--disable-renderer-backgrounding',
'--disable-background-timer-throttling',
],
});
try {
const page = await browser.newPage();
await page.setViewport({ width: 1440, height: 860 });
await page.goto(url, { waitUntil: 'domcontentloaded' });
await page.waitForFunction(() => !!window.__godsEyeView?.viewer, { timeout: 90_000 });
await new Promise((r) => setTimeout(r, 12_000));
await page.evaluate(async () => {
const gev = window.__godsEyeView;
gev.viewer.camera.cancelFlight();
const ell = gev.viewer.scene.globe.ellipsoid;
gev.viewer.camera.setView({
destination: ell.cartographicToCartesian({
longitude: -40 * Math.PI / 180, latitude: 35 * Math.PI / 180, height: 4_500_000,
}),
orientation: { heading: 0, pitch: -Math.PI / 2, roll: 0 },
});
for (const [id, entry] of gev.dataManager.layers) {
if (entry.enabled) { try { await gev.dataManager.setEnabled(id, false, { origin: 'user' }); } catch { /* probe */ } }
}
});
await new Promise((r) => setTimeout(r, 5_000));
const stats = (values) => ({
mean: +(values.reduce((s, v) => s + v, 0) / Math.max(1, values.length)).toFixed(2),
max: +Math.max(...values).toFixed(2),
});
const measure = async (label, enabled) => {
await page.evaluate(async (id, on) => {
const gev = window.__godsEyeView;
await gev.dataManager.setEnabled(id, on, { origin: 'user' });
}, LAYER_ID, enabled);
await new Promise((r) => setTimeout(r, enabled ? 6_000 : 3_000)); // load/teardown + settle
// 1. PARKED timed renders.
const parked = await page.evaluate(() => {
const v = window.__godsEyeView.viewer;
const durations = [];
for (let i = 0; i < 40; i++) {
const t0 = performance.now();
v.render();
durations.push(performance.now() - t0);
}
return durations;
});
// 2. PAN-STOP cycles: move, raise moveEnd, wait 650 ms, 20 timed renders.
const cycles = [];
for (let cycle = 0; cycle < 3; cycle++) {
const frames = await page.evaluate(async (step) => {
const v = window.__godsEyeView.viewer;
const ell = v.scene.globe.ellipsoid;
v.camera.setView({
destination: ell.cartographicToCartesian({
longitude: (-40 + step * 3) * Math.PI / 180,
latitude: 35 * Math.PI / 180,
height: 4_500_000,
}),
orientation: { heading: 0, pitch: -Math.PI / 2, roll: 0 },
});
v.camera.moveEnd.raiseEvent();
await new Promise((resolve) => setTimeout(resolve, 650));
const durations = [];
for (let i = 0; i < 20; i++) {
const t0 = performance.now();
v.render();
durations.push(performance.now() - t0);
}
return durations;
}, cycle + 1);
cycles.push(stats(frames));
}
const worst = Math.max(...cycles.map((c) => c.max));
console.log(` [${label}] parked render: ${JSON.stringify(stats(parked))}`);
console.log(` [${label}] pan-stop cycles: ${JSON.stringify(cycles)} worstFrame=${worst}`);
return { parked: stats(parked), cycles, worst };
};
const on = await measure('cables ON ', true);
const off = await measure('cables OFF', false);
console.log(`\nparked delta (ON-OFF mean): ${(on.parked.mean - off.parked.mean).toFixed(2)}ms`);
console.log(`pan-stop worst: ON ${on.worst}ms vs OFF ${off.worst}ms`);
} finally {
await browser.close();
}