199 lines
9.4 KiB
Markdown
199 lines
9.4 KiB
Markdown
# 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
|
|
|
|
```mermaid
|
|
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
|
|
|
|
```mermaid
|
|
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
|
|
|
|
```mermaid
|
|
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
|
|
|
|
```mermaid
|
|
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 |
|
|
|
|
## Related decisions and references
|
|
|
|
- [`world-renderer.md`](world-renderer.md)
|
|
- [`terrain-query.md`](terrain-query.md)
|
|
- [`../../RENDER.md`](../../RENDER.md)
|
|
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
|