render: extract native M2 animation resource observer

This commit is contained in:
2026-07-18 10:55:47 +04:00
parent d22a9cd743
commit 1cb0101a73
18 changed files with 932 additions and 104 deletions
+1
View File
@@ -25,6 +25,7 @@
| M2 build resource snapshot | Implemented extraction | [`m2-build-resource-snapshot.md`](m2-build-resource-snapshot.md) |
| M2 static build resource observer | Implemented extraction | [`m2-static-build-resource-observer.md`](m2-static-build-resource-observer.md) |
| M2 cached animation resource observer | Implemented extraction | [`m2-cached-animation-resource-observer.md`](m2-cached-animation-resource-observer.md) |
| M2 native animation resource observer | Implemented extraction | [`m2-native-animation-resource-observer.md`](m2-native-animation-resource-observer.md) |
| M2 build queue | Implemented extraction | [`m2-build-queue.md`](m2-build-queue.md) |
| M2 static batch materializer | Implemented extraction | [`m2-static-batch-materializer.md`](m2-static-batch-materializer.md) |
| M2 runtime mesh rebuild classifier | Implemented extraction | [`m2-runtime-mesh-rebuild-classifier.md`](m2-runtime-mesh-rebuild-classifier.md) |
+8 -7
View File
@@ -148,7 +148,7 @@ flowchart TB
- `M2BuildResourceSnapshot` owns the typed call-local observation contract;
dispatch reads availability/pending/missing accessors but no engine references.
- Loader owns native-first observation order, action execution and permit use;
static/cached-animation observers own their lookup/request phases.
native/static/cached-animation observers own their resource phases.
- `M2BuildQueue` owns pending jobs, FIFO keys and progress cursors.
- Materializers own main-thread scene construction under loader-owned roots.
- Pure calls are thread-safe; the current loader adapter calls on main thread.
@@ -192,9 +192,8 @@ remain unchanged.
## Extension points
Static and cached animated observation/request execution now live behind sibling
services that produce `M2BuildResourceSnapshot`. Native animation remains an
orchestrator-owned phase. Generic callbacks, signals and a shared state-machine
Static, cached animated and native animated observation now live behind sibling
services that produce `M2BuildResourceSnapshot`. Generic callbacks, signals and a shared state-machine
framework remain intentionally excluded.
## Capability status
@@ -202,13 +201,14 @@ framework remain intentionally excluded.
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| M2 build action selection | Implemented extraction | Priority/matrix/source/timing verifier | Asset-backed traversal pending |
| Static/cached animation observation and requests | Implemented separately | Observer/cache/pipeline regressions | Native observation remains loader-owned |
| Static/cached/native animation observation | Implemented separately | Observer/cache/pipeline regressions | Asset-backed traversal pending |
| Queue/cursor state | Implemented separately | M2 build queue verifier | Asset-backed traversal pending |
| Materialization | Implemented separately | Static/animated materializer verifiers | GPU/p95/p99 evidence pending |
## Known gaps and risks
- Loader still performs synchronous/native prototype attempts and static request I/O.
- Native prototype attempts remain synchronous through the observer; loader still
owns static request I/O and terminal animated ResourceLoader polling.
- Synthetic timing does not measure ResourceLoader, Node or GPU work.
- Private traversal, leak, visual and p95/p99 evidence remains unavailable.
@@ -219,7 +219,8 @@ framework remain intentionally excluded.
| `src/render/m2/m2_build_dispatch_planner.gd` | Pure action priority and transition plan |
| `src/render/m2/m2_build_resource_snapshot.gd` | Typed per-step resource observation contract |
| `src/render/m2/m2_cached_animation_resource_observer.gd` | Cached animation observation/request phase |
| `src/scenes/streaming/streaming_world_loader.gd` | Native observation order, action execution, permits and engine lifetime |
| `src/render/m2/m2_native_animation_resource_observer.gd` | Native candidate/read/build/cache observation |
| `src/scenes/streaming/streaming_world_loader.gd` | Observation order, action execution, permits and engine lifetime |
| `src/render/m2/m2_build_batch_planner.gd` | Batch count and cursor plan |
| `src/render/m2/m2_build_queue.gd` | Pending jobs, FIFO and cursor ownership |
| `src/tools/verify_m2_build_dispatch_planner.gd` | Matrix, boundary and timing regression |
+10 -9
View File
@@ -28,7 +28,7 @@ static Mesh and terminal missing-model state. The snapshot never owns engine lif
```mermaid
flowchart LR
Native[StreamingWorldLoader native observation] --> Snapshot[M2BuildResourceSnapshot]
Native[M2NativeAnimationResourceObserver] --> Snapshot[M2BuildResourceSnapshot]
Cached[M2CachedAnimationResourceObserver] --> Snapshot
Static[M2StaticBuildResourceObserver] --> Snapshot
Snapshot --> Dispatch[M2BuildDispatchPlanner]
@@ -112,7 +112,7 @@ sequenceDiagram
participant B as M2BuildBatchPlanner
participant D as M2BuildDispatchPlanner
participant M as M2 materializer
L->>L: resolve native animated prototype
L->>L: delegate native animated prototype observation
L->>S: construct native result
opt no native prototype
L->>L: delegate cached animation observer
@@ -149,8 +149,8 @@ flowchart TB
## Ownership, threading and resources
- Snapshot owns only copied scalar state and temporary references.
- Prototype/cache services retain resource state; loader owns native-first
observation order while cached/static observers own their phases.
- Prototype/cache services retain resource state; the native observer owns its
phase while loader owns native-first ordering among resource observers.
- Scene roots remain loader/tile-owned; Mesh lifetime follows cache/resource refs.
- Dispatch planner reads only snapshot accessors and never mutates the snapshot.
- Current construction/adoption occurs on renderer main thread.
@@ -195,8 +195,8 @@ queue/hitch metrics remain unchanged.
## Extension points
`M2CachedAnimationResourceObserver` produces the cached animated phase and
`M2StaticBuildResourceObserver` produces the static phase. Native animation
observation remains loader-owned. The value contract stays independent of all
`M2StaticBuildResourceObserver` produces the static phase. Native animation is
produced by `M2NativeAnimationResourceObserver`. The value stays independent of all
producers and of generic callback frameworks.
## Capability status
@@ -207,14 +207,14 @@ producers and of generic callback frameworks.
| Dispatch decision | Implemented separately | Dispatch planner verifier | Asset-backed traversal pending |
| Static lookup/request execution | Implemented separately | Static observer verifier | Asset-backed traversal pending |
| Cached animated lookup/request execution | Implemented separately | Cached observer verifier | Asset-backed traversal pending |
| Native animated lookup/build execution | Loader-owned | Native renderer regression | Service extraction pending |
| Native animated lookup/build execution | Implemented observer extraction | Native observer lifecycle/source verifier | Asset-backed traversal pending |
| Engine lifetime/materialization | Loader/materializer-owned | Lifetime/materializer regressions | GPU/p95/p99 evidence pending |
## Known gaps and risks
- Static observation is mutable during its short two-phase construction.
- Raw Node3D/Mesh references remain necessary for current materializer APIs.
- Loader still owns synchronous native prototype attempts and terminal polling.
- Native parsing/build remains synchronous; loader still owns terminal polling.
- Synthetic timing does not measure resource, Node or GPU work.
## Source map
@@ -225,7 +225,8 @@ producers and of generic callback frameworks.
| `src/render/m2/m2_build_dispatch_planner.gd` | Snapshot-to-action planning |
| `src/render/m2/m2_static_build_resource_observer.gd` | Static snapshot production and requests |
| `src/render/m2/m2_cached_animation_resource_observer.gd` | Cached animated snapshot production and requests |
| `src/scenes/streaming/streaming_world_loader.gd` | Native animation observation and materializer borrowing |
| `src/render/m2/m2_native_animation_resource_observer.gd` | Native prototype observation producer |
| `src/scenes/streaming/streaming_world_loader.gd` | Observer ordering and materializer borrowing |
| `src/tools/verify_m2_build_resource_snapshot.gd` | Identity/adoption/lifetime/boundary/timing regression |
## Related decisions and references
@@ -28,7 +28,7 @@ or record a terminal static-only animation outcome without changing behavior.
```mermaid
flowchart LR
Loader[StreamingWorldLoader] --> Native[Native animation attempt]
Loader[StreamingWorldLoader] --> Native[M2NativeAnimationResourceObserver]
Native -->|no prototype| Observer[M2CachedAnimationResourceObserver]
Prototype[M2PrototypeCacheState] --> Observer
Pipeline[M2AnimationLoadPipelineState] --> Observer
@@ -151,8 +151,8 @@ flowchart TB
- 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.
- The sibling native observer owns the native build attempt. Loader remains owner
of terminal polling/finalize, permits, materialization and SceneTree mutation.
## Errors, cancellation and recovery
@@ -206,7 +206,7 @@ behind a dedicated adapter while retaining the pipeline state.
| 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 |
| Native animation observation | Implemented sibling extraction | Native observer lifecycle/source verifier | Asset-backed traversal pending |
| Animated finalize/preparation | Loader/finalizer split | Pipeline/finalizer regressions | Polling adapter optional |
## Known gaps and risks
@@ -225,12 +225,14 @@ behind a dedicated adapter while retaining the pipeline state.
| `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/render/m2/m2_native_animation_resource_observer.gd` | Native-first candidate/read/build/cache observation |
| `src/scenes/streaming/streaming_world_loader.gd` | Composition, fallback order, 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-native-animation-resource-observer.md`](m2-native-animation-resource-observer.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)
@@ -0,0 +1,245 @@
# M2 Native Animation Resource Observer
## Metadata
| Field | Value |
|---|---|
| Status | Implemented extraction |
| Target/work package | M03 / `M03-RND-M2-NATIVE-ANIMATION-RESOURCE-OBSERVER-001` |
| Owners | Native animation candidate policy, raw read, build and cache outcome |
| Last verified | Worktree `work/sindo-main-codex/m03-m2-native-animation-resource-observer`, 2026-07-18 |
| Profiles/capabilities | Existing native GryphonRoost animated M2 path |
## Purpose
Resolve the historical native animated M2 path as one synchronous observation:
select a GryphonRoost candidate, reuse cached state, read animated raw data,
build a detached prototype and publish either the prototype or static-only result.
## Non-goals
- Add native animation candidates or change the `gryphonroost` predicate.
- Observe/request cached GLB animation or poll/finalize ResourceLoader work.
- Duplicate, attach, animate or free accepted instances in the SceneTree.
- Consume render permits or change build queue, batching and dispatch policy.
- Change native parsing/building, cache formats, profiles or visible output.
## Context and boundaries
```mermaid
flowchart LR
Loader[StreamingWorldLoader] --> Observer[M2NativeAnimationResourceObserver]
Observer --> Repository[M2RawModelRepository]
Observer --> Builder[M2NativeAnimatedBuilder]
Observer --> Cache[M2PrototypeCacheState]
Observer --> Prototype[Detached animated prototype]
Prototype --> Snapshot[M2BuildResourceSnapshot]
Observer -->|no prototype| Cached[M2CachedAnimationResourceObserver]
```
The observer is the native-first resource boundary. The loader supplies an
already-normalized path and remains responsible for fallback ordering and for
constructing the snapshot consumed by dispatch.
## Public API
| Symbol | Kind | Purpose | Failure behavior |
|---|---|---|---|
| `is_native_animation_candidate(path)` | Pure query | Apply the historical case-insensitive GryphonRoost substring policy | Empty/unmatched path returns false |
| `observe(path, extracted_dir, repository, cache, debug)` | Command/query | Return an exact cached/new native prototype or record static-only fallback | Invalid/non-candidate input returns null without mutation |
| `build_animated_prototype(data, extracted_dir)` | Adapter command | Call the retained native animated builder dependency | Missing builder or invalid result returns null |
The optional constructor dependency exists for deterministic tests; production
uses `M2NativeAnimatedBuilder` without a new abstraction or runtime setting.
## Inputs and outputs
| Direction | Data | Producer | Consumer | Ownership/lifetime |
|---|---|---|---|---|
| Input | Normalized relative M2 path | Loader path adapter | Observer | Copied String; one call |
| Input | Extracted directory | Loader configuration | Repository/builder | Copied String; one call |
| Input | Raw animated Dictionary | Repository | Observer/builder | Call-local value |
| Input | Prototype cache state | Loader composition | Observer | Borrowed service; loader session |
| Output | Cached or adopted prototype | Cache/observer | Loader snapshot | Borrowed exact Node3D reference |
| Output | Static-only transition | Observer | Prototype cache | Copied path; loader session |
| Output | Debug line | Observer | Runtime log | Emitted only when enabled and build succeeds |
## Data flow
```mermaid
flowchart TD
Start[Observe normalized path] --> Candidate{Valid native candidate?}
Candidate -->|no| None[Return null]
Candidate -->|yes| Cached{Cached prototype?}
Cached -->|yes| Return[Return exact prototype]
Cached -->|no| Static{Marked static-only?}
Static -->|yes| None
Static -->|no| Read[Read animated raw data]
Read --> Surfaces{Data and animated surfaces?}
Surfaces -->|no| Mark[Mark static-only]
Surfaces -->|yes| Build[Build detached prototype]
Build --> Children{Prototype has children?}
Children -->|no| Mark
Children -->|yes| Adopt[Adopt first cache prototype]
Adopt --> Log{Debug enabled?}
Log -->|yes| Emit[Emit exact native cache fields]
Log -->|no| Return
Emit --> Return
Mark --> None
```
## Lifecycle/state
```mermaid
stateDiagram-v2
[*] --> Observing
Observing --> Rejected: invalid or non-candidate
Observing --> Cached: positive cache hit
Observing --> StaticOnly: negative cache hit
Observing --> Reading: uncached candidate
Reading --> StaticOnly: no animated surfaces
Reading --> Building: usable raw data
Building --> StaticOnly: null or childless result
Building --> Adopted: valid result
Adopted --> [*]
Cached --> [*]
StaticOnly --> [*]
Rejected --> [*]
```
## Main sequence
```mermaid
sequenceDiagram
participant L as StreamingWorldLoader
participant O as NativeAnimationObserver
participant C as M2PrototypeCacheState
participant R as M2RawModelRepository
participant B as M2NativeAnimatedBuilder
L->>O: observe(path, directory, repository, cache, debug)
O->>C: find animated / is static-only
alt uncached native candidate
O->>R: load_animated_model_data(directory, path)
R-->>O: raw Dictionary
O->>B: build(raw, directory)
alt valid prototype
O->>C: adopt_animated_prototype(path, prototype)
O-->>L: canonical prototype
else unavailable
O->>C: mark_animation_static(path)
O-->>L: null
end
else cached result
O-->>L: prototype or null
end
```
## Dependency diagram
```mermaid
flowchart TB
Observer[M2NativeAnimationResourceObserver] --> Repository[M2RawModelRepository]
Observer --> Builder[M2NativeAnimatedBuilder]
Observer --> Cache[M2PrototypeCacheState]
Loader[StreamingWorldLoader] --> Observer
Observer -. no dependency .-> ResourceLoader
Observer -. no dependency .-> Pipeline[M2AnimationLoadPipelineState]
Observer -. no dependency .-> Scheduler[RenderBudgetScheduler]
Observer -. no dependency .-> SceneTree
```
## Ownership, threading and resources
- Observation and native parsing/building remain synchronous on the renderer
main thread, exactly as before extraction; there is no mid-call cancellation.
- The observer retains no path, raw Dictionary, Node or service reference.
- Prototype cache owns an accepted detached Node until final shutdown; callers
only borrow the canonical reference.
- The historical childless-builder rejection does not free its detached Node.
This deliberately preserved lifetime edge is documented as a remaining leak
risk rather than silently changed during architectural extraction.
- Loader owns snapshots, fallback sequencing, permits, materialization and every
SceneTree mutation.
## Errors, cancellation and recovery
| Failure/state | Behavior | Recovery |
|---|---|---|
| Empty/non-candidate path or missing dependency | Return null without state mutation | Correct composition/path |
| Cached prototype | Return exact canonical Node immediately | None |
| Existing static-only outcome | Return null without repeated I/O | New loader session after source repair |
| Empty raw data or animated surfaces | Mark static-only and return null | Repair source/parser and start new session |
| Null/childless build | Mark static-only and return null | Repair builder/data and start new session |
| Duplicate successful build | Cache releases later candidate and returns first | None |
| Cancellation during native call | Not supported; call completes | Loader controls whether the call starts |
| Shutdown | Observer retains nothing | Cache releases adopted Nodes after drains |
## Configuration and capabilities
The observer consumes the existing extracted directory and `debug_streaming`
flag. Candidate policy remains the case-insensitive substring `gryphonroost`.
No profile, cache, budget or animation setting is added.
## Persistence, cache and migration
No persistence or format changes are introduced. Positive and static-only state
continues to use the shutdown-lifetime prototype cache; no asset rebake is needed.
## Diagnostics and observability
A successful debug-enabled build emits the unchanged `M2_NATIVE_ANIM_CACHE`
record with path, surface count, bone count, animation id, sequence index,
activity score and length. Rejections remain silent as before.
## Verification
- Dedicated synthetic verifier covers candidate policy, invalid dependencies,
cache identity, static suppression, raw/surface/build failures, preserved
childless lifetime, exact repository/builder arguments, adoption, source
ownership and bounded candidate checks.
- Cached observer, raw repository, prototype cache, snapshot, dispatch, shutdown,
facade, internal-access and render-baseline checks protect adjacent behavior.
- Fidelity evidence is exact control-flow and diagnostic extraction. It is not
original-client animation, visual, memory or asset-backed performance evidence.
## Extension points
Terminal cached-GLB polling/finalization can move behind a separate adapter
without changing this native-first contract. Candidate expansion requires its
own fidelity evidence and must not silently change `Blizzlike335` behavior.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Native candidate selection | Implemented extraction | Policy/source/timing verifier | Additional candidates require fidelity work |
| Raw read/build/cache outcome | Implemented extraction | Failure/identity/adoption verifier | Asset-backed traversal pending |
| Terminal cached ResourceLoader polling | Loader-owned | Existing pipeline regressions | Dedicated finalize adapter |
## Known gaps and risks
- Native parsing/building is synchronous and not bounded by a render permit.
- Rejected childless builder Nodes retain the historical unowned lifetime edge.
- Private asset traversal, descriptor/leak pressure, p95/p99 and paired visual/
animation evidence remain unavailable in this package.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/m2/m2_native_animation_resource_observer.gd` | Candidate/cache/raw/build/outcome/logging observation |
| `src/render/m2/m2_raw_model_repository.gd` | Optional native file/parser boundary |
| `src/render/m2/m2_prototype_cache_state.gd` | Positive prototype and static-only ownership |
| `addons/mpq_extractor/loaders/m2_native_animated_builder.gd` | Existing animated prototype construction |
| `src/scenes/streaming/streaming_world_loader.gd` | Composition, native-first fallback order and execution |
| `src/tools/verify_m2_native_animation_resource_observer.gd` | Lifecycle/identity/boundary/timing regression |
## Related decisions and references
- [`m2-cached-animation-resource-observer.md`](m2-cached-animation-resource-observer.md)
- [`m2-build-resource-snapshot.md`](m2-build-resource-snapshot.md)
- [`m2-raw-model-repository.md`](m2-raw-model-repository.md)
- [`m2-prototype-cache-state.md`](m2-prototype-cache-state.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)
+6 -5
View File
@@ -142,7 +142,7 @@ flowchart TB
Loader --> Raw[M2RawModelRepository]
Observer --> ResourceLoader
Loader --> StaticBuilder[M2Builder]
Loader --> AnimatedBuilder[M2NativeAnimatedBuilder]
Observer --> AnimatedBuilder[M2NativeAnimatedBuilder]
State -. no dependency .-> Raw
State -. no dependency .-> ResourceLoader
State -. no dependency .-> StaticBuilder
@@ -192,7 +192,7 @@ and native M2 formats are unchanged; no migration or rebake is introduced.
- `diagnostic_snapshot` exposes four sorted path arrays without Node references.
- Cached eligibility rejection logging belongs to the cached observer; native
animation logs remain loader-owned.
animation success logging belongs to the native observer.
- Existing renderer queue metrics remain unchanged because these tables never
contributed work counts.
- Normalized relative path remains the correlation key.
@@ -212,8 +212,8 @@ and native M2 formats are unchanged; no migration or rebake is introduced.
## Extension points
Eviction or byte/count budgets require measured memory evidence and explicit
prototype-user lifetime rules. Cached animation observation consumes this
service without moving ResourceLoader or builder ownership into prototype state.
prototype-user lifetime rules. Cached and native animation observers consume
this state without moving ResourceLoader or builder ownership into cache state.
## Capability status
@@ -237,7 +237,8 @@ service without moving ResourceLoader or builder ownership into prototype state.
| Path | Responsibility |
|---|---|
| `src/render/m2/m2_prototype_cache_state.gd` | Positive Node ownership, negative path state and shutdown release |
| `src/scenes/streaming/streaming_world_loader.gd` | Normalization, I/O/build/fallback decisions and cache adapters |
| `src/render/m2/m2_native_animation_resource_observer.gd` | Animated raw/build and positive/static-only transitions |
| `src/scenes/streaming/streaming_world_loader.gd` | Normalization, observer order, remaining I/O/fallback adapters |
| `src/tools/verify_m2_prototype_cache_state.gd` | Admission/identity/lifecycle/source/timing regression |
| `src/tools/verify_render_runtime_cache_shutdown.gd` | Integrated final-shutdown release regression |
+12 -7
View File
@@ -28,10 +28,12 @@ static or animated Dictionary consumed by existing builders and classifiers.
```mermaid
flowchart LR
Loader[StreamingWorldLoader] --> Repository[M2RawModelRepository]
Observer[M2NativeAnimationResourceObserver] --> Repository
Repository --> File[Extracted M2 file]
Repository --> Native[ClassDB M2Loader]
Native --> Raw[Raw Dictionary]
Raw --> Loader
Raw --> Observer
Loader --> Builder[Existing M2 builders/finalizer]
```
@@ -54,7 +56,7 @@ renderer policy and other application layers are forbidden.
| Input | Already-normalized relative M2 path String | Loader normalization | Repository | Copied value | One call |
| Input | Extracted `.m2` bytes | Local legal extraction | Native M2Loader | File-owned | Native call |
| Output | Static raw M2 Dictionary | Native `load_m2` | Loader/finalizer/M2Builder | Fresh native result | One caller operation |
| Output | Animated raw M2 Dictionary | Native `load_m2_animated` | Loader/animated builder | Fresh native result | One caller operation |
| Output | Animated raw M2 Dictionary | Native `load_m2_animated` | Native animation observer/builder | Fresh native result | One caller operation |
| Output | Empty Dictionary | Repository guards | Loader fallback and prototype outcome adapter | Fresh value | One failed call |
Side effects are limited to file-existence inspection, synchronous native file
@@ -98,18 +100,18 @@ No request, result or failure state survives a call.
```mermaid
sequenceDiagram
participant Loader as StreamingWorldLoader
participant Caller as Loader or NativeAnimationObserver
participant Repo as M2RawModelRepository
participant File as FileAccess
participant Native as M2Loader
Loader->>Repo: load static/animated(directory, path)
Caller->>Repo: load static/animated(directory, path)
Repo->>File: file_exists(globalized joined path)
alt dependency or file unavailable
Repo-->>Loader: empty Dictionary
Repo-->>Caller: empty Dictionary
else available
Repo->>Native: instantiate and call exact native method
Native-->>Repo: Variant
Repo-->>Loader: Dictionary or empty Dictionary
Repo-->>Caller: Dictionary or empty Dictionary
end
```
@@ -118,13 +120,14 @@ sequenceDiagram
```mermaid
flowchart TB
Loader[StreamingWorldLoader] --> Repository[M2RawModelRepository]
Observer[M2NativeAnimationResourceObserver] --> Repository
Repository --> ProjectSettings
Repository --> FileAccess
Repository --> ClassDB
ClassDB --> Native[M2Loader extension]
Loader --> Finalizer[M2RuntimeMeshFinalizer]
Loader --> StaticBuilder[M2Builder]
Loader --> AnimatedBuilder[M2NativeAnimatedBuilder]
Observer --> AnimatedBuilder[M2NativeAnimatedBuilder]
Repository -. no dependency .-> Finalizer
Repository -. no dependency .-> StaticBuilder
Repository -. no dependency .-> Cache[Renderer caches/queues]
@@ -133,7 +136,8 @@ flowchart TB
## Ownership, threading and resources
- The repository owns only call-local path, native instance and result values.
- The loader owns path normalization and fallback selection;
- The loader owns path normalization and fallback selection; the native observer
owns the animated raw-read/build decision;
`M2PrototypeCacheState` owns prototype/negative adoption.
- Native `M2Loader` owns parsing behavior and returns a new Dictionary value.
- Calls are synchronous on the caller's thread; current renderer callers use the
@@ -211,6 +215,7 @@ measured work packages rather than expansion of this repository.
| Path | Responsibility |
|---|---|
| `src/render/m2/m2_raw_model_repository.gd` | Stateless native class/file/method boundary |
| `src/render/m2/m2_native_animation_resource_observer.gd` | Animated raw-data consumer and builder adapter |
| `src/render/m2/m2_prototype_cache_state.gd` | Prototype and negative-result retention |
| `src/scenes/streaming/streaming_world_loader.gd` | Path normalization, fallback decisions and result consumers |
| `src/native/src/m2_loader.cpp` | Native static/animated parsing implementation |
+12 -6
View File
@@ -157,6 +157,7 @@ from externally reading/writing loader-private queue, task, cache and tile-state
| `M2RuntimeMeshFinalizer` | Internal M2 preparation service | Owns refresh version, rebuild classification, M2Builder rebuild and fallback | Renderer main thread; decisions cached until reset | Null returns null; missing/failed rebuild marks and reuses original Mesh |
| `M2RawModelRepository` | Internal M2 native repository | Reads static/animated raw Dictionaries through exact M2Loader methods | Synchronous; stateless | Invalid/unavailable/non-Dictionary result returns empty Dictionary |
| `M2PrototypeCacheState` | Internal M2 prototype cache | Owns detached static/animated Nodes and missing/static-only outcomes | Renderer main thread; final shutdown | Invalid admission rejected; first valid prototype wins |
| `M2NativeAnimationResourceObserver` | Internal native M2 resource observer | Selects GryphonRoost, reads/builds and publishes prototype/static-only outcome | Synchronous renderer main thread; stateless | Invalid/unavailable candidates return null; failures mark static-only |
| `WmoPlacementResolver.normalize_relative_path/resolve_unique_key/resolve_world_transform` | Internal pure WMO service | Resolves cache key, registry identity and world transform | Main/any thread; stateless | Missing UID uses tile/index fallback; transform fields use historical defaults |
| `WmoPlacementRegistry.add_reference/release_reference/contains/active_count/diagnostic_snapshot/clear` | Internal WMO service | Owns placement-key to tile/global reference sets | Renderer main thread; map session | Empty/unknown/non-owner input is rejected without mutation |
| `WmoRenderBuildStepPlanner.plan_step` | Internal pure WMO service | Selects one mesh-first lightweight render-group operation and next cursors | Main/any thread; stateless | Raw integer comparisons are preserved without clamping |
@@ -204,7 +205,7 @@ loader configuration remains transitional composition data, not a caller API.
| Internal WMO render cache | Normalized path, cache path and validated Resource | Loader / `WmoRenderResourceCacheState` | Loader lookup, ResourceLoader poll and build queue | State-owned Resource/path references; detached request snapshots | Until transient/full clear |
| Internal WMO scene cache | Normalized path, `.tscn` path and validated PackedScene | Loader / `WmoSceneResourceCacheState` | Loader lookup, request poll and scene instantiation | State-owned PackedScene/path references; detached request snapshots | Until transient/full clear |
| Internal ADT water load | Tile key, ADT path, task ID and parsed Dictionary | Loader/worker / `AdtWaterLoadPipelineState` | Loader task start, budgeted drain and finalization | State-owned records; mutex result mailbox | Request through result completion/reset |
| Internal raw M2 read | Extracted directory and normalized relative path | Loader / `M2RawModelRepository` | Finalizer, static or animated builder adapters | Fresh Dictionary; repository retains nothing | One synchronous native call |
| Internal raw M2 read | Extracted directory and normalized relative path | Loader/native observer via `M2RawModelRepository` | Finalizer, static or animated builder adapters | Fresh Dictionary; repository retains nothing | One synchronous native call |
| Internal animated M2 load | Normalized path, cached GLB path and opaque terminal status | Cached observer / loader / `M2AnimationLoadPipelineState` | Loader poll/finalize adapters | State-owned String/status records; detached pending snapshots | Request through terminal finalize/reset |
| Internal animated M2 candidate | Loaded PackedScene, static material root and detached candidate | Loader / `M2AnimatedSceneFinalizer` | Loader prototype adoption/static fallback | Finalizer owns until rejection or exact-root transfer | One main-thread finalize permit |
| Internal animated M2 playback | Prototype/instance native animators, path/index and player inventory | Materializer/finalizer / `M2AnimationPlaybackController` | Native and imported animation state | Borrowed Nodes; detached optional diagnostics | One duplicated instance startup |
@@ -398,9 +399,11 @@ sequenceDiagram
`M2StaticBuildResourceObserver` owns static Mesh lookup/request admission and
snapshot adoption. `M2CachedAnimationResourceObserver` owns cached animated
prototype lookup, GLB policy/request admission and snapshot production.
`M2NativeAnimationResourceObserver` owns native candidate selection, raw read,
detached build and prototype/static-only cache outcome.
`M2StaticBatchMaterializer` owns static MultiMesh construction/attachment; the
loader retains native animation observation, action execution, cursor adoption,
root cleanup, budgets and Editor ownership.
loader retains action execution, cursor adoption, root cleanup, budgets and
Editor ownership.
- `WmoPlacementResolver` is stateless and owns only call-local cache-key,
identity and transform values. `WmoPlacementRegistry` owns only placement-key
reference sets. `WmoRenderBuildStepPlanner` owns only a call-local operation
@@ -428,8 +431,8 @@ sequenceDiagram
- `M2AnimationLoadPipelineState` owns animated M2 pending Resource paths, opaque
terminal statuses and completion-order finalize FIFO. The cached animation
observer owns allow/deny/path/GLB selection, request admission and initial
snapshot production. The loader retains native animation build, ResourceLoader
polling/finalize, permits and terminal adoption.
snapshot production. The native observer owns synchronous native build; the
loader retains ResourceLoader polling/finalize, permits and terminal adoption.
- `M2MeshResourceCacheState` owns prepared static Mesh references and releases
them at the existing final-shutdown site. Prototype state and materialization
belong to the sibling cache service and loader respectively.
@@ -592,7 +595,8 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
| M2 build dispatch planner | Implemented extraction | Scene-free priority/action/transition/source/timing contract | Resource observation/orchestration and asset-backed traversal remain pending |
| M2 build resource snapshot | Implemented extraction | Typed identity/adoption/lifetime/source/timing contract | Resource observation service and asset-backed traversal remain pending |
| M2 static build resource observer | Implemented extraction | Cache/pending/path/request/source/timing contract | Asset-backed traversal and finalize extraction pending |
| M2 cached animation resource observer | Implemented extraction | Cache/pending/policy/GLB/request/source/timing contract | Native observation and asset-backed traversal pending |
| M2 cached animation resource observer | Implemented extraction | Cache/pending/policy/GLB/request/source/timing contract | Asset-backed traversal and terminal polling extraction pending |
| M2 native animation resource observer | Implemented extraction | Candidate/cache/raw/build/adoption/source/timing contract | Asset-backed traversal/leak/p95/p99 pending |
| M2 build queue | Implemented extraction | Typed lifecycle/FIFO/progress/lifetime/source/timing contract | Resource dispatch and asset-backed traversal remain pending |
| M2 animation load pipeline state | Implemented extraction | Synthetic lifecycle/FIFO/source/timing contract | Asset-backed traversal/leak/animation-fidelity/p95/p99 pending |
| M2 animated scene finalizer | Implemented extraction | Synthetic type/lifetime/material/player/source/timing contract | Asset-backed GLB traversal/material/animation comparison pending |
@@ -675,6 +679,7 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
| `src/render/m2/m2_mesh_resource_extractor.gd` | First-Mesh selection and temporary PackedScene instance lifetime |
| `src/render/m2/m2_runtime_mesh_finalizer.gd` | Runtime refresh version, rebuild classification/build and fallback |
| `src/render/m2/m2_raw_model_repository.gd` | Stateless static/animated native M2 file boundary |
| `src/render/m2/m2_native_animation_resource_observer.gd` | Native candidate/read/build/cache observation |
| `src/render/m2/m2_prototype_cache_state.gd` | Detached prototype ownership and negative lookup outcomes |
| `src/render/streaming/streaming_target_planner.gd` | Scene-free wanted/retained ADT target calculation |
| `src/render/streaming/streaming_target_policy.gd` | Immutable renderer radius/prefetch policy |
@@ -706,6 +711,7 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
| `src/tools/verify_m2_mesh_resource_extractor.gd` | M2 direct/PackedScene/subtree order/lifetime/boundary/timing regression |
| `src/tools/verify_m2_runtime_mesh_finalizer.gd` | M2 current/stale/rebuild/fallback/boundary/timing regression |
| `src/tools/verify_m2_raw_model_repository.gd` | M2 invalid/missing/native-boundary/dependency/timing regression |
| `src/tools/verify_m2_native_animation_resource_observer.gd` | Native candidate/cache/raw/build/adoption/boundary/timing regression |
| `src/tools/verify_m2_prototype_cache_state.gd` | M2 prototype identity/negative/lifecycle/boundary/timing regression |
| `src/tools/verify_streaming_target_planner.gd` | Planner behavior, dependency and bounded timing regression |
| `src/tools/verify_render_budget_scheduler.gd` | Scheduler bounds, shared-lane priority, cancellation and timing regression |