Files
open-wc/docs/modules/m2-build-batch-planner.md
T

8.7 KiB

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

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

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

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