196 lines
8.9 KiB
Markdown
196 lines
8.9 KiB
Markdown
# 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)
|