# Terrain Chunk Geometry Queue Planner ## Metadata | Field | Value | |---|---| | Status | Implemented extraction | | Target/work package | M03 / `M03-RND-TERRAIN-CHUNK-QUEUE-001` | | Owners | Pure chunk geometry create/remove request calculation | | Last verified | Worktree `work/sindo-main-codex/m03-terrain-chunk-queue`, 2026-07-16 | | Profiles/capabilities | Existing chunk/tile LOD desired state; all profiles | ## Purpose Compare loader-owned current chunk geometry with desired chunk/tile LOD state and produce fresh removal and nearest-first creation request arrays. ## Non-goals - Retain, drain or bound queues, or own per-frame operation permits. - Create/remove Mesh, Node, material or RID resources. - Calculate desired chunk/tile LOD, parse ADT data or schedule worker tasks. - Change cache versions, renderer profiles or visible fidelity rules. - Provide a generic queue/operation framework. ## Context and boundaries ```mermaid flowchart LR Current[Loader-owned tile/chunk state] --> Planner[TerrainChunkGeometryQueuePlanner] Desired[Desired chunk and tile LOD state] --> Planner Focus[GodotWorldPosition] --> Planner Planner --> Remove[Stable-order remove requests] Planner --> Create[Nearest-first create requests] Remove --> Loader[StreamingWorldLoader queues] Create --> Loader Loader --> Budget[RenderBudgetScheduler permit] Budget --> GPU[Existing Mesh/Node/RID side effects] ``` Allowed dependencies are typed Godot position and call-local scalar/container values. SceneTree, RenderingServer, ResourceLoader, worker APIs, gameplay, network, editor UI, files and persistent caches are forbidden. ## Public API | Symbol | Kind | Purpose | Thread/lifetime | Errors | |---|---|---|---|---| | `TerrainChunkGeometryQueuePlanner.plan(tile_states, focus_position)` | Pure query | Return fresh `create_requests` and `remove_requests` arrays | Synchronous; caller owns result | Null/wrong focus returns two empty arrays | Script-identity validation uses a preload and therefore works from a cold Godot global-class cache. ## Inputs and outputs | Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime | |---|---|---|---|---|---| | Input | Tile-state read model with current chunks and desired LODs | Streamer | Planner | Loader-owned; read only | One rebuild | | Input | Desired tile LOD | Tile LOD calculation | Planner | Scalar in loader state | One rebuild | | Input | Parsed chunk origins and tile fallback origin | ADT result/loader | Planner | Loader-owned; read only | Map session | | Input | `GodotWorldPosition` | Streamer focus adapter | Planner | Immutable caller reference | One rebuild | | Output | Remove `{tile, chunk}` records | Planner | Loader removal queue | Fresh caller array/dictionaries | Until drained/rebuilt | | Output | Create `{tile, chunk, lod, dist_sq}` records | Planner | Loader creation queue | Fresh nearest-first array/dictionaries | Until drained/rebuilt | Distance is horizontal XZ squared distance from the chunk origin, not its center. Y is intentionally ignored. A missing/sparse parsed origin falls back to the tile state's existing origin exactly as before extraction. ## Data flow ```mermaid flowchart TD Start[plan] --> FocusValid{Typed focus?} FocusValid -->|no| Empty[Return two empty arrays] FocusValid -->|yes| Tile[Iterate tile state insertion order] Tile --> TileLod{Desired tile LOD active?} TileLod -->|yes| RemoveAll[Append every current chunk removal] TileLod -->|no| Compare[Compare current and desired chunks] Compare --> RemoveMissing[Append current chunks absent from desired] Compare --> CreateMismatch[Append LOD-mismatched creates] Compare --> CreateMissing[Append absent desired creates] CreateMismatch --> Origin[Parsed chunk origin or tile fallback] CreateMissing --> Origin Origin --> Distance[Attach squared XZ distance] RemoveAll --> Next[Next tile] RemoveMissing --> Next Distance --> Next Next --> Sort[Sort creates nearest-first] Sort --> Result[Return fresh arrays] ``` ## Lifecycle/state The planner is stateless. Each loader queue rebuild supplies current read models and receives new arrays. The loader adopts and drains those arrays until another rebuild replaces them. Planner destruction requires no cleanup or cancellation. ## Main sequence ```mermaid sequenceDiagram participant Loader as StreamingWorldLoader participant Planner as TerrainChunkGeometryQueuePlanner participant Budget as RenderBudgetScheduler participant Render as Existing terrain finalization Loader->>Planner: plan(tile states, typed focus) Planner-->>Loader: fresh create/remove arrays Loader->>Budget: request shared chunk_geometry permit Budget-->>Loader: permit Loader->>Render: pop_back removal first Loader->>Budget: request shared chunk_geometry permit Budget-->>Loader: permit Loader->>Render: pop_front nearest creation ``` ## Ownership, threading and resources - The planner owns only call-local arrays and dictionaries until return. - The loader owns adopted queue storage, drain ordering, tile/chunk state and all Mesh/Node/material/RID side effects. - Calls and drains currently run on the renderer main thread. The planner has no lock, task, retained resource or concurrent mutation. - Removal and creation continue to share the existing `chunk_geometry` budget lane. ## Errors, cancellation and recovery | Failure | Detection | Behavior | Diagnostic | Recovery | |---|---|---|---|---| | Null/wrong focus script | Identity guard | Return empty request arrays | Contract verifier | Supply typed focus and rebuild | | Sparse/out-of-range chunk data | Origin resolver | Use tile fallback origin | Synthetic fixture | Normal ADT completion restores precise origin | | Tile LOD becomes desired | Desired tile LOD check | Remove every current chunk; no chunk creates | Queue metrics | Existing tile-LOD create path takes over | | Queue rebuild before drain | Loader lifecycle | Replace old desired-operation arrays | Existing queue metrics | Latest state wins | | Shutdown/map reset | Loader lifecycle | Loader clears queues; planner has nothing to cancel | Shutdown verifier | New session replans | ## Configuration and capabilities The planner introduces no settings. It consumes already-calculated desired LOD state. Operation limits, profile defaults and removal-first drain order remain loader/scheduler configuration. ## Persistence, cache and migration No serialization, cache entry or format changes. Baked terrain, control-splat and splat caches require no rebuild or migration. ## Diagnostics and observability The planner emits no logs. Existing queue metrics observe the adopted arrays. The contract verifier reports semantic cases and bounded multi-tile timing. Asset-backed p95/p99 remains milestone acceptance work. ## Verification - `verify_terrain_chunk_geometry_queue_planner.gd`: missing/mismatched/unchanged chunks, tile-LOD removal, sparse origin fallback, XZ-only distance, stable removals, nearest creates, invalid focus, loader boundary and bounded timing. - Existing LOD planner, terrain cache, facade, internal-access, manifest, shutdown, scheduler, streaming and coordinate regressions remain required. - Fidelity evidence is exact branch/request/order preservation only. No new build-12340 visual parity claim is made. ## Extension points - Extract tile-LOD operation planning as its own bounded contract if justified. - Add explicit typed tile-state adapters only when more than one consumer exists. ## Capability status | Capability | Status | Evidence | Gap/next step | |---|---|---|---| | Pure chunk create/remove planning | Implemented extraction | Asset-free semantic/source/timing verifier | Asset-backed p95/p99 pending | | Queue execution/resource ownership | Remains in loader | Drain source boundary and scheduler regression | Further terrain decomposition required | ## Known gaps and risks - Raw tile/chunk dictionaries remain a transitional ADT adapter boundary. - The planner performs a second linear tile-state pass beside tile-LOD planning; bounded synthetic timing is green, but asset-backed frame metrics are pending. - Equal-distance create ordering retains comparator behavior without a tie-breaker. - Terrain geometry finalization and state mutation remain monolithic. ## Source map | Path | Responsibility | |---|---| | `src/render/terrain/terrain_chunk_geometry_queue_planner.gd` | Pure request calculation and nearest-first creation order | | `src/scenes/streaming/streaming_world_loader.gd` | Queue adoption, tile-LOD planning, budgets and render side effects | | `src/tools/verify_terrain_chunk_geometry_queue_planner.gd` | Semantic, boundary and timing regression | ## Related decisions and references - [`terrain-chunk-lod-planner.md`](terrain-chunk-lod-planner.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)