mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
test(track): freshen rotation passes before the arrival probe and retry an inconclusive timeout
A headless SwiftShader run showed a 926 ms first frame on the military probe, which no wall-clock budget can accept: the 1 s idle refresh may already have run inside it. Raise moveEnd and wait one frame before each attempt so the settle frame is measured against a pass that just ran, and treat a timeout (never a wrong angle) as inconclusive with one more attempt. Document why the per-phase budget stays at half the refresh interval. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
1 parent
9059cd942f
commit
e3c4e88d6f
2 files changed
+43
-25
No files matched your search
@@ -2770,28 +2770,43 @@ async function main() {
|
||||
const bb = findBB(id);
|
||||
if (!bb || !bb.show) return { error: `${id} missing/hidden` };
|
||||
const r0 = bb.rotation; // settled tamper origin; correctness uses a fresh projection below
|
||||
const observed = await observeArrivalRotation({
|
||||
scene: v.scene,
|
||||
startMoveEnd() {
|
||||
// Only moveEnd can repair the tampered angle at the idle pose.
|
||||
bb.rotation = r0 + 1.5;
|
||||
v.camera.moveEnd.raiseEvent();
|
||||
},
|
||||
startReveal() {
|
||||
// Hide immediately after the confirmed settle frame. The next
|
||||
// eligible fleet tick must reveal AND re-aim in that same tick.
|
||||
bb.rotation = r0 + 1.5;
|
||||
bb.show = false;
|
||||
},
|
||||
read() {
|
||||
return {
|
||||
shown: bb.show,
|
||||
rotationError: angDiff(bb.rotation,
|
||||
screenProjectedRotation(v.scene, bb.position, course, null)),
|
||||
};
|
||||
},
|
||||
});
|
||||
const hasModel = !!(window.__g3dFindModel && window.__g3dFindModel(id));
|
||||
// A wall-clock timeout is inconclusive, not a wrong angle: the frame
|
||||
// landed where the 1 s idle refresh could already have repaired it
|
||||
// (seen once on headless SwiftShader: a 926 ms first frame). Such an
|
||||
// observation gets one more attempt; a wrong first angle never does.
|
||||
let observed;
|
||||
for (let attempt = 1; attempt <= 2; attempt++) {
|
||||
// Freshen both layers' rotation passes right before the probe, so the
|
||||
// settle frame is measured against a pass that just ran rather than
|
||||
// one left over from the previous probe — with the camera idle, a
|
||||
// layer otherwise runs a pass only every ROTATION_REFRESH_MS.
|
||||
v.camera.moveEnd.raiseEvent();
|
||||
await nextFrames(1);
|
||||
observed = await observeArrivalRotation({
|
||||
scene: v.scene,
|
||||
startMoveEnd() {
|
||||
// Only moveEnd can repair the tampered angle at the idle pose.
|
||||
bb.rotation = r0 + 1.5;
|
||||
v.camera.moveEnd.raiseEvent();
|
||||
},
|
||||
startReveal() {
|
||||
// Hide immediately after the confirmed settle frame. The next
|
||||
// eligible fleet tick must reveal AND re-aim in that same tick.
|
||||
bb.rotation = r0 + 1.5;
|
||||
bb.show = false;
|
||||
},
|
||||
read() {
|
||||
return {
|
||||
shown: bb.show,
|
||||
rotationError: angDiff(bb.rotation,
|
||||
screenProjectedRotation(v.scene, bb.position, course, null)),
|
||||
};
|
||||
},
|
||||
});
|
||||
observed.attempts = attempt;
|
||||
if (!observed.error) break;
|
||||
}
|
||||
return { r0, ...observed, hasModel };
|
||||
};
|
||||
const flightCourse = window.__SYNTH.flights.find((f) => f.icao === 'aaa002')?.track ?? 0;
|
||||
@@ -2801,8 +2816,8 @@ async function main() {
|
||||
military: await probe('bbb101', militaryCourse),
|
||||
};
|
||||
});
|
||||
const fmtArr = (a) => (!a ? `no result (${arrival?.error || 'phase error'})` : a.error ? `${a.error}; elapsed=${Number(a.elapsedMs).toFixed(1)} ms`
|
||||
: `settle-err=${(a.dMoveEnd * 180 / Math.PI).toFixed(1)}° reveal-err=${(a.dReveal * 180 / Math.PI).toFixed(1)}° shown=${a.shown} flipFrame=${a.flipFrame} settle=${a.moveEndMs.toFixed(1)} ms reveal=${a.revealMs.toFixed(1)} ms total=${a.elapsedMs.toFixed(1)} ms hasModel=${a.hasModel}`);
|
||||
const fmtArr = (a) => (!a ? `no result (${arrival?.error || 'phase error'})` : a.error ? `${a.error}; elapsed=${Number(a.elapsedMs).toFixed(1)} ms attempts=${a.attempts}`
|
||||
: `settle-err=${(a.dMoveEnd * 180 / Math.PI).toFixed(1)}° reveal-err=${(a.dReveal * 180 / Math.PI).toFixed(1)}° shown=${a.shown} flipFrame=${a.flipFrame} settle=${a.moveEndMs.toFixed(1)} ms reveal=${a.revealMs.toFixed(1)} ms total=${a.elapsedMs.toFixed(1)} ms attempts=${a.attempts} hasModel=${a.hasModel}`);
|
||||
const TAMPER_DEG = 1.5 * 180 / Math.PI; // ≈ 86° — an uncorrected nose stays this far off
|
||||
record(`arrival: moveEnd + first reveal both re-aim before idle refresh, flights (tamper was ${TAMPER_DEG.toFixed(0)}°)`,
|
||||
arrivalRotationPassed(arrival.flights), fmtArr(arrival.flights));
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
* Wall-clock budget for EACH observed frame, regardless of FPS. Both the settle
|
||||
* frame (measured from the forced moveEnd) and the reveal frame (measured from
|
||||
* the settle frame that hid the contact) must land inside it, so neither can be
|
||||
* repaired by the one-second idle rotation refresh before it is read — while a
|
||||
* ~300 ms software-rendered frame (headless SwiftShader) still fits twice.
|
||||
* repaired by the one-second idle rotation refresh before it is read. The
|
||||
* refresh clock starts at the fleet tick (preRender) of the frame the budget is
|
||||
* measured from, so the budget must leave room for that frame's own render
|
||||
* time: half the refresh interval tolerates frames up to ~500 ms, and a ~300 ms
|
||||
* software-rendered frame (headless SwiftShader) fits with margin.
|
||||
*/
|
||||
export const ARRIVAL_OBSERVATION_MS = 500;
|
||||
|
||||
|
||||
Reference in new issue
Block a user