refactor(M03): extract M2 runtime mesh finalizer
Work-Package: M03-RND-M2-RUNTIME-MESH-FINALIZER-001 Agent: sindo-main-codex Tests: 37 headless renderer/coordinate contracts pass; checkpoint dry-run 7/7; documentation and coordination gates pass Fidelity: preserves refresh version 2, classifier, rebuild and fallback transitions; no visual parity claim
This commit is contained in:
@@ -0,0 +1,222 @@
|
||||
# M2 Runtime Mesh Finalizer
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-RUNTIME-MESH-FINALIZER-001` |
|
||||
| Owners | Stale Mesh refresh version, rebuild classification, M2Builder rebuild and fallback |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-runtime-mesh-finalizer`, 2026-07-17 |
|
||||
| Profiles/capabilities | Static cached and prototype M2 runtime material preparation |
|
||||
|
||||
## Purpose
|
||||
|
||||
Finalize an extracted static M2 Mesh for runtime use by accepting already-loaded
|
||||
raw M2 data, preserving material refresh version `2`, rebuilding only billboard/
|
||||
UV-rotation cases and returning the historical original-Mesh fallback.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Read raw `.m2` files or call ClassDB/FileAccess/ResourceLoader.
|
||||
- Own request/finalize queues, Mesh/missing/prototype caches or MultiMeshes.
|
||||
- Select cache paths or extract a Mesh from the originally loaded Resource.
|
||||
- Change classifier predicates, builder materials, refresh version or profiles.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Extractor[M2MeshResourceExtractor] --> Loader[StreamingWorldLoader]
|
||||
Loader -->|is raw data required?| Finalizer[M2RuntimeMeshFinalizer]
|
||||
Loader --> Raw[M2Loader raw Dictionary]
|
||||
Raw --> Finalizer
|
||||
Finalizer --> Classifier[M2RuntimeMeshRebuildClassifier]
|
||||
Finalizer --> Builder[M2Builder]
|
||||
Builder --> Subtree[M2MeshResourceExtractor]
|
||||
Finalizer -->|prepared Mesh| Cache[M2MeshResourceCacheState]
|
||||
```
|
||||
|
||||
The finalizer may use Mesh metadata, raw value Dictionaries, M2Builder and the
|
||||
two existing scene-free M2 services. Raw-file I/O, cache state, render permits,
|
||||
Nodes outside temporary rebuild roots and other application layers are forbidden.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `requires_raw_data_for_refresh(mesh)` | Query | Tell loader whether stale Mesh needs raw-file lookup | Renderer main thread | Null/current false |
|
||||
| `finalize_mesh(path, mesh, raw_data, extracted_directory)` | Command/query | Return current, marked, rebuilt or fallback Mesh | Renderer main thread; one finalize operation | Null Mesh returns null; invalid rebuild falls back |
|
||||
| `clear()` | Command | Clear memoized classifier decisions | Map reset/final shutdown | Idempotent |
|
||||
| `cached_rebuild_decision_count()` | Diagnostic query | Expose memoized path-decision count | Tests/diagnostics | None |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Extracted Mesh and normalized M2 path | Loader/extractor adapter | Finalizer | Borrowed Mesh reference/String copy | One call |
|
||||
| Input | Already-loaded raw M2 Dictionary | Loader raw-file boundary | Classifier/M2Builder | Caller-owned value container | One call |
|
||||
| Input | Extracted directory path | Loader configuration | M2Builder texture resolution | Copied String | One rebuild |
|
||||
| Output | Original or rebuilt current Mesh | Finalizer | Cache/prototype adapter | Borrowed/new Resource reference | Cache may retain |
|
||||
|
||||
Side effects are Mesh refresh metadata, classifier memoization and temporary
|
||||
M2Builder prototype creation/destruction during required rebuilds.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Mesh[Extracted Mesh] --> Current{Refresh version >= 2?}
|
||||
Current -->|yes| ReturnOriginal[Return original unchanged]
|
||||
Current -->|no| NeedRaw[Loader supplies raw M2 data]
|
||||
NeedRaw --> HasData{Raw data available?}
|
||||
HasData -->|no| Mark[Mark original version 2]
|
||||
HasData -->|yes| Classify{Billboard or UV rotation?}
|
||||
Classify -->|no| Mark
|
||||
Classify -->|yes| Build[M2Builder temporary prototype]
|
||||
Build --> Extract[M2MeshResourceExtractor first Mesh]
|
||||
Extract --> Rebuilt{Rebuilt Mesh exists?}
|
||||
Rebuilt -->|yes| ReturnRebuilt[Return rebuilt Mesh]
|
||||
Rebuilt -->|no| Mark
|
||||
Mark --> ReturnOriginal
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Current: Mesh metadata >= 2
|
||||
[*] --> Stale: Mesh metadata < 2
|
||||
Stale --> Current: empty/ordinary data marks original
|
||||
Stale --> Rebuilding: classifier requires rebuild
|
||||
Rebuilding --> Current: rebuilt Mesh returned
|
||||
Rebuilding --> Current: failure marks original fallback
|
||||
Current --> Current: later preparation is no-op
|
||||
```
|
||||
|
||||
Classifier decisions are memoized per normalized path until either existing
|
||||
map/reset clear site calls `clear()`.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant Finalizer as M2RuntimeMeshFinalizer
|
||||
participant Raw as M2Loader boundary
|
||||
participant Classifier as RebuildClassifier
|
||||
participant Builder as M2Builder
|
||||
Loader->>Finalizer: requires_raw_data_for_refresh(mesh)
|
||||
alt current Mesh
|
||||
Finalizer-->>Loader: false; reuse Mesh
|
||||
else stale Mesh
|
||||
Finalizer-->>Loader: true
|
||||
Loader->>Raw: load raw Dictionary
|
||||
Loader->>Finalizer: finalize_mesh(path, mesh, raw, extracted dir)
|
||||
Finalizer->>Classifier: needs_runtime_mesh_rebuild(path, raw)
|
||||
opt rebuild required
|
||||
Finalizer->>Builder: build(raw, extracted dir)
|
||||
Finalizer->>Finalizer: extract first Mesh; free prototype
|
||||
end
|
||||
Finalizer-->>Loader: rebuilt or marked fallback Mesh
|
||||
end
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Finalizer[M2RuntimeMeshFinalizer]
|
||||
Finalizer --> Classifier[M2RuntimeMeshRebuildClassifier]
|
||||
Finalizer --> Extractor[M2MeshResourceExtractor]
|
||||
Finalizer --> Builder[M2Builder]
|
||||
Loader --> Raw[FileAccess / ClassDB M2Loader]
|
||||
Loader --> Cache[M2MeshResourceCacheState]
|
||||
Finalizer -. no dependency .-> Raw
|
||||
Finalizer -. no dependency .-> Cache
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Renderer main thread executes metadata changes and M2Builder work.
|
||||
- Loader owns raw M2 file existence/load calls and supplies value data.
|
||||
- Finalizer owns classifier memoization and temporary rebuild prototype lifetime.
|
||||
- M2Builder owns construction rules; extractor selects the first rebuilt Mesh.
|
||||
- Cache/prototype adapters decide where the returned Mesh reference is retained.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Null Mesh | Guard | Return null | Contract verifier | Caller marks missing |
|
||||
| Current Mesh | Metadata query | Return exact reference; no raw load | Contract verifier | None needed |
|
||||
| Raw file/parse unavailable | Empty Dictionary from loader | Mark/reuse original | Existing renderer behavior | Source/cache repair |
|
||||
| Ordinary stale Mesh | Classifier false | Mark/reuse original | Classifier verifier | None needed |
|
||||
| Builder produces no Mesh | Extractor null | Mark/reuse original | Finalizer fallback fixture | Source/builder repair |
|
||||
| Reset/shutdown | Loader lifecycle | Clear classifier decisions | Source/clear verifier | Recompute next session |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
`MATERIAL_REFRESH_VERSION` remains `2` and is now owned by this finalizer.
|
||||
Finalize permits, cache paths, extracted directory configuration and quality
|
||||
profiles remain unchanged in the loader.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
No new format is serialized. Existing Mesh metadata key
|
||||
`wow_m2_material_refresh_version`, M2Builder `MATERIAL_FORMAT_VERSION == 2` and
|
||||
`.tscn/.glb` caches are unchanged; no migration or rebake is introduced.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
`cached_rebuild_decision_count` exposes only a scalar count. Existing renderer
|
||||
logs/queue metrics are unchanged. Normalized M2 path remains the correlation key.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_m2_runtime_mesh_finalizer.gd`: null/current/raw requirement, empty and
|
||||
ordinary marking, synthetic triangle billboard rebuild, rebuild failure
|
||||
fallback, refresh metadata, classifier clear, source boundary and 100-by-256
|
||||
bounded finalization.
|
||||
- Classifier/extractor/cache/pipeline/material/shutdown/M2 build regressions cover
|
||||
adjacent behavior. No asset-backed or original-client visual claim is made.
|
||||
|
||||
## Extension points
|
||||
|
||||
- Raw M2 file loading may later move behind a dedicated repository without
|
||||
changing the finalizer's value-data contract.
|
||||
- Refresh versions above `2` require explicit cache/fidelity evidence and docs.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Runtime Mesh refresh/fallback | Implemented extraction | Synthetic transition/rebuild verifier | Asset-backed corrupt/stale cache pending |
|
||||
| Rebuild predicate | Implemented extraction dependency | Classifier fixtures | Original-client material comparison pending |
|
||||
| Raw M2 refresh I/O | Existing loader-owned | Source boundary | Separate repository extraction optional |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Required rebuild remains synchronous main-thread work behind existing permits.
|
||||
- Synthetic geometry proves transition/build behavior, not full asset materials.
|
||||
- No proprietary M2 traversal, descriptor-pressure/leak, p95/p99 or paired
|
||||
original-client capture is included.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_runtime_mesh_finalizer.gd` | Refresh version, classification, rebuild and fallback |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Raw-file I/O and returned-Mesh adoption |
|
||||
| `src/render/m2/m2_runtime_mesh_rebuild_classifier.gd` | Memoized billboard/UV-rotation predicate |
|
||||
| `src/render/m2/m2_mesh_resource_extractor.gd` | First rebuilt-Mesh selection |
|
||||
| `src/tools/verify_m2_runtime_mesh_finalizer.gd` | Transition/rebuild/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-runtime-mesh-rebuild-classifier.md`](m2-runtime-mesh-rebuild-classifier.md)
|
||||
- [`m2-mesh-resource-extractor.md`](m2-mesh-resource-extractor.md)
|
||||
- [`m2-mesh-resource-cache-state.md`](m2-mesh-resource-cache-state.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)
|
||||
Reference in New Issue
Block a user