188 lines
8.7 KiB
Markdown
188 lines
8.7 KiB
Markdown
# M2 Build Batch Planner
|
|
|
|
## Metadata
|
|
|
|
| Field | Value |
|
|
|---|---|
|
|
| Status | Implemented |
|
|
| Target/work package | M03 / `M03-RND-M2-BUILD-BATCH-PLANNER-001` |
|
|
| Owners | Pure static/animated M2 batch sizing and group cursor progression |
|
|
| Last verified | Worktree `work/sindo-main-codex/m03-m2-build-batch-planner`, 2026-07-17 |
|
|
| Profiles/capabilities | Existing static MultiMesh and animated-instance build paths |
|
|
|
|
## Purpose
|
|
|
|
Select the effective per-tick M2 batch limit, cap it to remaining transforms and
|
|
plan the next group offset without accessing loader state or renderer resources.
|
|
|
|
## Non-goals
|
|
|
|
- Own M2 build jobs, queues, tile state, retries or cancellation.
|
|
- Decide whether an animated prototype or static mesh is ready.
|
|
- Rotate queues or consume `RenderBudgetScheduler` permits.
|
|
- Create or modify Nodes, MultiMesh, Mesh, materials or RIDs.
|
|
- Introduce spatial cells or change profile batch limits.
|
|
|
|
## Context and boundaries
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
Groups[M2PlacementGrouper transform arrays] --> Loader[Loader build loop]
|
|
Limits[Animated/static batch limits] --> Planner[M2BuildBatchPlanner]
|
|
Loader --> Planner
|
|
Planner --> Plan[Detached batch plan]
|
|
Plan --> Loader
|
|
Loader --> Ready{Resource ready?}
|
|
Ready --> Materialize[Animated or MultiMesh materialization]
|
|
Loader --> Budget[RenderBudgetScheduler permit]
|
|
```
|
|
|
|
Allowed dependencies are integer/boolean scalar math and a fresh Dictionary.
|
|
Node, SceneTree, RenderingServer, ResourceLoader, WorkerThreadPool, mutexes,
|
|
renderer caches, gameplay, network, editor UI and files are forbidden.
|
|
|
|
## Public API
|
|
|
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
|
|---|---|---|---|---|
|
|
| `plan_batch(transform_count, current_offset, has_animated_prototype, animated_batch_limit, static_batch_limit)` | Pure query | Return the selected batch size/count and next group cursor | Any thread; call-local result | Non-positive selected limit clamps to one; empty/past-end range completes |
|
|
|
|
## Inputs and outputs
|
|
|
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
|
|---|---|---|---|---|---|
|
|
| Input | Transform count | Grouped transform array | Planner remaining-count formula | Scalar copy | One plan |
|
|
| Input | Current group offset | Loader build job | Planner cursor formula | Scalar copy | One plan |
|
|
| Input | Animated prototype availability | Loader resource selection | Planner limit selector | Scalar copy | One plan |
|
|
| Input | Animated/static raw limits | Renderer profile/exports | Planner clamp | Scalar copy | One plan |
|
|
| Output | `effective_batch_size` | Planner | Tests/diagnostics | Detached integer | One plan |
|
|
| Output | `batch_count` | Planner | Loader materialization adapter | Detached integer | One plan |
|
|
| Output | `next_offset` | Planner | Loader build-job cursor | Detached integer | Until next batch |
|
|
| Output | `group_complete` | Planner | Loader group-index progression | Detached boolean | One plan |
|
|
|
|
The output Dictionary is newly allocated and caller-owned. The planner retains
|
|
no reference to it or any input.
|
|
|
|
## Data flow
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
Select{Animated prototype?} -->|yes| Animated[animated_batch_limit]
|
|
Select -->|no| Static[static_batch_limit]
|
|
Animated --> Clamp[max 1]
|
|
Static --> Clamp
|
|
Clamp --> Remaining[max 0 of transform_count - current_offset]
|
|
Remaining --> Count[min effective limit, remaining]
|
|
Count --> Complete{count <= 0 or offset + count >= total?}
|
|
Complete -->|yes| Done[group_complete true; next_offset 0]
|
|
Complete -->|no| Continue[group_complete false; next_offset offset + count]
|
|
```
|
|
|
|
## Lifecycle/state
|
|
|
|
The planner is stateless. It has no initialization, reset, cancellation,
|
|
recovery or shutdown work. Each call returns a complete detached value plan.
|
|
|
|
## Main sequence
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Loader as StreamingWorldLoader build loop
|
|
participant Planner as M2BuildBatchPlanner
|
|
participant Materializer as Animated/MultiMesh adapter
|
|
participant Scheduler as RenderBudgetScheduler
|
|
Loader->>Planner: plan_batch(count, offset, path kind, limits)
|
|
Planner-->>Loader: batch_count, next_offset, group_complete
|
|
Loader->>Loader: resolve/request prototype or mesh
|
|
Loader->>Materializer: materialize selected transform slice
|
|
Loader->>Loader: adopt cursor/group completion
|
|
Loader->>Scheduler: consume M2_BUILD permit
|
|
```
|
|
|
|
## Ownership, threading and resources
|
|
|
|
- The planner owns only call-local scalar values and the returned Dictionary.
|
|
- The loader owns build-job Dictionaries, queue ordering, tile checks, resource
|
|
readiness/retry, serial numbers and group-index mutation.
|
|
- Materializers own main-thread Node/MultiMesh construction under loader roots.
|
|
- The scheduler owns the frame-local `M2_BUILD` counter.
|
|
- Pure planning is thread-safe, though the current adapter calls it on main thread.
|
|
|
|
## Errors, cancellation and recovery
|
|
|
|
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
|
|---|---|---|---|---|
|
|
| Zero/negative selected limit | Scalar clamp | Effective size becomes one | Contract fixture | Correct profile value later |
|
|
| Empty transform group | Remaining count zero | Count zero; group completes | Contract fixture | Loader advances group |
|
|
| Offset at/past end | Remaining count clamp | Count zero; offset resets | Contract fixture | Loader advances group |
|
|
| Negative offset | No new validation | Historical arithmetic is preserved | Contract fixture | Loader inputs should remain valid |
|
|
| Resource pending/missing | Outside planner | Loader rotates or advances as before | Existing cache/build diagnostics | Resource loader retry/fallback |
|
|
| Tile cancelled | Outside planner | Loader cancels job before planning/materialization | Existing lifecycle gates | Eligible tile may requeue |
|
|
|
|
## Configuration and capabilities
|
|
|
|
The planner introduces no setting. It consumes existing
|
|
`m2_animated_instances_per_tick` and `m2_multimesh_batch_size` values after the
|
|
loader determines whether an animated prototype is available.
|
|
|
|
## Persistence, cache and migration
|
|
|
|
No persistence, schema or cache format participates. ADT/M2 cache versions,
|
|
quality presets and rebuild instructions are unchanged.
|
|
|
|
## Diagnostics and observability
|
|
|
|
The planner emits no logs. Its verifier covers deterministic formula/source
|
|
boundaries and records bounded 20,000-plan timing. Existing loader metrics retain
|
|
queue depth, build activity and hitch observability.
|
|
|
|
## Verification
|
|
|
|
- `verify_m2_build_batch_planner.gd`: static/animated selection, invalid-limit
|
|
clamp, remaining cap, exact completion, empty/past-end and negative-offset
|
|
behavior, loader/dependency source boundaries and bounded timing.
|
|
- Grouper, transform resolver, unique registry/dedupe, facade, internal-access,
|
|
manifest, shutdown, scheduler, streaming, coordinate, documentation and
|
|
coordination regressions remain required.
|
|
- Fidelity evidence is formula extraction only. Asset-backed frame pacing and
|
|
visual output require the renderer checkpoint/performance workflow.
|
|
|
|
## Extension points
|
|
|
|
- A later package may define typed build-job state once queue/resource transitions
|
|
are extracted together with explicit cancellation and recovery.
|
|
- Spatial-cell batching must use measured culling/performance evidence and must
|
|
not silently change this model-path batch cursor.
|
|
|
|
## Capability status
|
|
|
|
| Capability | Status | Evidence | Gap/next step |
|
|
|---|---|---|---|
|
|
| Static/animated batch cursor planning | Implemented extraction | Contract/source/timing verifier | Asset-backed p95/p99 pending |
|
|
| Build queue/resource state machine | Remains in loader | Existing lifecycle regressions | Stateful extraction pending |
|
|
| Spatial-cell batching | Planned | Renderer roadmap | Culling evidence/design pending |
|
|
|
|
## Known gaps and risks
|
|
|
|
- The result remains a Dictionary while loader build jobs are untyped Dictionaries.
|
|
- Negative offsets are preserved rather than rejected to avoid a hidden behavior change.
|
|
- Planning timing does not measure resource loading or materialization.
|
|
- Private asset-backed p95/p99 and visual comparison remain unavailable.
|
|
|
|
## Source map
|
|
|
|
| Path | Responsibility |
|
|
|---|---|
|
|
| `src/render/m2/m2_build_batch_planner.gd` | Pure limit/count/cursor planning |
|
|
| `src/scenes/streaming/streaming_world_loader.gd` | Build job, queue, resources, materialization and budgets |
|
|
| `src/render/m2/m2_placement_grouper.gd` | Produces grouped transform arrays |
|
|
| `src/tools/verify_m2_build_batch_planner.gd` | Formula, source and timing regression |
|
|
|
|
## Related decisions and references
|
|
|
|
- [`m2-placement-grouper.md`](m2-placement-grouper.md)
|
|
- [`m2-placement-transform-resolver.md`](m2-placement-transform-resolver.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)
|