238 lines
11 KiB
Markdown
238 lines
11 KiB
Markdown
# M2 Build Dispatch Planner
|
|
|
|
## Metadata
|
|
|
|
| Field | Value |
|
|
|---|---|
|
|
| Status | Implemented extraction |
|
|
| Target/work package | M03 / `M03-RND-M2-BUILD-DISPATCH-PLANNER-001` |
|
|
| Owners | Pure M2 resource-state to build-action decision |
|
|
| Last verified | Worktree `work/sindo-main-codex/m03-m2-build-dispatch-planner`, 2026-07-18 |
|
|
| Profiles/capabilities | Existing static MultiMesh and animated-instance build paths |
|
|
|
|
## Purpose
|
|
|
|
Select one M2 build action after renderer observers produce animation/static
|
|
resource state. The planner makes wait priority, materializer selection and the
|
|
historical missing-model serial transition explicit without owning resources or
|
|
engine work.
|
|
|
|
## Non-goals
|
|
|
|
- Locate, request, load, cache or validate M2 resources.
|
|
- Size batches, own build jobs/queues or consume renderer permits.
|
|
- Create, attach or free Nodes, Meshes, MultiMeshes or animated instances.
|
|
- Change transforms, ordering, render settings, profiles or visible output.
|
|
- Add spatial-cell batching or M2 format/fidelity behavior.
|
|
|
|
## Context and boundaries
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
Loader[StreamingWorldLoader resource observations] --> Planner[M2BuildDispatchPlanner]
|
|
Planner --> Action[Detached action and transition plan]
|
|
Action --> Loader
|
|
Loader --> Queue[M2BuildQueue rotate/progress]
|
|
Loader --> Animated[M2AnimatedInstanceMaterializer]
|
|
Loader --> Static[M2StaticBatchMaterializer]
|
|
Loader --> Budget[RenderBudgetScheduler]
|
|
```
|
|
|
|
The planner depends only on `RefCounted`, scalar values, `StringName` constants
|
|
and a fresh Dictionary. ResourceLoader, caches, Nodes, Meshes, RenderingServer,
|
|
SceneTree, files, workers, mutexes, gameplay, network and Editor APIs are forbidden.
|
|
|
|
## Public API
|
|
|
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
|
|---|---|---|---|---|
|
|
| `ACTION_WAIT_FOR_ANIMATION` | Constant | Pending animation request blocks all other actions | Immutable | None |
|
|
| `ACTION_MATERIALIZE_ANIMATED` | Constant | Build the selected slice from animated prototype | Immutable | None |
|
|
| `ACTION_WAIT_FOR_STATIC_MESH` | Constant | Static resource is unresolved and queue must rotate | Immutable | None |
|
|
| `ACTION_MATERIALIZE_STATIC` | Constant | Build the selected slice from prepared static Mesh | Immutable | None |
|
|
| `ACTION_ADVANCE_WITHOUT_MATERIALIZATION` | Constant | Empty or terminally missing slice advances without a Node | Immutable | None |
|
|
| `plan_step(batch_count, resource_snapshot)` | Pure query | Return action, queue-rotation and serial-transition values | Any thread; call-local result | Null snapshot waits for static Mesh; non-positive count advances |
|
|
|
|
## Inputs and outputs
|
|
|
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
|
|---|---|---|---|---|---|
|
|
| Input | Planned batch count | `M2BuildBatchPlanner` through loader | Dispatch planner | Integer copy | One call |
|
|
| Input | Typed animated/static availability, pending and missing observations | Loader / `M2BuildResourceSnapshot` | Dispatch planner | Borrowed snapshot; engine references are not read | One call |
|
|
| Output | Action | Dispatch planner | Loader materializer/wait branch | Immutable StringName | One operation |
|
|
| Output | `rotate_queue` | Dispatch planner | Loader queue adapter | Detached boolean | One operation |
|
|
| Output | `increment_batch_serial` | Dispatch planner | Loader progress adapter | Detached boolean | One operation |
|
|
|
|
The output Dictionary is newly allocated and caller-owned. The planner retains
|
|
no state, resource or engine-object reference.
|
|
|
|
## Data flow
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
Input[Observed state] --> AnimationPending{Animation request pending?}
|
|
AnimationPending -->|yes| WaitAnimation[Wait animation; rotate]
|
|
AnimationPending -->|no| Positive{Batch count positive?}
|
|
Positive -->|no| AdvanceEmpty[Advance; keep serial]
|
|
Positive -->|yes| Animated{Animated prototype?}
|
|
Animated -->|yes| BuildAnimated[Materialize animated; increment serial]
|
|
Animated -->|no| StaticReady{Static Mesh ready?}
|
|
StaticReady -->|yes| BuildStatic[Materialize static; increment serial]
|
|
StaticReady -->|no| Missing{Model terminally missing?}
|
|
Missing -->|yes| AdvanceMissing[Advance without Node; increment serial]
|
|
Missing -->|no| WaitStatic[Wait static Mesh; rotate]
|
|
```
|
|
|
|
## Lifecycle/state
|
|
|
|
```mermaid
|
|
stateDiagram-v2
|
|
[*] --> Observe
|
|
Observe --> Waiting: animation/static unresolved
|
|
Observe --> Materializing: animated/static resource ready
|
|
Observe --> Advancing: empty or terminally missing batch
|
|
Waiting --> [*]: detached wait plan
|
|
Materializing --> [*]: detached materialization plan
|
|
Advancing --> [*]: detached advance plan
|
|
```
|
|
|
|
The planner itself is stateless. Queue rotation, cursor adoption, cancellation,
|
|
map reset and shutdown remain external lifecycle transitions.
|
|
|
|
## Main sequence
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant L as StreamingWorldLoader
|
|
participant B as M2BuildBatchPlanner
|
|
participant D as M2BuildDispatchPlanner
|
|
participant Q as M2BuildQueue
|
|
participant M as M2 materializer
|
|
participant S as RenderBudgetScheduler
|
|
L->>L: observe animation request/prototype
|
|
alt animation not pending
|
|
L->>B: plan_batch
|
|
L->>L: lookup/request static Mesh when required
|
|
end
|
|
L->>D: plan_step(batch count, resource snapshot)
|
|
D-->>L: action, rotate flag, serial flag
|
|
alt wait action
|
|
L->>Q: rotate_front
|
|
else materialize action
|
|
L->>M: materialize selected slice
|
|
L->>Q: adopt progress and serial
|
|
else advance action
|
|
L->>Q: adopt progress and optional serial
|
|
end
|
|
L->>S: consume one M2_BUILD permit
|
|
```
|
|
|
|
## Dependency diagram
|
|
|
|
```mermaid
|
|
flowchart TB
|
|
Loader[StreamingWorldLoader] --> Dispatch[M2BuildDispatchPlanner]
|
|
Loader --> Batch[M2BuildBatchPlanner]
|
|
Loader --> Queue[M2BuildQueue]
|
|
Loader --> Caches[M2 cache/pipeline state]
|
|
Loader --> Materializers[M2 materializers]
|
|
Dispatch --> Snapshot[M2BuildResourceSnapshot accessors]
|
|
Dispatch --> Values[Integers + booleans + StringName]
|
|
Dispatch -. no dependency .-> Engine[Node / Mesh / ResourceLoader]
|
|
Dispatch -. no dependency .-> State[Queue / cache / scheduler state]
|
|
```
|
|
|
|
## Ownership, threading and resources
|
|
|
|
- Planner owns call-local comparisons and the returned Dictionary only.
|
|
- `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;
|
|
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.
|
|
|
|
## Errors, cancellation and recovery
|
|
|
|
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|
|
|---|---|---|---|---|
|
|
| Animation request pending | Boolean observation | Wait wins even if other flags are true | Priority fixture | Retry when queue returns front |
|
|
| Non-positive batch | Count comparison | Advance without serial increment | Zero/negative fixtures | Next group/cursor operation |
|
|
| Static Mesh unresolved | No Mesh and not missing | Rotate without serial increment | Retry fixture | Loader request/cache completes |
|
|
| Static model missing | Terminal missing flag | Advance without Node and increment serial | Missing fixture | Later group continues |
|
|
| Contradictory static ready/missing | Both true | Ready Mesh wins | Priority fixture | Loader normally prevents contradiction |
|
|
| Tile cancellation/root invalid | Outside planner | Loader cancels before dispatch | Queue/shutdown regressions | Eligible tile may requeue |
|
|
|
|
## Configuration and capabilities
|
|
|
|
The planner adds no setting. Existing animated/static batch limits, animation
|
|
enable flag, visibility/shadow settings and `M2_BUILD` permits remain external.
|
|
|
|
## Persistence, cache and migration
|
|
|
|
No state is serialized. No ADT/M2 cache format or version changes; no rebake or
|
|
migration is required.
|
|
|
|
## Diagnostics and observability
|
|
|
|
The planner emits no logs. Its verifier reports the action matrix, source
|
|
boundaries and bounded 20,000-call timing. Existing loader queue/hitch metrics
|
|
remain unchanged.
|
|
|
|
## Verification
|
|
|
|
- `verify_m2_build_dispatch_planner.gd` covers wait priority, zero/negative
|
|
counts, animated/static priority, unresolved retry, terminal missing, detached
|
|
results, loader/dependency boundaries and bounded timing.
|
|
- Queue, batch planner, materializers, caches, shutdown, facade, internal-access
|
|
and checkpoint tests protect adjacent behavior.
|
|
- Fidelity evidence is exact branch/transition extraction only. No private asset
|
|
or original-client visual comparison is claimed.
|
|
|
|
## Extension points
|
|
|
|
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
|
|
|
|
| Capability | Status | Evidence | Gap/next step |
|
|
|---|---|---|---|
|
|
| M2 build action selection | Implemented extraction | Priority/matrix/source/timing verifier | Asset-backed traversal pending |
|
|
| 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
|
|
|
|
- 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.
|
|
|
|
## Source map
|
|
|
|
| Path | Responsibility |
|
|
|---|---|
|
|
| `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/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 |
|
|
|
|
## Related decisions and references
|
|
|
|
- [`m2-build-batch-planner.md`](m2-build-batch-planner.md)
|
|
- [`m2-build-resource-snapshot.md`](m2-build-resource-snapshot.md)
|
|
- [`m2-build-queue.md`](m2-build-queue.md)
|
|
- [`m2-static-batch-materializer.md`](m2-static-batch-materializer.md)
|
|
- [`m2-animated-instance-materializer.md`](m2-animated-instance-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)
|