mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-20 17:02:53 -04:00
pagescene: free the scene when the editor unmounts
unmountEngine disposed the renderers but none of the scene. Every mount builds a new Editor, and on a scene change it builds it on the same canvases, which hands it the same WebGL context, so the old scene's buffers and textures stayed allocated in a live context. In the webapp this happened on every scene-to-scene navigation. Unmount now finishes running entrances, disposes the gizmo (which also removes its pointer listeners from the reused canvas) and frees the scene's contents and background. This is safe because EngineProvider serializes the scene before calling unmountEngine and nothing reuses an Editor after it. The old pause/resume use of unmount/remount, which kept the scene alive across tab switches, went away in #654. forceContextLoss is deliberately not called: the next Editor may be created on these canvases and would start on a lost context.
This commit is contained in:
@@ -1128,6 +1128,16 @@ class Editor {
|
||||
this.bus.emit(new SceneLoadedEvent(false));
|
||||
this.stopRenderLoop();
|
||||
|
||||
// Free the scene's GPU resources. EngineProvider serialized the scene
|
||||
// before calling us, and the next mount builds a fresh Editor, so
|
||||
// nothing reads this scene again. The gizmo's dispose also removes its
|
||||
// pointer listeners from the canvas. No forceContextLoss: a scene
|
||||
// change mounts the next Editor on these same canvases, which hands it
|
||||
// this same WebGL context.
|
||||
this.entranceAnimator.clear();
|
||||
this.gizmo.control?.dispose();
|
||||
this.activeScene.dispose();
|
||||
|
||||
// Fix: dispose 3D contexts
|
||||
this.renderer?.dispose();
|
||||
this.postProcessing.dispose();
|
||||
|
||||
@@ -110,6 +110,23 @@ describe("Scene disposal", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("dispose frees the contents and the background", () => {
|
||||
const scene = makeScene();
|
||||
const geometry = new THREE.BoxGeometry();
|
||||
const background = new THREE.CubeTexture();
|
||||
const disposed = new Set<unknown>();
|
||||
geometry.addEventListener("dispose", () => disposed.add(geometry));
|
||||
background.addEventListener("dispose", () => disposed.add(background));
|
||||
scene.scene.add(new THREE.Mesh(geometry));
|
||||
scene.scene.background = background;
|
||||
|
||||
scene.dispose();
|
||||
|
||||
expect(disposed).toEqual(new Set([geometry, background]));
|
||||
expect(scene.scene.children).toHaveLength(0);
|
||||
expect(scene.scene.background).toBeNull();
|
||||
});
|
||||
|
||||
it("disposeBackground frees a texture background", () => {
|
||||
const scene = makeScene();
|
||||
const background = new THREE.CubeTexture();
|
||||
|
||||
@@ -178,6 +178,15 @@ class Scene {
|
||||
disposeObject3D(root);
|
||||
}
|
||||
|
||||
// Final teardown when the owning Editor unmounts: free the contents and
|
||||
// the background. The Scene is not used again — every mount builds a new
|
||||
// Editor, and EngineProvider serializes the scene before unmounting.
|
||||
dispose() {
|
||||
this.disposeContents();
|
||||
this.disposeBackground();
|
||||
this.scene.background = null;
|
||||
}
|
||||
|
||||
disposeBackground() {
|
||||
if (this.scene.background instanceof THREE.Texture) {
|
||||
this.scene.background.dispose();
|
||||
|
||||
Reference in New Issue
Block a user