refactor(M03): extract M2 build batch planner
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
| M2 unique placement registry | Implemented extraction | [`m2-unique-placement-registry.md`](m2-unique-placement-registry.md) |
|
||||
| M2 placement transform resolver | Implemented | [`m2-placement-transform-resolver.md`](m2-placement-transform-resolver.md) |
|
||||
| M2 placement grouper | Implemented extraction | [`m2-placement-grouper.md`](m2-placement-grouper.md) |
|
||||
| M2 build batch planner | Implemented extraction | [`m2-build-batch-planner.md`](m2-build-batch-planner.md) |
|
||||
| Third-person camera | Implemented | [`third-person-camera.md`](third-person-camera.md) |
|
||||
| Character presentation | Implemented boundary / Partial fidelity | [`character-presentation.md`](character-presentation.md) |
|
||||
| Renderer | Partial | [`world-renderer.md`](world-renderer.md), [`../../RENDER.md`](../../RENDER.md) |
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
# 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)
|
||||
@@ -153,8 +153,8 @@ continue to own task/build/cache observability.
|
||||
|
||||
## Extension points
|
||||
|
||||
- A later package may extract the M2 build-job state machine behind explicit
|
||||
budgets while retaining this pure grouping input.
|
||||
- `M2BuildBatchPlanner` now owns pure batch sizing/cursor progression; the
|
||||
remaining build-job state machine stays behind explicit loader budgets.
|
||||
- Spatial cells require measured culling evidence and are outside this contract.
|
||||
|
||||
## Capability status
|
||||
@@ -179,11 +179,13 @@ continue to own task/build/cache observability.
|
||||
|---|---|
|
||||
| `src/render/m2/m2_placement_grouper.gd` | Validation, normalization and grouping |
|
||||
| `src/render/m2/m2_placement_transform_resolver.gd` | Basis and origin rules |
|
||||
| `src/render/m2/m2_build_batch_planner.gd` | Downstream batch sizing and cursor planning |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Worker/result/build lifecycle and resources |
|
||||
| `src/tools/verify_m2_placement_grouper.gd` | Contract, dependency and timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-build-batch-planner.md`](m2-build-batch-planner.md)
|
||||
- [`m2-placement-transform-resolver.md`](m2-placement-transform-resolver.md)
|
||||
- [`m2-unique-placement-registry.md`](m2-unique-placement-registry.md)
|
||||
- [`world-renderer.md`](world-renderer.md)
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Partial |
|
||||
| Target/work package | M00 baseline; `M01-RND-STREAMING-FOCUS-001`; `M01-QAR-SERVER-SPAWN-RENDERER-001`; M03 facade/planner/scheduler/internal-access/ground/environment/entity packages; M03 terrain packages; M03 M2 unique/transform/grouping packages |
|
||||
| Target/work package | M00 baseline; `M01-RND-STREAMING-FOCUS-001`; `M01-QAR-SERVER-SPAWN-RENDERER-001`; M03 facade/planner/scheduler/internal-access/ground/environment/entity packages; M03 terrain packages; M03 M2 unique/transform/grouping/batch packages |
|
||||
| Owners | Renderer workstream / milestone integrator |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-placement-grouper`, 2026-07-16 |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-build-batch-planner`, 2026-07-17 |
|
||||
| Profiles/capabilities | `Performance`, `Balanced`, `High`, `Custom`; Blizzlike fidelity incomplete |
|
||||
|
||||
## Purpose
|
||||
@@ -52,6 +52,8 @@ flowchart LR
|
||||
M2Grouper --> M2Transform[M2PlacementTransformResolver]
|
||||
M2Transform --> M2Grouper
|
||||
M2Transform --> Loader
|
||||
Loader --> M2Batch[M2BuildBatchPlanner]
|
||||
M2Batch --> Loader
|
||||
Native --> Parsed[Parsed tile/model data]
|
||||
Parsed --> Loader
|
||||
Loader --> Scene[SceneTree nodes]
|
||||
@@ -121,6 +123,7 @@ from externally reading/writing loader-private queue, task, cache and tile-state
|
||||
| `M2UniquePlacementRegistry.reserve/release/clear` | Internal M2 service | Owns positive cross-tile ADT placement-ID reservations | Renderer thread/map session | Invalid/unkeyed/non-owner inputs preserve documented fallback |
|
||||
| `M2PlacementTransformResolver.resolve_basis/resolve_origin_offset` | Internal pure M2 service | Resolves regular and calibrated model-specific ADT placement transforms | Worker/main thread; stateless | Unknown paths use regular basis and zero offset |
|
||||
| `M2PlacementGrouper.group_placements` | Internal pure M2 service | Validates and groups ordered tile-local placement transforms by normalized path | Worker/main thread; stateless | Invalid variants/name IDs/empty paths are skipped |
|
||||
| `M2BuildBatchPlanner.plan_batch` | Internal pure M2 service | Selects static/animated batch count and next group cursor | Main/any thread; stateless | Non-positive selected limit clamps to one; empty range completes |
|
||||
|
||||
Публичным contract не считаются `StreamingWorldLoader` methods/properties,
|
||||
внутренние dictionaries, queues, job records и generated node names. Scene-owned
|
||||
@@ -148,6 +151,7 @@ loader configuration remains transitional composition data, not a caller API.
|
||||
| Internal registry | Tile key and M2 placement unique IDs | Loader / `M2UniquePlacementRegistry` | Filtered grouping input and tile retry state | Registry-owned strings; fresh result arrays | Map session |
|
||||
| Internal transform | Rotation/path/scale | Loader or grouper / `M2PlacementTransformResolver` | Group/placeholder/instance transforms | Value-only Basis/Vector3 | One placement |
|
||||
| Internal grouping | Tile origin, M2 names and placements | Loader / `M2PlacementGrouper` | Loader worker result/build job | Fresh Dictionary/Transform3D arrays | One grouping task |
|
||||
| Internal batch plan | Transform count/offset, path kind and limits | Loader / `M2BuildBatchPlanner` | Loader materialization/cursor adapter | Fresh scalar Dictionary | One build operation |
|
||||
| Output | Desired tile/detail operations | Streamer plan application | Finalize queues | Loader-owned | Cross-frame |
|
||||
| Output | Terrain/M2/WMO/liquid instances | Loader/builders | Godot world/renderer | Loader/world owner | Main-thread attach |
|
||||
| Output | Detached renderer metrics | `WorldRenderFacade` | Capture/baseline tools | Caller-owned deep copy | On demand |
|
||||
@@ -194,7 +198,8 @@ flowchart TD
|
||||
R --> M2Registry[M2UniquePlacementRegistry reserve]
|
||||
M2Registry --> M2Grouper[M2PlacementGrouper]
|
||||
M2Transform[M2PlacementTransformResolver] --> M2Grouper
|
||||
M2Grouper --> M2
|
||||
M2Grouper --> M2Batch[M2BuildBatchPlanner]
|
||||
M2Batch --> M2
|
||||
R --> B[RenderBudgetScheduler permits]
|
||||
B --> Terrain[Terrain attach/upgrade]
|
||||
B --> M2[M2 group/MultiMesh attach]
|
||||
@@ -314,6 +319,8 @@ sequenceDiagram
|
||||
transforms and every build/render side effect remain loader-owned.
|
||||
- `M2PlacementGrouper` is stateless and owns only call-local grouped transforms.
|
||||
The loader retains tasks, mutex/result queues, stale checks and build state.
|
||||
- `M2BuildBatchPlanner` is stateless and owns only call-local scalar plans. The
|
||||
loader retains queue/resource transitions, cursor adoption and materialization.
|
||||
- Rendered-ground query results and diagnostic snapshots are caller-owned values;
|
||||
the facade never returns Mesh, Node, tile-state or queue references.
|
||||
- Loaded-mesh ground sampling is renderer diagnostics, not authoritative gameplay
|
||||
@@ -403,6 +410,8 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
normalization, scale compensation, all three consumers and bounded timing.
|
||||
- M2 placement grouper contract: invalid/default records, historical path rules,
|
||||
group order, local transforms, fresh output, source boundary and bounded timing.
|
||||
- M2 build batch contract: static/animated limit selection, clamp, remaining
|
||||
count, completion/cursor rules, source boundary and bounded timing.
|
||||
- Budget scheduler contract: exact lane exhaustion, shared chunk removal/create
|
||||
priority, independent lanes, frame reset, invalid limits, terminal cancellation,
|
||||
dependency boundaries and bounded permit timing without loading a world scene.
|
||||
@@ -435,6 +444,7 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
| M2 unique placement registry | Implemented extraction | Scene-free ownership/lifecycle/timing contract and historical `uid:11785` smoke | Group/build/tasks/finalization and asset-backed p95/p99 remain pending |
|
||||
| M2 placement transform resolver | Implemented extraction | Scene-free formula/source/timing contract across three consumers | Asset-backed visual recheck and general placement parity pending |
|
||||
| M2 placement grouper | Implemented extraction | Scene-free validation/order/transform/source/timing contract | Worker/build state, spatial cells and asset-backed p95/p99 remain pending |
|
||||
| M2 build batch planner | Implemented extraction | Scene-free limit/count/cursor/source/timing contract | Queue/resource state, spatial cells and asset-backed p95/p99 remain pending |
|
||||
| WMO rendering | Partial | Cached group rendering | Portals/rooms/material parity |
|
||||
| Liquids | Partial | MH2O/MLIQ paths | LiquidType/depth/shore fidelity |
|
||||
| Sky/light | Partial | DBC controller/logs | Zone transition paired validation |
|
||||
|
||||
Reference in New Issue
Block a user