mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +00:00
Report a panel's render errors in full and restart drawing
Cesium stops drawing after a render error and prints worker failures as [object Object]. An inline panel now logs the error's details and starts drawing again, up to three times.
This commit is contained in:
1 parent
47742f335d
commit
5ad0dd0004
2 files changed
+82
-1
No files matched your search
+41
-1
@@ -49,6 +49,19 @@ export function isEmbeddedInline() {
|
|||||||
// frames; see keepPanelRendering.
|
// frames; see keepPanelRendering.
|
||||||
const PANEL_FRAME_MS = 33;
|
const PANEL_FRAME_MS = 33;
|
||||||
const MISSED_FRAMES_MS = 250;
|
const MISSED_FRAMES_MS = 250;
|
||||||
|
const RENDER_RESTARTS = 3;
|
||||||
|
const RENDER_RESTART_MS = 1000;
|
||||||
|
|
||||||
|
/** A render error's details, including the plain objects workers report. */
|
||||||
|
function describeError(error) {
|
||||||
|
if (error instanceof Error)
|
||||||
|
return `${error.name}: ${error.message}\n${error.stack}`;
|
||||||
|
try {
|
||||||
|
return JSON.stringify(error, Object.getOwnPropertyNames(error ?? {}));
|
||||||
|
} catch {
|
||||||
|
return String(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keep the globe drawing in an inline panel. Some hosts report a panel on
|
* Keep the globe drawing in an inline panel. Some hosts report a panel on
|
||||||
@@ -67,14 +80,41 @@ export function keepPanelRendering(
|
|||||||
frameRequest = windowRef.requestAnimationFrame(onFrame);
|
frameRequest = windowRef.requestAnimationFrame(onFrame);
|
||||||
};
|
};
|
||||||
frameRequest = windowRef.requestAnimationFrame(onFrame);
|
frameRequest = windowRef.requestAnimationFrame(onFrame);
|
||||||
|
// Cesium stops drawing after a render error. Report it in full, and
|
||||||
|
// start again a few times in case it does not repeat.
|
||||||
|
let failed = false;
|
||||||
|
let restarts = 0;
|
||||||
|
let restart = null;
|
||||||
|
const removeErrorListener = viewer.scene?.renderError?.addEventListener(
|
||||||
|
(_scene, error) => {
|
||||||
|
failed = true;
|
||||||
|
console.error(
|
||||||
|
"[God's Eye View panel] render error:",
|
||||||
|
describeError(error),
|
||||||
|
);
|
||||||
|
if (restarts >= RENDER_RESTARTS) return;
|
||||||
|
restarts += 1;
|
||||||
|
restart = windowRef.setTimeout(() => {
|
||||||
|
failed = false;
|
||||||
|
viewer.useDefaultRenderLoop = true;
|
||||||
|
}, RENDER_RESTART_MS);
|
||||||
|
},
|
||||||
|
);
|
||||||
const timer = windowRef.setInterval(() => {
|
const timer = windowRef.setInterval(() => {
|
||||||
if (now() - lastFrame < MISSED_FRAMES_MS || viewer.isDestroyed?.()) return;
|
if (
|
||||||
|
failed ||
|
||||||
|
now() - lastFrame < MISSED_FRAMES_MS ||
|
||||||
|
viewer.isDestroyed?.()
|
||||||
|
)
|
||||||
|
return;
|
||||||
viewer.resize();
|
viewer.resize();
|
||||||
viewer.render();
|
viewer.render();
|
||||||
}, PANEL_FRAME_MS);
|
}, PANEL_FRAME_MS);
|
||||||
return () => {
|
return () => {
|
||||||
windowRef.clearInterval(timer);
|
windowRef.clearInterval(timer);
|
||||||
|
windowRef.clearTimeout?.(restart);
|
||||||
windowRef.cancelAnimationFrame(frameRequest);
|
windowRef.cancelAnimationFrame(frameRequest);
|
||||||
|
removeErrorListener?.();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -246,3 +246,44 @@ test('a panel keeps drawing from a timer while animation frames stop', () => {
|
|||||||
assert.equal(tick, null);
|
assert.equal(tick, null);
|
||||||
assert.equal(frame, null);
|
assert.equal(frame, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('a panel reports a render error in full and starts drawing again', () => {
|
||||||
|
let listener = null;
|
||||||
|
const timeouts = [];
|
||||||
|
const windowRef = {
|
||||||
|
requestAnimationFrame: () => 1,
|
||||||
|
cancelAnimationFrame() {},
|
||||||
|
setInterval: () => 1,
|
||||||
|
clearInterval() {},
|
||||||
|
setTimeout: (fn) => (timeouts.push(fn), timeouts.length),
|
||||||
|
clearTimeout() {},
|
||||||
|
};
|
||||||
|
const viewer = {
|
||||||
|
useDefaultRenderLoop: false,
|
||||||
|
scene: {
|
||||||
|
renderError: {
|
||||||
|
addEventListener: (fn) => ((listener = fn), () => (listener = null)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const logged = [];
|
||||||
|
const original = console.error;
|
||||||
|
console.error = (...args) => logged.push(args.join(' '));
|
||||||
|
try {
|
||||||
|
const stop = keepPanelRendering(viewer, { windowRef, now: () => 0 });
|
||||||
|
for (let attempt = 0; attempt < 5; attempt++)
|
||||||
|
listener(viewer.scene, {
|
||||||
|
name: 'RuntimeError',
|
||||||
|
message: 'worker failed',
|
||||||
|
});
|
||||||
|
assert.match(logged[0], /render error: .*"message":"worker failed"/);
|
||||||
|
// Only a few restarts, so an error that repeats does not spin.
|
||||||
|
assert.equal(timeouts.length, 3);
|
||||||
|
timeouts[0]();
|
||||||
|
assert.equal(viewer.useDefaultRenderLoop, true);
|
||||||
|
stop();
|
||||||
|
assert.equal(listener, null);
|
||||||
|
} finally {
|
||||||
|
console.error = original;
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in new issue
Block a user