Skip to content

fix: reuse cached scene definition on LSD reload instead of full re-discovery - #9505

Draft
dalkia wants to merge 1 commit into
devfrom
fix/lsd-reload-reuse-scene-definition
Draft

fix: reuse cached scene definition on LSD reload instead of full re-discovery#9505
dalkia wants to merge 1 commit into
devfrom
fix/lsd-reload-reuse-scene-definition

Conversation

@dalkia

@dalkia dalkia commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Pull Request Description

What does this PR change?

Cuts ~1.8s off every Local Scene Development (LSD) scene reload by reusing the scene definition already cached in ECS instead of re-running the full static-pointer discovery pipeline.

Problem. On every LSD reload (/reload-scene chat command or CLI websocket trigger), the client destroyed the scene definition entity and nulled StaticScenePointers.Promise, forcing a complete re-discovery: entities/active POST → deferred-loading queue wait → definition deserialization → scene entity re-creation. Measured on Genesis Plaza 2025 via LSD, this discovery round-trip costs ~1.8s of an ~4.9s reload (~6.1s with local asset bundles, where the pipeline additionally re-fetches the AB manifest twice — see follow-ups).

Why the re-fetch buys nothing. The LSD dev server derives content hashes from file paths, not content (entity id = b64 of the project path; every content entry hash = b64 of the file path). Editing code or models changes nothing in the definition — the same JSON is returned byte-for-byte. The client re-fetches the JS bundle and re-loads assets on reload anyway.

Fix. Two deletions, reusing the mechanism regular scenes already have:

  • UnloadSceneSystem: stop excluding LSD from the "keep definition entity = cache in ECS itself" path (the localSceneDevelopment ctor param is now unused and removed).
  • ECSReloadScene: stop nulling StaticScenePointers.Promise on LSD reload.

On reload, ResolveStaticPointersSystem recreates the facade promise from the kept entity (it queries for definition entities with no ISceneFacade/promise). Realm teardown still destroys the kept entities via RealmController.CLEAR_QUERY, same as regular scenes.

Reload timing (Genesis Plaza 2025, measured before / expected after):

Step Before After
Dispose + cache drain ~0.5s ~0.5s
Definition re-discovery ~1.8s ~0s
Facade creation (JS fetch + V8 + ECS world) ~1.3s ~1.3s
Total (to Running + loading concluded signal) ~4.9s ~3.1s

Known limitation. Adding or removing files changes the definition's content list and is no longer picked up by a reload — re-entering the realm (or restarting the preview) is required. Edits to existing files (code, models, scene.json metadata — the overwhelmingly common case) are unaffected: scene.json is re-fetched during facade creation, and JS/assets are re-fetched by path. A follow-up can restore added-file support with an inline entities/active refresh (~0.3s) that patches the kept definition.

Follow-ups identified while profiling (not in this PR):

  • With --local-asset-bundles, the definition flow fetches the same AB manifest twice (AssetBundleManifestFallbackHelper + SceneAssetBundleDigestsLoader) at ~2.5s each — the manifest system uses NoCache, so the "deduped via the promise cache" assumption doesn't hold.
  • GatherGltfAssetsSystem concludes immediately on reload (no readiness report queued → assets 0/N), so SceneLoadingConcluded fires before any GLTF is loaded, defeating the collider-protection wait in ECSReloadScene and unfreezing the character early.
  • The LSD websocket reload timeout (5s) is shorter than a real reload, so ws-triggered reloads always "time out".

Test Instructions

Steps (LSD reload):

# In a scene project (e.g. Genesis-Plaza-2025):
npm run start  # CLI preview server on :8000
# Launch Explorer with local scene development pointing at the local realm,
# teleport into the scene, then trigger a reload:
#   - chat command: /reload-scene
#   - or save a source file so the CLI ws message triggers the reload

Expected result:

  • Scene reloads visibly faster (definition re-discovery step gone; no entities/active POST between reloads in the CLI server logs).
  • Code edits are reflected after reload; edits to existing GLB/assets are reflected after reload.
  • Known limitation (expected): a newly added file is not picked up until the realm is re-entered.
  • Regular (non-LSD) realm scene loading/unloading behavior is unchanged.

Automation (if applicable):

metaforge explorer test XXXX

🤖 Generated with Claude Code

…iscovery

LSD reloads re-ran the whole static-pointer discovery pipeline (entities/active
POST, deferred-loading queue, definition deserialization) on every reload,
costing ~1.8s, even though the definition cannot change for edits-in-place:
the local dev server derives content hashes from file paths, not content.

Keep the scene definition entity alive on unload for LSD exactly like regular
scenes (UnloadSceneSystem already does this as an in-ECS definition cache), and
drop the StaticScenePointers promise reset in ECSReloadScene. On reload,
ResolveStaticPointersSystem recreates the facade promise from the kept entity.

Known limitation: newly added/removed files change the definition content list
and are only picked up on realm re-entry, not on reload.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

badge

Build skipped — no changes detected under Explorer/.

@dalkia dalkia self-assigned this Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant