render: extract static M2 build resource observer
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
# M2 Static Build Resource Observer
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-STATIC-BUILD-RESOURCE-OBSERVER-001` |
|
||||
| Owners | Static build Mesh lookup, request selection and missing transition |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-static-build-resource-observer`, 2026-07-18 |
|
||||
| Profiles/capabilities | Existing static MultiMesh M2 build path |
|
||||
|
||||
## Purpose
|
||||
|
||||
Produce the static phase of `M2BuildResourceSnapshot`: reuse a prepared Mesh,
|
||||
wait for an existing request, start the first eligible cache request, or record
|
||||
a terminal missing model while preserving historical path and GLB rules.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Observe animated/native prototypes or finalize threaded loads.
|
||||
- Own/free Meshes, Nodes, cache entries or snapshot lifetime.
|
||||
- Materialize instances, plan batches, rotate queues or consume permits.
|
||||
- Change cache format, candidate order, profiles or visible output.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Loader[StreamingWorldLoader] --> Observer[M2StaticBuildResourceObserver]
|
||||
MeshCache[M2MeshResourceCacheState] --> Observer
|
||||
Missing[M2PrototypeCacheState] --> Observer
|
||||
Pipeline[M2MeshLoadPipelineState] --> Observer
|
||||
Observer --> Snapshot[M2BuildResourceSnapshot]
|
||||
Snapshot --> Dispatch[M2BuildDispatchPlanner]
|
||||
```
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Errors |
|
||||
|---|---|---|---|
|
||||
| `observe(snapshot, path, cache_dir, mesh_cache, prototype_cache, pipeline)` | Command/query | Adopt cached/pending/requested/missing static result | Invalid inputs return `rejected` |
|
||||
| `cache_resource_paths(cache_dir, path, extensions)` | Pure query | Return unique historical nested/lowercase/basename candidates | Empty values yield fewer/empty candidates |
|
||||
| `glb_animation_schema(path)` | Read query | Read OpenWC schema marker from GLB JSON | Invalid/missing GLB returns empty String |
|
||||
| `OUTCOME_*` | Constants | Stable diagnostic transition identifiers | None |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Data | Producer | Consumer | Ownership/lifetime |
|
||||
|---|---|---|---|---|
|
||||
| Input | Normalized M2 path/cache directory | Loader | Observer | Copied Strings; one observation |
|
||||
| Input | Mesh/missing/request states | M2 state services | Observer | Borrowed services; map/session |
|
||||
| Input/output | Resource snapshot | Loader | Observer/dispatch/materializer | Borrowed; one build operation |
|
||||
| Output | Cached Mesh or missing/pending values | Observer | Snapshot | Exact borrowed Mesh/scalars |
|
||||
| Output | Threaded request record | Observer | Mesh pipeline | Pipeline-owned path record |
|
||||
| Output | Outcome StringName | Observer | Tests/future diagnostics | Immutable scalar |
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start[Observe static path] --> Cached{Mesh cached?}
|
||||
Cached -->|yes| AdoptMesh[Adopt exact Mesh; cached]
|
||||
Cached -->|no| Missing{Already missing?}
|
||||
Missing -->|yes| AdoptMissing[Adopt missing]
|
||||
Missing -->|no| Pending{Request pending?}
|
||||
Pending -->|yes| AdoptPending[Adopt unresolved; pending]
|
||||
Pending -->|no| Candidates[Generate tscn then glb candidates]
|
||||
Candidates --> Eligible{Exists and not pivot-prefix GLB?}
|
||||
Eligible -->|yes| Request[Threaded request]
|
||||
Request -->|OK or busy| Remember[Remember request; requested]
|
||||
Eligible -->|none/error| MarkMissing[Mark/adopt missing]
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Observing
|
||||
Observing --> Cached
|
||||
Observing --> Pending
|
||||
Observing --> Requested
|
||||
Observing --> Missing
|
||||
Observing --> Rejected
|
||||
Cached --> [*]
|
||||
Pending --> [*]
|
||||
Requested --> [*]
|
||||
Missing --> [*]
|
||||
Rejected --> [*]
|
||||
```
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant L as StreamingWorldLoader
|
||||
participant O as StaticBuildResourceObserver
|
||||
participant C as Mesh/prototype cache state
|
||||
participant P as MeshLoadPipelineState
|
||||
participant R as ResourceLoader
|
||||
participant S as M2BuildResourceSnapshot
|
||||
L->>O: observe(snapshot, path, cache_dir, states)
|
||||
O->>C: cached/missing lookup
|
||||
alt cache hit or missing
|
||||
O->>S: adopt static observation
|
||||
else pending
|
||||
O->>P: has_request
|
||||
O->>S: adopt unresolved observation
|
||||
else request candidate
|
||||
O->>R: exists + load_threaded_request
|
||||
O->>P: remember_request
|
||||
O->>S: adopt unresolved observation
|
||||
end
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Observer[M2StaticBuildResourceObserver] --> ResourceLoader
|
||||
Observer --> FileAccess[GLB header/JSON read]
|
||||
Observer --> MeshCache[M2MeshResourceCacheState]
|
||||
Observer --> PrototypeCache[M2PrototypeCacheState]
|
||||
Observer --> Pipeline[M2MeshLoadPipelineState]
|
||||
Observer --> Snapshot[M2BuildResourceSnapshot]
|
||||
Observer -. no dependency .-> SceneTree
|
||||
Observer -. no dependency .-> Materializers
|
||||
Observer -. no dependency .-> Scheduler
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Observer retains no request, cache, snapshot or engine reference.
|
||||
- Cache/pipeline services retain their existing state and resource ownership.
|
||||
- Snapshot borrows the exact cached Mesh; observer never frees it.
|
||||
- Resource existence/request and GLB inspection run on renderer main thread.
|
||||
- Finalization and Mesh preparation remain in the loader drain path.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| State | Behavior | Recovery |
|
||||
|---|---|---|
|
||||
| Invalid dependency/path | Return `rejected`; no mutation | Correct composition |
|
||||
| Existing request | Return `pending`; no duplicate request | Retry when queue rotates |
|
||||
| Pivot-prefix GLB | Skip as static candidate | Continue next candidate |
|
||||
| Request error | Stop historical search and mark missing | Later reset/new session |
|
||||
| No candidate | Mark/adopt terminal missing | Cache content becomes available in new session |
|
||||
| Tile cancellation | Observer retains nothing | Loader/queue cancellation remains authoritative |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
The observer consumes existing `m2_cache_dir`. It adds no profile, cache version,
|
||||
batch, visibility, shadow or scheduler setting.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
No new persistence is introduced. Existing `.tscn/.glb` layout, pipeline records
|
||||
and missing-cache lifetime are unchanged; no rebake is required.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
Returned outcomes distinguish rejected/cached/pending/requested/missing without
|
||||
logging. Existing queue and hitch metrics remain unchanged.
|
||||
|
||||
## Verification
|
||||
|
||||
- Dedicated verifier covers invalid, cached identity, missing, pending, path
|
||||
order/deduplication, absent GLB schema, source ownership and bounded timing.
|
||||
- Snapshot/dispatch/pipeline/cache/shutdown/facade/internal-access/checkpoint
|
||||
regressions protect adjacent behavior.
|
||||
- No original-client visual claim is made; this is bookkeeping/I/O extraction.
|
||||
|
||||
## Extension points
|
||||
|
||||
Animated/native observation may become a separate producer of the animation
|
||||
phase. Shared cache candidate/GLB inspection can later be reused without adding
|
||||
a generic resource framework.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap |
|
||||
|---|---|---|---|
|
||||
| Static build lookup/request production | Implemented extraction | Lifecycle/path/source/timing verifier | Asset-backed traversal pending |
|
||||
| Static finalize/preparation | Existing loader-owned | Pipeline/finalizer regressions | Later extraction |
|
||||
| Animated observation | Existing loader-owned | Animation regressions | Separate producer pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Successful request behavior is protected by source plus existing pipeline
|
||||
tests; starting an undrained asynchronous request in the unit fixture would leak.
|
||||
- GLB schema parser reads synchronously, matching the previous loader behavior.
|
||||
- Private traversal, p95/p99, leak and visual evidence remain unavailable.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_static_build_resource_observer.gd` | Static cache/request/missing observation |
|
||||
| `src/render/m2/m2_build_resource_snapshot.gd` | Typed observation result |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Composition, finalize drain and action execution |
|
||||
| `src/tools/verify_m2_static_build_resource_observer.gd` | Lifecycle/path/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-build-resource-snapshot.md`](m2-build-resource-snapshot.md)
|
||||
- [`m2-mesh-load-pipeline-state.md`](m2-mesh-load-pipeline-state.md)
|
||||
- [`m2-mesh-resource-cache-state.md`](m2-mesh-resource-cache-state.md)
|
||||
- [`world-renderer.md`](world-renderer.md)
|
||||
- [`../../RENDER.md`](../../RENDER.md)
|
||||
Reference in New Issue
Block a user