render: extract cached M2 animation resource observer
This commit is contained in:
@@ -0,0 +1,239 @@
|
||||
# M2 Cached Animation Resource Observer
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-CACHED-ANIMATION-RESOURCE-OBSERVER-001` |
|
||||
| Owners | Cached animated M2 eligibility, request admission and initial snapshot |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-cached-animation-resource-observer`, 2026-07-18 |
|
||||
| Profiles/capabilities | Existing optional cached-GLB animated M2 path |
|
||||
|
||||
## Purpose
|
||||
|
||||
Produce the cached-animation phase of `M2BuildResourceSnapshot`: reuse an exact
|
||||
animated prototype, wait for a pending request, admit the first safe GLB request,
|
||||
or record a terminal static-only animation outcome without changing behavior.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Load/build native GryphonRoost animation or emit its diagnostics.
|
||||
- Poll/finalize threaded Resources or instantiate/fix imported scenes.
|
||||
- Own/free prototype Nodes, snapshots, pipeline entries or cache state.
|
||||
- Materialize instances, consume permits or change animation policy defaults.
|
||||
- Change GLB layout, cache format, batching, profiles or visible output.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Loader[StreamingWorldLoader] --> Native[Native animation attempt]
|
||||
Native -->|no prototype| Observer[M2CachedAnimationResourceObserver]
|
||||
Prototype[M2PrototypeCacheState] --> Observer
|
||||
Pipeline[M2AnimationLoadPipelineState] --> Observer
|
||||
Resource[ResourceLoader and GLB JSON] --> Observer
|
||||
Observer --> Snapshot[M2BuildResourceSnapshot]
|
||||
Snapshot --> Dispatch[M2BuildDispatchPlanner]
|
||||
```
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Failure behavior |
|
||||
|---|---|---|---|
|
||||
| `observe(path, cache_dir, max_primitives, allow, deny, prototype_state, pipeline_state, debug)` | Command/query | Produce cached prototype/pending/static-only snapshot | Invalid input returns empty non-pending snapshot |
|
||||
| `find_eligible_glb_cache_path(...)` | I/O query | Select first existing safe historical candidate | Returns empty String |
|
||||
| `is_animation_path_allowed(path, allow, deny)` | Pure query | Apply stripped case-insensitive substring policy | Empty path/allowlist rejects; deny wins |
|
||||
| `cache_resource_paths(cache_dir, path)` | Pure query | Return nested/lowercase/basename GLB candidates | Empty values yield fewer candidates |
|
||||
| `glb_cache_is_safe_for_runtime_animation(path, max, debug)` | I/O query | Validate animations, primitive limit and schema | Invalid/missing GLB rejects |
|
||||
| `read_glb_json(path)` | I/O query | Read version-2 GLB JSON chunk | Returns empty Dictionary |
|
||||
| `glb_primitive_count(gltf)` | Pure query | Count primitives across meshes | Invalid entries contribute zero |
|
||||
| `glb_animation_schema(gltf)` | Pure query | Read OpenWC schema marker | Missing marker returns empty String |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Data | Producer | Consumer | Ownership/lifetime |
|
||||
|---|---|---|---|---|
|
||||
| Input | Normalized M2 path/cache directory | Loader | Observer | Copied Strings; one observation |
|
||||
| Input | Primitive cap and allow/deny patterns | Loader exports | Observer | Copied/read-only call values |
|
||||
| Input | Prototype and request states | M2 state services | Observer | Borrowed services; map/session |
|
||||
| Input | Existing GLB cache file | Offline cache pipeline | Observer | Read-only file access |
|
||||
| Output | Resource snapshot | Observer | Loader/dispatch/materializer | Caller-owned snapshot; one build step |
|
||||
| Output | Borrowed exact prototype | Prototype state | Snapshot/materializer | No ownership transfer |
|
||||
| Output | Threaded request record | Observer | Animation pipeline | Pipeline-owned copied paths |
|
||||
| Output | Static-only transition | Observer | Prototype state | State-owned copied path |
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start[Observe normalized path] --> Valid{Valid composition?}
|
||||
Valid -->|no| Empty[Empty non-pending snapshot]
|
||||
Valid -->|yes| Cached{Prototype cached?}
|
||||
Cached -->|yes| Ready[Snapshot with exact prototype]
|
||||
Cached -->|no| Static{Already static-only?}
|
||||
Static -->|yes| Empty
|
||||
Static -->|no| Pending{Request exists?}
|
||||
Pending -->|yes| Wait[Pending snapshot]
|
||||
Pending -->|no| Policy{Allow and not deny?}
|
||||
Policy -->|no| Mark[Mark static-only]
|
||||
Policy -->|yes| Candidate[Historical GLB candidates]
|
||||
Candidate --> Safe{Animations, primitive cap, schema safe?}
|
||||
Safe -->|no usable| Mark
|
||||
Safe -->|yes| Request[Threaded request]
|
||||
Request -->|OK or busy| Remember[Remember and return pending snapshot]
|
||||
Request -->|error| Mark
|
||||
Mark --> Empty
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Observing
|
||||
Observing --> Cached
|
||||
Observing --> Pending
|
||||
Observing --> Requested
|
||||
Observing --> StaticOnly
|
||||
Observing --> Rejected
|
||||
Cached --> [*]
|
||||
Pending --> [*]
|
||||
Requested --> [*]
|
||||
StaticOnly --> [*]
|
||||
Rejected --> [*]
|
||||
```
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant L as StreamingWorldLoader
|
||||
participant O as CachedAnimationObserver
|
||||
participant C as PrototypeCacheState
|
||||
participant P as AnimationLoadPipelineState
|
||||
participant R as ResourceLoader
|
||||
participant S as M2BuildResourceSnapshot
|
||||
L->>L: attempt native candidate
|
||||
alt no native prototype
|
||||
L->>O: observe(path, cache/policy/state)
|
||||
O->>C: find animated / static-only
|
||||
O->>P: has request
|
||||
opt eligible cache candidate
|
||||
O->>R: exists + GLB JSON + threaded request
|
||||
O->>P: remember request
|
||||
end
|
||||
O-->>L: snapshot
|
||||
else native prototype
|
||||
L->>S: construct native snapshot
|
||||
end
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Observer[M2CachedAnimationResourceObserver] --> Snapshot[M2BuildResourceSnapshot]
|
||||
Observer --> Prototype[M2PrototypeCacheState]
|
||||
Observer --> Pipeline[M2AnimationLoadPipelineState]
|
||||
Observer --> ResourceLoader
|
||||
Observer --> FileAccess[GLB header and JSON]
|
||||
Observer -. no dependency .-> Native[M2 native builder/repository]
|
||||
Observer -. no dependency .-> Finalizer[M2AnimatedSceneFinalizer]
|
||||
Observer -. no dependency .-> Scheduler[RenderBudgetScheduler]
|
||||
Observer -. no dependency .-> SceneTree
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Observer retains no service, request, snapshot, Resource or Node reference.
|
||||
- Prototype state retains accepted Nodes and static-only paths until shutdown.
|
||||
- Pipeline state retains copied request paths until terminal polling/finalize.
|
||||
- Snapshot borrows the exact cached prototype; observer never frees it.
|
||||
- Resource existence/request and synchronous GLB JSON inspection run on the
|
||||
renderer main thread, matching the previous loader behavior.
|
||||
- Loader remains owner of native animation build, terminal polling/finalize,
|
||||
permits, materialization and every SceneTree mutation.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure/state | Behavior | Recovery |
|
||||
|---|---|---|
|
||||
| Empty path/dependency | Return empty non-pending snapshot without mutation | Correct composition |
|
||||
| Cached prototype | Return exact reference immediately | None |
|
||||
| Existing request | Return pending without duplicate admission | Retry after terminal drain |
|
||||
| Disallowed/denied path | Mark static-only | New loader session/configuration |
|
||||
| Missing/invalid/unsafe GLB | Continue candidates, then mark static-only | Repair/rebake cache and restart session |
|
||||
| Request error | Mark static-only | New loader session/cache repair |
|
||||
| Tile cancellation | Observer retains nothing | Loader queue remains authoritative |
|
||||
| Shutdown | Loader drains requests before state clear | New loader starts empty |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
The service consumes existing `m2_cache_dir`, `m2_animated_max_primitives`,
|
||||
`m2_animated_allowlist_patterns`, `m2_animated_denylist_patterns` and debug flag.
|
||||
It adds no setting and preserves runtime mutability and defaults.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
No persistence or cache format changes are introduced. Existing `.glb` candidate
|
||||
layout and OpenWC schema markers are read unchanged; no rebake is required.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
Unsupported non-empty schemas preserve the historical optional
|
||||
`M2_ANIM_REJECT` debug line. Snapshot and pipeline diagnostics remain detached;
|
||||
queue and hitch metrics are unchanged.
|
||||
|
||||
## Verification
|
||||
|
||||
- Dedicated verifier covers invalid/cached identity/static/pending/missing
|
||||
lifecycle, allow/deny semantics, exact path order, generated GLB header/JSON,
|
||||
animation presence, primitive cap, accepted/rejected schemas, source boundaries
|
||||
and bounded timing.
|
||||
- Snapshot, dispatch, pipeline, prototype, shutdown, facade, internal-access and
|
||||
checkpoint regressions protect adjacent behavior.
|
||||
- Fidelity evidence is exact policy/I/O extraction. No original-client visual or
|
||||
animation parity claim is made.
|
||||
|
||||
## Extension points
|
||||
|
||||
Native animation observation can become a sibling service without changing this
|
||||
cached-GLB contract. Terminal ResourceLoader polling/finalization may later move
|
||||
behind a dedicated adapter while retaining the pipeline state.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Cached animation observation/request | Implemented extraction | Lifecycle/policy/GLB/source/timing verifier | Asset-backed traversal pending |
|
||||
| Native animation observation | Existing loader-owned | Native renderer regression | Separate extraction |
|
||||
| Animated finalize/preparation | Loader/finalizer split | Pipeline/finalizer regressions | Polling adapter optional |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- A successful asynchronous request is not started by the unit fixture because
|
||||
leaving it undrained leaks process work; source and pipeline tests cover admission.
|
||||
- Generated GLB metadata fixtures validate policy, not Godot import fidelity.
|
||||
- Private traversal, leak/descriptor pressure, p95/p99 and paired visuals remain
|
||||
unavailable without private assets.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_cached_animation_resource_observer.gd` | Cached GLB policy, request and snapshot production |
|
||||
| `src/render/m2/m2_build_resource_snapshot.gd` | Typed per-step observation value |
|
||||
| `src/render/m2/m2_animation_load_pipeline_state.gd` | Pending request/finalize state |
|
||||
| `src/render/m2/m2_prototype_cache_state.gd` | Prototype and static-only outcomes |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Native attempt, composition, finalize and execution |
|
||||
| `src/tools/verify_m2_cached_animation_resource_observer.gd` | Lifecycle/policy/GLB/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-build-resource-snapshot.md`](m2-build-resource-snapshot.md)
|
||||
- [`m2-animation-load-pipeline-state.md`](m2-animation-load-pipeline-state.md)
|
||||
- [`m2-prototype-cache-state.md`](m2-prototype-cache-state.md)
|
||||
- [`m2-animated-scene-finalizer.md`](m2-animated-scene-finalizer.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)
|
||||
Reference in New Issue
Block a user