Isolate globe renderer suspension from the switch cache

This commit is contained in:
German Khokhlov committed 2026-11-02 22:42:40 +04:00
1 parent 73ea608881
commit 4bd7f97cc4
5 files changed
+63 -25

No files matched your search

+3
View File
@@ -833,6 +833,7 @@
"src/ui/cockpitCamera.js", "src/ui/cockpitCamera.js",
"src/ui/cockpitContext.js", "src/ui/cockpitContext.js",
"src/ui/cockpitController.js", "src/ui/cockpitController.js",
"src/ui/cockpitHost.js",
"src/ui/cockpitDisplayPortal.js", "src/ui/cockpitDisplayPortal.js",
"src/ui/displayPanelScroll.js", "src/ui/displayPanelScroll.js",
"src/ui/cockpitInput.js", "src/ui/cockpitInput.js",
@@ -975,6 +976,7 @@
"src/ui/cockpitCamera.js", "src/ui/cockpitCamera.js",
"src/ui/cockpitContext.js", "src/ui/cockpitContext.js",
"src/ui/cockpitController.js", "src/ui/cockpitController.js",
"src/ui/cockpitHost.js",
"src/ui/cockpitDisplayPortal.js", "src/ui/cockpitDisplayPortal.js",
"src/ui/displayPanelScroll.js", "src/ui/displayPanelScroll.js",
"src/ui/cockpitInput.js", "src/ui/cockpitInput.js",
@@ -3085,6 +3087,7 @@
"src/ui/cockpitCamera.js", "src/ui/cockpitCamera.js",
"src/ui/cockpitContext.js", "src/ui/cockpitContext.js",
"src/ui/cockpitController.js", "src/ui/cockpitController.js",
"src/ui/cockpitHost.js",
"src/ui/cockpitDisplayPortal.js", "src/ui/cockpitDisplayPortal.js",
"src/ui/displayPanelScroll.js", "src/ui/displayPanelScroll.js",
"src/ui/cockpitInput.js", "src/ui/cockpitInput.js",
+9 -1
View File
@@ -242,7 +242,15 @@ export async function createNavaraScene({
viewer: null, viewer: null,
engine, engine,
tileset: null, tileset: null,
setRenderLoop: globe.setRenderLoop, suspend() {
uninstallRenderGovernor(engine);
engine.camera.cancelFlight();
engine.render.setContinuous(false);
globe.setRenderLoop(false);
},
resume() {
globe.setRenderLoop(true);
},
mapStackController, mapStackController,
}; };
} }
+15 -1
View File
@@ -181,5 +181,19 @@ async function createRenderer({
}); });
signal.throwIfAborted(); signal.throwIfAborted();
return { viewer, engine, tileset, mapStackController }; return {
viewer,
engine,
tileset,
mapStackController,
suspend() {
uninstallRenderGovernor(viewer);
engine.camera.cancelFlight();
releaseCameraVerbs(viewer);
viewer.useDefaultRenderLoop = false;
},
resume() {
viewer.useDefaultRenderLoop = !document.hidden;
},
};
} }
+13 -17
View File
@@ -1,6 +1,3 @@
import { releaseCameraVerbs } from '../cameraVerbs.js';
import { uninstallRenderGovernor } from '../renderGovernor.js';
// Only the renderer and its map survive a switch. The outgoing UI, data feeds, // Only the renderer and its map survive a switch. The outgoing UI, data feeds,
// tools and their subscriptions are still disposed with their application. // tools and their subscriptions are still disposed with their application.
export function createRendererCache() { export function createRendererCache() {
@@ -50,25 +47,24 @@ export function createRendererCache() {
throw error; throw error;
} }
} else { } else {
host.prepend(entry.container); try {
if (entry.scene.viewer) host.prepend(entry.container);
entry.scene.viewer.useDefaultRenderLoop = !document.hidden; entry.scene.resume();
else entry.scene.setRenderLoop(true); entry.scene.engine.render.request('engine-resume');
entry.scene.engine.render.request('engine-resume'); } catch (error) {
entry.dispose();
throw error;
}
} }
const { viewer, engine } = entry.scene;
defer(() => { defer(() => {
const keep = retainKind === kind && !entry.lost; const keep = retainKind === kind && !entry.lost;
retainKind = null; retainKind = null;
if (!keep) return entry.dispose(); if (!keep) return entry.dispose();
uninstallRenderGovernor(viewer ?? engine); try {
engine.camera.cancelFlight(); entry.scene.suspend();
if (viewer) { } catch (error) {
releaseCameraVerbs(viewer); entry.dispose();
viewer.useDefaultRenderLoop = false; throw error;
} else {
engine.render.setContinuous(false);
entry.scene.setRenderLoop(false);
} }
entry.container.remove(); entry.container.remove();
idle.set(kind, entry); idle.set(kind, entry);
+23 -6
View File
@@ -28,13 +28,15 @@ test('a parked renderer is reused, but a lost context is disposed before rebuild
released += 1; released += 1;
}); });
return { return {
setRenderLoop(enabled) { suspend() {
this.running = enabled; this.running = false;
},
resume() {
this.running = true;
}, },
engine: { engine: {
host: { container, canvas: new EventTarget() }, host: { container, canvas: new EventTarget() },
camera: { cancelFlight() {} }, render: { request() {} },
render: { request() {}, setContinuous() {} },
}, },
}; };
}; };
@@ -53,9 +55,24 @@ test('a parked renderer is reused, but a lost context is disposed before rebuild
cache.retain('navara'); cache.retain('navara');
release(); release();
first.engine.host.canvas.dispatchEvent(new Event('webglcontextlost')); first.engine.host.canvas.dispatchEvent(new Event('webglcontextlost'));
assert.notEqual(await open(), first); const second = await open();
assert.notEqual(second, first);
assert.equal(released, 1); assert.equal(released, 1);
cache.retain('navara');
release(); release();
cache.clear(); second.resume = () => {
throw new Error('resume failed');
};
await assert.rejects(open(), /resume failed/);
assert.equal(second.engine.host.container.isConnected, false);
assert.equal(released, 2); assert.equal(released, 2);
const third = await open();
third.suspend = () => {
throw new Error('suspend failed');
};
cache.retain('navara');
assert.throws(release, /suspend failed/);
assert.equal(third.engine.host.container.isConnected, false);
cache.clear();
assert.equal(released, 3);
}); });