# WMO Render Build Queue ## Metadata | Field | Value | |---|---| | Status | Implemented | | Target/work package | M03 / `M03-RND-WMO-RENDER-BUILD-QUEUE-001` | | Owners | Typed lightweight-WMO build jobs and FIFO placement-key queue | | Last verified | Worktree `work/sindo-main-codex/m03-wmo-render-build-queue`, 2026-07-17 | | Profiles/capabilities | Lightweight WMO render-cache path; profile-independent | ## Purpose Own pending lightweight-WMO render build records and their FIFO placement-key order outside the monolithic loader. Each typed job retains its placement key, scene root, render resource and mesh/MultiMesh cursors while the loader performs all validation, planning, materialization, permit consumption and destruction. ## Non-goals - Free or queue-free Nodes, Resources or RIDs. - Create MeshInstance3D/MultiMeshInstance3D or refresh materials. - Own frame permits, placement references, render cache state or load I/O. - Change duplicate enqueue, stale-key cleanup, cancellation or ordering. - Change cache versions, WMO transforms, shadows, visibility or fidelity rules. ## Context and boundaries ```mermaid flowchart LR Placement[Successful WMO placement] --> Loader[StreamingWorldLoader] Root[Node3D root] --> Loader Cache[WMOStreamingResource] --> Loader Loader --> Queue[WmoRenderBuildQueue] Queue --> Job[WmoRenderBuildJob] Job --> Loader Loader --> Planner[WmoRenderBuildStepPlanner] Planner --> Step[Operation and next cursors] Step --> Loader Loader --> Scene[Main-thread group Node attach] Loader --> Budget[RenderBudgetScheduler permit] ``` The queue may strongly reference Node3D and Resource through typed jobs, but it must not destroy, mutate or inspect their render content. RenderingServer, RID, Mesh/MultiMesh instances, files, worker threads, mutexes, gameplay, network and editor UI are forbidden dependencies. ## Public API ### `WmoRenderBuildJob` | Symbol | Kind | Purpose | Thread/lifetime | Errors | |---|---|---|---|---| | constructor `(placement_key, root, render_resource)` | Constructor | Create cursor-zero job with strong references | Renderer main thread; until cancel/clear/replacement | Queue validates inputs before construction | | `placement_key()` | Query | Return immutable queue identity | Any serialized caller; job lifetime | None | | `root()` | Query | Borrow retained Node3D reference | Main thread; no ownership transfer | May later be invalid; loader checks | | `render_resource()` | Query | Borrow retained Resource reference | Main thread; no ownership transfer | Queue does not validate cache contents | | `mesh_index()` / `multimesh_index()` | Query | Return current group cursors | Serialized job drain | None | | `adopt_cursors(next_mesh_index, next_multimesh_index)` | Command | Atomically adopt planner cursors | Renderer main thread | Raw integers are accepted | | `diagnostic_snapshot()` | Query | Return detached scalar state and presence flags | Caller-owned result | Never exposes engine objects | ### `WmoRenderBuildQueue` | Symbol | Kind | Purpose | Thread/lifetime | Errors | |---|---|---|---|---| | `enqueue(placement_key, root, render_resource)` | Command | Replace keyed job and append FIFO key | Renderer main thread; map session | Empty/null input returns false | | `has_pending()` / `pending_count()` | Query | Observe FIFO entries including duplicate/stale keys | Renderer main thread | None | | `active_job_count()` | Query | Count current keyed jobs | Renderer main thread | None | | `front_key()` / `pop_front()` | Queue operation | Inspect/remove one front FIFO key | Renderer main thread | Empty queue returns empty String | | `has_job(placement_key)` | Query | Distinguish a live job from a stale FIFO key | Renderer main thread | Unknown key returns false | | `job_for(placement_key)` | Diagnostic/test query | Borrow current job through its `RefCounted` base or return null | Renderer main thread | Runtime loader uses typed accessors instead | | `root_for` / `render_resource_for` | Typed query | Borrow current engine references without exposing job implementation type | Renderer main thread | Stale key returns null | | `mesh_index_for` / `multimesh_index_for` | Typed query | Return current cursors | Renderer main thread | Stale key returns zero | | `adopt_cursors(placement_key, next_mesh_index, next_multimesh_index)` | Command/query | Atomically update current job cursors | Renderer main thread | Stale key returns false | | `cancel(placement_key)` | Command/query | Remove keyed job and first matching FIFO key | Renderer main thread | Unknown key returns false; stale duplicates remain | | `clear()` | Command | Release all job/key references idempotently | Reset/shutdown | Does not free engine objects | | `diagnostic_snapshot()` | Query | Return detached FIFO order and key-sorted scalar jobs | Caller-owned result | None | ## Inputs and outputs | Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime | |---|---|---|---|---|---| | Input | Resolved placement key | Placement/build adapter | Queue/job | Copied String | Until cancel/clear/replacement | | Input | WMO root Node3D | Loader instance adapter | Job | Strong reference; loader owns engine lifetime | Until job release | | Input | Validated WMOStreamingResource | Loader cache adapter | Job | Strong Resource reference; cache/loader owns semantics | Until job release | | Input | Next mesh/MultiMesh cursors | Build step planner through loader | Job | Integer values | Until next adoption | | Output | FIFO front key and typed job | Queue | Loader drain | Borrowed references | One drain iteration | | Output | Detached scalar diagnostics | Job/queue | Verifier/future facade adapter | Fresh values | Caller lifetime | Side effects are limited to reference retention/release, cursor mutation and Dictionary/Array ordering. Scene tree, material, RID, file and permit effects remain outside the module. ## Data flow ```mermaid flowchart TD Enqueue[enqueue key/root/resource] --> Valid{Inputs valid?} Valid -->|no| Reject[false; unchanged] Valid -->|yes| Replace[Replace keyed typed job] Replace --> Append[Append key even when duplicate] Drain[front key] --> Lookup{Current keyed job exists?} Lookup -->|no stale| Pop[pop front and continue] Lookup -->|yes| Borrow[Borrow job to loader] Borrow --> Plan[Loader invokes step planner] Plan --> Adopt[Job adopts both next cursors] Cancel[cancel key] --> EraseJob[Erase keyed job] EraseJob --> EraseFirst[Erase first matching FIFO key] ``` ## Lifecycle/state The queue can be `Empty`, `Pending` or `StaleFront`. Duplicate enqueue followed by cancel can intentionally produce `StaleFront`, matching the previous separate Dictionary/Array behavior. The loader pops a stale front without consuming a permit. `clear` returns every state to `Empty` without freeing engine objects. ```mermaid stateDiagram-v2 [*] --> Empty Empty --> Pending: valid enqueue Pending --> Pending: enqueue / cursor adoption Pending --> Empty: final cancel or clear Pending --> StaleFront: duplicate key then cancel first occurrence StaleFront --> Pending: pop stale, more live entries remain StaleFront --> Empty: pop stale or clear ``` ## Main sequence ```mermaid sequenceDiagram participant Loader as StreamingWorldLoader participant Queue as WmoRenderBuildQueue participant Job as WmoRenderBuildJob participant Planner as WmoRenderBuildStepPlanner Loader->>Queue: enqueue(key, root, resource) loop while permit and pending key Loader->>Queue: front_key + has_job alt stale key Loader->>Queue: pop_front else live job Loader->>Queue: typed root/resource/cursor accessors Loader->>Planner: plan_step(counts, job cursors) Planner-->>Loader: operation and next cursors Loader->>Loader: materialize one group on main thread Loader->>Queue: adopt_cursors(key, next mesh, next MultiMesh) Loader->>Loader: consume one permit end end Loader->>Queue: cancel(key) or clear() Note over Queue: references released; Nodes/Resources not freed ``` ## Ownership, threading and resources - Queue owns its job Dictionary and FIFO key Array. - Job owns strong references, but loader owns Node/Resource semantic lifetime. - Loader checks `is_instance_valid`, creates/frees Nodes and controls caches. - Loader invokes planner and adopts cursor output on the main thread. - Queue/job contain no mutex; all mutation is serialized by the renderer drain. - `clear` may release the last Resource reference but never calls engine free APIs. ## Errors, cancellation and recovery | Failure/state | Detection | Behavior | Diagnostic | Recovery | |---|---|---|---|---| | Empty key/null root/resource | Enqueue guards | Reject without mutation | Contract verifier | Repair caller input | | Duplicate key | Existing keyed job | Replace job and append key | Queue diagnostics | Historical stale handling remains | | Stale FIFO key | `job_for` returns null | Loader pops front without permit | Contract/source verifier | Continue drain | | Root invalidated externally | Loader validity check | Loader cancels keyed job | Shutdown/runtime diagnostics | Placement can rebuild later | | Resource absent | Loader check | Loader cancels keyed job | Runtime diagnostics | Reload/requeue placement | | Final placement release | Placement registry returns final | Loader cancels build then frees Node | Registry/shutdown regressions | Later placement starts fresh | | Reset/shutdown | Loader lifecycle | Queue clear releases references | Shutdown verifier | Next map starts empty | ## Configuration and capabilities The module introduces no settings or profile branches. WMO group permit count, quality presets, cache limits, visibility and shadows remain loader-owned. ## Persistence, cache and migration No data is serialized. WMO cache format/version/path and load request behavior are unchanged. No migration or cache rebuild is required. ## Diagnostics and observability `pending_count` preserves existing queue metrics, including stale/duplicate keys. The detached snapshot exposes FIFO order, cursor values and root/resource presence without exposing mutable jobs or engine objects. No runtime logs are emitted. ## Verification - `verify_wmo_render_build_queue.gd`: invalid inputs, FIFO, typed references, cursor adoption, duplicate replacement, stale pop, first-key cancel, clear, engine lifetime, detached sorted diagnostics, source boundaries and timing. - WMO planner/registry/resolver/material/shutdown and renderer regressions verify unchanged ordering, permit use, destruction and surrounding lifecycle. - Fidelity evidence is exact preservation of current queue/Dictionary semantics; no original-client visual or asset-backed performance claim is made. ## Extension points - Cache/load-request ownership can be extracted separately without changing jobs. - A later typed materialization command may consume the borrowed job and planner output while this queue remains the pending-state owner. ## Capability status | Capability | Status | Evidence | Gap/next step | |---|---|---|---| | WMO render build pending-state ownership | Implemented | Lifecycle/source/timing verifier | Asset-backed traversal/leak evidence pending | | WMO group planning | Implemented | Build-step planner verifier | Asset-backed p95/p99 pending | | WMO render Resource state | Implemented extraction | Cache-state lifecycle/source/timing verifier | Asset-backed traversal/leak evidence pending | | WMO materialization/cache I/O | Partial | Existing runtime regressions | Cached-scene and I/O seams remain | ## Known gaps and risks - Jobs intentionally retain strong Node/Resource references until cancel/clear. - Duplicate enqueue preserves a stale-key edge case rather than normalizing it. - Resource array shape remains a WMO cache-builder invariant. - No proprietary WMO corpus, traversal profile or original-client paired capture is included in this package. ## Source map | Path | Responsibility | |---|---| | `src/render/wmo/wmo_render_build_job.gd` | Typed references and cursors | | `src/render/wmo/wmo_render_build_queue.gd` | Keyed jobs, FIFO order, cancel/clear and diagnostics | | `src/render/wmo/wmo_render_build_step_planner.gd` | Pure operation/cursor planning | | `src/scenes/streaming/streaming_world_loader.gd` | Validation, materialization, permits and engine destruction | | `src/tools/verify_wmo_render_build_queue.gd` | Lifecycle, ownership, boundary and timing regression | ## Related decisions and references - [`wmo-render-build-step-planner.md`](wmo-render-build-step-planner.md) - [`wmo-render-resource-cache-state.md`](wmo-render-resource-cache-state.md) - [`wmo-placement-registry.md`](wmo-placement-registry.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)