ffed91c364
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
212 lines
10 KiB
Markdown
212 lines
10 KiB
Markdown
# 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.
|
|
|
|
```mermaid
|
|
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
|
|
|
|
```mermaid
|
|
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
|
|
|
|
```mermaid
|
|
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
|
|
|
|
```mermaid
|
|
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 |
|
|
|
|
## Related decisions and references
|
|
|
|
- [`wmo-runtime-mesh-finalizer.md`](wmo-runtime-mesh-finalizer.md)
|
|
- [`wmo-scene-resource-finalizer.md`](wmo-scene-resource-finalizer.md)
|
|
- [`wmo-render-group-materializer.md`](wmo-render-group-materializer.md)
|
|
- [`world-renderer.md`](world-renderer.md)
|
|
- [`../../RENDER.md`](../../RENDER.md)
|
|
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
|