Files
open-wc/docs/modules/terrain-quality-mesh-cache.md
T

9.4 KiB

Terrain Quality Mesh Cache

Metadata

Field Value
Status Implemented extraction
Target/work package M03 / M03-RND-TERRAIN-CACHE-SERVICE-001
Owners Full-quality terrain revisit Mesh retention and LRU policy
Last verified Worktree work/sindo-main-codex/m03-terrain-quality-cache, 2026-07-16
Profiles/capabilities Performance/Balanced/High/Custom capacity; session-memory only

Purpose

Retain eligible full-quality terrain Mesh references after ADT tile state unload, so a revisit can restore quality without flashing to a lower-quality fallback, while bounding retained references with the existing least-recently-used policy.

Non-goals

  • Parse/build terrain, own tile state or schedule quality-upgrade tasks.
  • Create/finalize Nodes or RIDs, mutate materials or choose terrain LOD.
  • Persist cache entries or change baked/control-splat/splat format versions.
  • Cache control-splat/splat sources whose active radius has separate ownership.
  • Provide a generic cache framework or change profile capacity defaults.

Context and boundaries

flowchart LR
    Upgrade[Full-quality baked upgrade result] --> Loader[StreamingWorldLoader adapter]
    Loader --> Cache[TerrainQualityMeshCache]
    Cache -->|retained Mesh reference| Revisit[Reloaded tile state]
    Revisit --> Finalize[Existing tile LOD/finalization path]
    Splat[Control/splat quality sources] -. rejected .-> Cache

Allowed dependencies: Mesh, Dictionary/Array scalar containers and the loader adapter. Forbidden dependencies: Node/SceneTree/RID, tasks/mutexes, ADT parsing, gameplay/network/editor UI, files and persistent Resource cache ownership.

Public API

Symbol Kind Purpose Thread/lifetime Errors
TerrainQualityMeshCache.store(tile_key, mesh, source, capacity) Command Admit/touch a Mesh and trim oldest entries Owning renderer thread/session Returns false for rejected input or immediate zero-capacity eviction
TerrainQualityMeshCache.restore(tile_key) Query/touch Return a fresh Mesh/source record and mark newest Owning renderer thread Empty on miss; invalid record is removed
TerrainQualityMeshCache.clear() Lifecycle command Release all retained Mesh references and LRU keys Reset/shutdown Idempotent
TerrainQualityMeshCache.diagnostic_snapshot() Read model Detached count and oldest-to-newest key/source list Caller-owned result Never exposes Mesh references

Inputs and outputs

Direction Contract/data Producer Consumer Ownership Thread/lifetime
Input Tile key Streamer tile catalog/state Cache index Copied string Map session
Input Full-quality Mesh Baked upgrade/fallback path Cache Retained shared reference Until eviction/clear
Input Quality source Streamer quality adapter Admission/restore Copied string Entry lifetime
Input Current capacity Active renderer profile Store trim Scalar copy One store
Output Fresh {mesh, source} record Cache Loader state adapter Caller dictionary; shared Mesh reference One restore
Output Detached diagnostics Cache Tests/future metrics Caller-owned scalars One query

Side effects are limited to retained reference and LRU container mutation. There is no scene, GPU, task, file, database, network or persistent-cache mutation.

Data flow

flowchart TD
    Store[store tile/mesh/source/capacity] --> Admit{Valid key + Mesh + source?}
    Admit -->|no| Reject[Return false]
    Admit -->|yes| Touch[Insert/replace and touch newest]
    Touch --> Trim[Evict oldest until count <= capacity]
    Restore[restore tile] --> Hit{Valid admitted entry?}
    Hit -->|no| Miss[Remove invalid / return empty]
    Hit -->|yes| TouchRestore[Touch newest and return fresh record]
    Clear[world reset/shutdown] --> Empty[Release all references and keys]

Lifecycle/state

stateDiagram-v2
    [*] --> Empty
    Empty --> Populated: admitted store with capacity > 0
    Populated --> Populated: store / restore touch / bounded eviction
    Populated --> Empty: capacity zero store evicts all
    Populated --> Empty: clear on reset/shutdown
    Empty --> Empty: rejected store / miss / clear
    Empty --> [*]
    Populated --> [*]: owner releases cache

Main sequence

sequenceDiagram
    participant Upgrade as Terrain upgrade result
    participant Loader as StreamingWorldLoader
    participant Cache as TerrainQualityMeshCache
    Upgrade->>Loader: full Mesh + baked_full source
    Loader->>Cache: store(key, mesh, source, profile capacity)
    Note over Loader,Cache: tile state may later unload
    Loader->>Cache: restore(key) on tile creation/revisit
    Cache-->>Loader: fresh record or empty
    Loader->>Loader: apply Mesh/source to existing tile state
    Loader->>Cache: clear() on world reset/shutdown

Ownership, threading and resources

  • The cache owns its dictionary, ordered tile keys and retained Mesh references.
  • The loader retains tile state, async tasks/results, quality selection and all Node/RID/material finalization.
  • Returned records are new dictionaries; their Mesh is a shared reference.
  • Calls currently occur on the main renderer thread. The service has no lock and must not be mutated concurrently.
  • clear deterministically drops references during the existing world reset path.

Errors, cancellation and recovery

Failure Detection Behavior Diagnostic Recovery
Empty key/null Mesh Store guard Reject unchanged Boolean/test Fix caller result
Splat/control source Admission list Reject unchanged Boolean/test Keep source in active-radius ownership
Zero/negative capacity Clamp and trim Entry immediately evicted; cache empty Snapshot/count Increase profile capacity
Miss/invalid entry Restore validation Remove invalid and return empty Empty record Existing loader fallback/rebuild
World reset/shutdown Loader lifecycle Clear every reference/key Shutdown regression Refill during next session

There is no asynchronous work or cancellation token inside this service.

Configuration and capabilities

Setting/capability Default Profile Runtime mutable Effect
terrain_quality_mesh_cache_limit Existing loader value 0/24/48 in current presets Read on every store Maximum retained entries
Admitted source Non-splat (currently baked_full) All No hidden override Revisit retention
Excluded sources control_splat_cache, splat_cache, splat All No Preserve active quality ownership

Persistence, cache and migration

  • This is an in-memory reference cache, not a serialized cache format.
  • REQUIRED_BAKED_TILE_FORMAT_VERSION=5, control splat v3 and splat v1 remain loader/resource validation contracts and are unchanged.
  • No migration or rebuild is required. Entry keys retain the existing tile-key form.

Diagnostics and observability

  • Detached diagnostics report count and oldest-to-newest tile key/source entries.
  • Mesh, material, Node and RID references are deliberately absent.
  • Performance is bounded dictionary lookup plus one ordered-array erase/append; no new optimization or scale claim is made.

Verification

  • verify_terrain_quality_mesh_cache.gd: admission, exclusions, Mesh identity, source, LRU touch/eviction, detached diagnostics, negative capacity and clear.
  • Loader source boundary proves cache composition/delegation and removal of its former LRU array/touch/trim implementation.
  • Existing shutdown, facade, internal-access, manifest, planner and scheduler regressions remain required.
  • Fidelity: source rules, capacities and three loader adapter sites are unchanged; no visible terrain or build-12340 parity change is claimed.

Extension points

  • Expose scalar cache counts through facade metrics when needed.
  • Move additional terrain responsibilities only as separate bounded services.
  • Replace ordered-array LRU only after measured scale evidence.

Capability status

Capability Status Evidence Gap/next step
Bounded quality Mesh retention Implemented extraction Contract/LRU fixtures Asset-backed memory/revisit timing pending
Deterministic reset ownership Implemented Clear path plus shutdown regression Full terrain service still monolithic
Persistent terrain cache formats Unchanged Loader version constants Separate acceptance audit remains

Known gaps and risks

  • Ordered-array touch is linear in entry count, matching prior behavior.
  • Mesh memory size is not measured; capacity remains entry-count based.
  • Only revisit Mesh ownership moved; build/tasks/state/finalization remain loader-owned.
  • Asset-backed flash/revisit timing and p95/p99 evidence remain pending.

Source map

Path Responsibility
src/render/terrain/terrain_quality_mesh_cache.gd Session cache entries, LRU and admission
src/scenes/streaming/streaming_world_loader.gd Store/restore state adapter and reset lifecycle
src/tools/verify_terrain_quality_mesh_cache.gd Scene-free cache and loader-boundary regression