Files
open-wc/docs/modules/wmo-runtime-scene-preparer.md
sindoring ffed91c364 render: extract WMO runtime scene preparer
Work-Package: M03-RND-WMO-RUNTIME-SCENE-PREPARER-001
Agent: sindo-main-codex
Tests: 64/65 headless verifiers passed; proprietary ADT probe unavailable; checkpoint dry-run 7/7; docs and coordination passed
Fidelity: preserves cached/live preparation distinction, direct occluder policy and shadow semantics
2026-08-01 10:37:23 +04:00

10 KiB

WMO Runtime Scene Preparer

Metadata

Field Value
Status Implemented
Target M03 Renderer Facade and Safe Extraction
Work package M03-RND-WMO-RUNTIME-SCENE-PREPARER-001
Owner Render
Last verified 2026-08-01

Purpose

WmoRuntimeScenePreparer applies the existing post-instantiation rules to a borrowed WMO subtree on the renderer main thread. Cached scenes receive recursive Mesh finalization before render policy; live-built duplicates receive render policy only.

Non-goals

  • load, validate, instantiate, duplicate, place or attach a WMO scene;
  • change WmoRuntimeMeshFinalizer material/version rules;
  • own the supplied subtree or decide Editor persistence;
  • change cache formats, queue progress, permits or shutdown;
  • implement portal/room visibility or recursive occluder discovery.

Context and boundaries

The loader distinguishes validated cached PackedScene instances from duplicated live-built prototypes. That distinction remains explicit through two public methods so the live path does not gain cached-Mesh refresh behavior.

flowchart LR
    Cached[Validated cached WMO instance] --> Loader[StreamingWorldLoader]
    Live[Duplicated live-built WMO instance] --> Loader
    Loader -->|prepare_cached_instance| Preparer[WmoRuntimeScenePreparer]
    Loader -->|prepare_live_instance| Preparer
    Preparer -->|cached only| MeshFinalizer[WmoRuntimeMeshFinalizer]
    Preparer --> Policy[Direct Occluders policy + recursive shadow enabling]
    Policy --> Borrowed[Borrowed WMO Node3D subtree]
    Loader --> Placement[Placement/attachment/registry lifecycle]

Public API

Symbol Role Thread/lifetime Failure behavior
prepare_cached_instance(instance, extracted_directory, enable_occlusion_culling, cast_shadows) Finalize cached subtree Meshes, then apply render policy Renderer main thread; stateless after return Null/freed root returns false
prepare_live_instance(instance, enable_occlusion_culling, cast_shadows) Apply render policy without Mesh finalization Renderer main thread; stateless after return Null/freed root returns false

Both methods borrow the root and return a success flag. Disabled occlusion removes only the direct child named Occluders, matching the previous loader lookup. Enabled shadows set all descendant GeometryInstance3D nodes to ON. Disabled shadows preserve every existing value rather than forcing OFF.

Inputs and outputs

Direction Contract/data Producer Consumer Ownership Thread/lifetime
Input Cached or live-built WMO Node3D Loader scene/prototype adapter Preparer Borrowed exact subtree One main-thread call
Input Extracted directory Loader configuration Runtime Mesh finalizer via preparer Borrowed String Cached preparation call
Input Occlusion/shadow policy Loader quality configuration Preparer Scalar values One call
Internal Mesh/MultiMesh Mesh reference Borrowed subtree traversal WmoRuntimeMeshFinalizer Exact Resource reference; not retained by preparer One cached call
Output Success flag Preparer Loader Value Immediate
Side effect Mesh refresh, optional child removal and shadow mutation Preparer Borrowed subtree SceneTree remains loader/placement-owned Until subtree release

No filesystem, ResourceLoader, worker, RID, cache, queue, placement, attachment, Editor-owner or diagnostic side effect is introduced.

Data flow

flowchart LR
    Input[Borrowed root + path kind + policies] --> Valid{Root valid?}
    Valid -->|no| False[Return false]
    Valid -->|yes cached| Walk[Parent-before-children traversal]
    Valid -->|yes live| Policy
    Walk --> Mesh{MeshInstance or non-null MultiMesh?}
    Mesh -->|yes| Finalize[WmoRuntimeMeshFinalizer.finalize_mesh]
    Mesh -->|no| Next[Continue]
    Finalize --> Next
    Next --> Policy[Apply direct Occluders and shadow policies]
    Policy --> True[Return true]

Main sequence

sequenceDiagram
    participant Loader as StreamingWorldLoader
    participant Preparer as WmoRuntimeScenePreparer
    participant Finalizer as WmoRuntimeMeshFinalizer
    participant Root as Borrowed WMO subtree
    alt cached scene
        Loader->>Preparer: prepare_cached_instance(root, directory, policies)
        loop parent-before-children Mesh traversal
            Preparer->>Finalizer: finalize_mesh(exact Mesh, directory)
        end
    else live-built duplicate
        Loader->>Preparer: prepare_live_instance(root, policies)
    end
    Preparer->>Root: optional direct Occluders removal
    Preparer->>Root: optional recursive shadow ON
    Preparer-->>Loader: true/false
    Loader->>Loader: place, attach, register and own lifetime

Dependency diagram

flowchart TB
    Loader[StreamingWorldLoader] --> Preparer[WmoRuntimeScenePreparer]
    Preparer --> Finalizer[WmoRuntimeMeshFinalizer]
    Preparer --> Engine[Node3D / GeometryInstance3D / Mesh / MultiMesh]
    Preparer -. no dependency .-> IO[ResourceLoader / FileAccess]
    Preparer -. no dependency .-> Queue[WMO queues / RenderBudgetScheduler]
    Preparer -. no dependency .-> Placement[WmoPlacementResolver / Registry]

Ownership, threading and resources

  • Calls are main-thread only because Mesh Resources and SceneTree nodes mutate.
  • The loader/placement registry retains ownership of the root and descendants.
  • Mesh finalization receives exact borrowed Resource identities.
  • A removed direct Occluders child is detached and queue_free()d as before.
  • The service retains no Node, Resource, RID, path, collection or state after return.

Errors, cancellation and recovery

Failure/state Detection Behavior Recovery
Null/freed root Guard Return false, no traversal Caller drops/retries stale placement
Missing finalizer composition Null dependency Skip Mesh refresh but apply render policy Fix renderer composition before cached use
Null Mesh Exact finalizer call for MeshInstance; finalizer handles null Continue traversal Cache rebuild/finalizer diagnostics
Null MultiMesh or its Mesh Guard/finalizer contract Skip null MultiMesh; finalizer handles null Mesh Continue safely
Occlusion disabled Direct-child lookup Detach and queue-free Occluders Reinstantiate to restore subtree
Shadows disabled Policy branch Preserve current node settings Re-run with enabled policy if required
Placement cancellation/shutdown Loader lifecycle Service has no retained work to cancel Existing subtree release/drain order

Configuration and capabilities

The service introduces no setting. It receives existing enable_occlusion_culling and wmo_cast_shadows values. The direct-child name Occluders and shadow-ON behavior are compatibility rules, not new capabilities.

Persistence, cache and migration

No serialized format or cache version changes. Cached Mesh refresh metadata continues to belong to WmoRuntimeMeshFinalizer; no rebake or migration is needed.

Diagnostics and observability

The preparer emits no log or metric. Loader placement/build metrics and the Mesh finalizer contracts remain the diagnostic surfaces.

Verification

  • verify_wmo_runtime_scene_preparer.gd covers cached parent-before-children Mesh/MultiMesh identity, directory forwarding, null MultiMesh, direct versus nested Occluders, enabled/preserved shadows, live finalizer suppression, missing dependency, invalid roots, source boundaries and 1,000 traversals.
  • Adjacent WMO finalizer/cache/queue/shutdown and checkpoint checks protect the unchanged lifecycle and presentation behavior.
  • Fidelity evidence is exact behavior-preserving extraction; no asset-backed or original-client 3.3.5a visual-parity claim is added.

The synthetic budget is 1,000 two-Mesh cached preparations under one second. Asset-backed CPU/GPU p95/p99, traversal and leak measurements remain pending.

Extension points

  • Asset-backed traversal can validate material identity and subtree lifetime without expanding this service contract.
  • Portal/room visibility requires its own documented WMO service and evidence; it must not be hidden inside generic subtree preparation.

Capability status

Capability Status Evidence Gap/next step
Cached WMO subtree Mesh finalization Implemented extraction Exact traversal/resource/directory contract Asset-backed visual/GPU p95/p99 pending
Live/cached render policy Implemented extraction Direct occluder and recursive shadow contract Portal/room and asset-backed traversal pending
Placement/attachment/lifetime Loader-owned Existing registry/shutdown regressions Further orchestration extraction pending

Known gaps and risks

  • Recursive Mesh finalization and shadow mutation remain synchronous main-thread work.
  • The missing-finalizer branch degrades safely for isolated tests but production composition must always inject WmoRuntimeMeshFinalizer.
  • No private WMO corpus, portal/room behavior, long traversal, leak/GPU timing or paired original-client capture is included.

Source map

Path Responsibility
src/render/wmo/wmo_runtime_scene_preparer.gd Cached/live subtree traversal and render policy
src/render/wmo/wmo_runtime_mesh_finalizer.gd Borrowed Mesh refresh/version/material rules
src/scenes/streaming/streaming_world_loader.gd Composition, instantiation, placement, attachment and lifetime
src/tools/verify_wmo_runtime_scene_preparer.gd Synthetic traversal/policy/boundary/timing regression