mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +00:00
Let the flights' fix history fill before the long soak measures the heap, and say why a context-loss case drew no frame
The long soak's heap slope counted the per-aircraft dead-reckoning history (POSITION_HISTORY_LIMIT fixes for 10 000 aircraft, about 14 MiB over the first 2.5 min of polls) as switch growth: a run with the same workload and no switches grows as much and then levels off. With --assert the soak now waits until the flights feed has been read more times than the history keeps before it switches; over 500 switches the heap then grows 1.07 KiB per switch. qa-strict-context-loss records, only when no non-blank frame came, how many frames the running and the watched engine drew in 3 s and how much of the last one differs from its first pixel.
This commit is contained in:
1 parent
f89aee1eac
commit
1100cdcff5
2 files changed
+63
-1
No files matched your search
@@ -164,6 +164,51 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
||||
const outcome = after.engine === to ? 'switched' : 'rolled back';
|
||||
if (after.running) await rememberEngine(p);
|
||||
const drawn = after.running ? await draws(after.engine) : false;
|
||||
// Why no frame passed, when none did: frames drawn in 3 s by the engine
|
||||
// the page runs and by the one the probe watched, and how many of 48x48
|
||||
// samples of the last frame differ from the first one.
|
||||
const noFrame =
|
||||
after.running && !drawn
|
||||
? await p.evaluate(async (kind) => {
|
||||
const running = window.__godsEyeView.engine;
|
||||
const watched = window.__qaEngines.get(kind);
|
||||
const probe = document.createElement('canvas');
|
||||
probe.width = probe.height = 48;
|
||||
const context = probe.getContext('2d', {
|
||||
willReadFrequently: true,
|
||||
});
|
||||
const count = { running: 0, watched: 0, differing: null };
|
||||
const offs = [
|
||||
running.frames.postRender.addEventListener(() => {
|
||||
count.running++;
|
||||
context.drawImage(running.host.canvas, 0, 0, 48, 48);
|
||||
const d = context.getImageData(0, 0, 48, 48).data;
|
||||
let differing = 0;
|
||||
for (let i = 4; i < d.length; i += 4)
|
||||
if (
|
||||
Math.abs(d[i] - d[0]) +
|
||||
Math.abs(d[i + 1] - d[1]) +
|
||||
Math.abs(d[i + 2] - d[2]) >
|
||||
24
|
||||
)
|
||||
differing++;
|
||||
count.differing = differing;
|
||||
}),
|
||||
watched === running
|
||||
? () => {}
|
||||
: watched?.frames.postRender.addEventListener(
|
||||
() => count.watched++,
|
||||
),
|
||||
];
|
||||
await new Promise((resolve) => setTimeout(resolve, 3000));
|
||||
offs.forEach((off) => off?.());
|
||||
return {
|
||||
...count,
|
||||
sameEngine: watched === running,
|
||||
connected: running.host.canvas.isConnected,
|
||||
};
|
||||
}, after.engine)
|
||||
: null;
|
||||
console.log(
|
||||
'after',
|
||||
label,
|
||||
@@ -205,6 +250,7 @@ await strict.run({ args: ['--use-angle=metal', '--enable-gpu'] }, async (b) => {
|
||||
sessionIntact: intact(after),
|
||||
history: after.history - before.history,
|
||||
drawn,
|
||||
...(noFrame ? { noFrame } : {}),
|
||||
back,
|
||||
backEngine: backState?.engine ?? null,
|
||||
backSessionIntact: backState ? intact(backState) : false,
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
//
|
||||
// Checked on every switch: the same session (manager and every layer
|
||||
// model), the radio still playing the same station with one play() call.
|
||||
// With --assert (the long run), after the warm-up (switch 20): live WebGL
|
||||
// With --assert (the long run), the flights' fix history fills first (see
|
||||
// below), then after the warm-up (switch 20): live WebGL
|
||||
// objects per engine context grow by at most 0.1 per switch (least-squares
|
||||
// slope over the checkpoints) and by at most 64 overall; no new WebGL
|
||||
// context; JS heap slope at most 10 KiB per switch; no new document; the
|
||||
@@ -42,6 +43,7 @@ import {
|
||||
} from './qa-strict-session.mjs';
|
||||
import { installRadioFixture, stations } from './fixtures/w2/radio.mjs';
|
||||
import { trafficFixtureResponse } from './traffic-fixtures.mjs';
|
||||
import { POSITION_HISTORY_LIMIT } from '../src/layers/flights/recordPolicy.js';
|
||||
|
||||
const base = localPreview(process.argv[2]);
|
||||
const option = (name, fallback) => {
|
||||
@@ -193,6 +195,20 @@ for (let run = 1; run <= runs; run++) {
|
||||
window.__godsEyeView.dataManager.layers.get('radio').module.getUIState()
|
||||
.audioState === 'playing',
|
||||
);
|
||||
// Each aircraft keeps its last POSITION_HISTORY_LIMIT fixes for dead
|
||||
// reckoning, so the first polls of the flights feed grow the heap by
|
||||
// itself (about 14 MiB here, over 2.5 min; a run without switches then
|
||||
// levels off). The long run lets that history fill before it switches,
|
||||
// so the heap slope it asserts is the switches' own.
|
||||
if (strictMode) {
|
||||
const filling = Date.now();
|
||||
await p.waitForFunction(
|
||||
(polls) => window.__qaReads.flights > polls,
|
||||
{ timeout: 300000, polling: 1000 },
|
||||
POSITION_HISTORY_LIMIT,
|
||||
);
|
||||
row.historyFillSeconds = Math.round((Date.now() - filling) / 1000);
|
||||
}
|
||||
row.workload = await p.evaluate(() => {
|
||||
const layers = window.__godsEyeView.dataManager.layers;
|
||||
return Object.fromEntries(
|
||||
|
||||
Reference in new issue
Block a user