render: extract M2 build dispatch planner
This commit is contained in:
@@ -41,6 +41,7 @@ Paired run 2026-07-11 подтвердил крупный coordinate/placement g
|
|||||||
- `src/render/m2/m2_animated_instance_materializer.gd` - main-thread animated instance duplication, render settings, playback startup and non-empty batch attachment.
|
- `src/render/m2/m2_animated_instance_materializer.gd` - main-thread animated instance duplication, render settings, playback startup and non-empty batch attachment.
|
||||||
- `src/render/m2/m2_static_batch_materializer.gd` - main-thread static M2 MultiMesh construction, render settings and attachment.
|
- `src/render/m2/m2_static_batch_materializer.gd` - main-thread static M2 MultiMesh construction, render settings and attachment.
|
||||||
- `src/render/m2/m2_build_queue.gd` / `m2_build_job.gd` - typed pending M2 jobs, FIFO/stale tile keys, grouped-transform references and progress cursors without engine destruction.
|
- `src/render/m2/m2_build_queue.gd` / `m2_build_job.gd` - typed pending M2 jobs, FIFO/stale tile keys, grouped-transform references and progress cursors without engine destruction.
|
||||||
|
- `src/render/m2/m2_build_dispatch_planner.gd` - pure animation/static wait, materialization and missing-model advance decision.
|
||||||
- `src/render/m2/m2_animation_load_pipeline_state.gd` - animated M2 threaded-load request records and completion-order finalize FIFO without I/O or Node ownership.
|
- `src/render/m2/m2_animation_load_pipeline_state.gd` - animated M2 threaded-load request records and completion-order finalize FIFO without I/O or Node ownership.
|
||||||
- `src/render/m2/m2_mesh_load_pipeline_state.gd` - static M2 threaded-load request records, terminal statuses and completion-order finalize FIFO without I/O or Mesh ownership.
|
- `src/render/m2/m2_mesh_load_pipeline_state.gd` - static M2 threaded-load request records, terminal statuses and completion-order finalize FIFO without I/O or Mesh ownership.
|
||||||
- `src/render/m2/m2_mesh_resource_cache_state.gd` - normalized-path prepared static M2 Mesh references with final-shutdown lifetime.
|
- `src/render/m2/m2_mesh_resource_cache_state.gd` - normalized-path prepared static M2 Mesh references with final-shutdown lifetime.
|
||||||
@@ -1058,6 +1059,18 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
|
|||||||
- Cache formats, quality profiles, batching output and visible rules are unchanged.
|
- Cache formats, quality profiles, batching output and visible rules are unchanged.
|
||||||
Asset-backed p95/p99 and spatial-cell batching evidence remain pending.
|
Asset-backed p95/p99 and spatial-cell batching evidence remain pending.
|
||||||
|
|
||||||
|
## 2026-07-18 M2 Build Dispatch Planner Extraction
|
||||||
|
|
||||||
|
- `M2BuildDispatchPlanner` now owns the pure action priority between pending
|
||||||
|
animation, animated materialization, pending/ready static Mesh and terminally
|
||||||
|
missing-model advancement.
|
||||||
|
- Pending animation still wins before batch planning. Unresolved static Meshes
|
||||||
|
still rotate and consume one `M2_BUILD` permit without advancing progress.
|
||||||
|
- Empty batches advance without serial change; animated, static and terminally
|
||||||
|
missing positive batches retain the historical serial increment.
|
||||||
|
- Resource lookup/request order, queue/cursor adoption, materialization, permits,
|
||||||
|
Node lifetime, cache formats, profiles and visible rules remain unchanged.
|
||||||
|
|
||||||
## 2026-07-17 M2 Runtime Mesh Rebuild Classifier Extraction
|
## 2026-07-17 M2 Runtime Mesh Rebuild Classifier Extraction
|
||||||
|
|
||||||
- `M2RuntimeMeshRebuildClassifier` now owns the memoized decision used when a
|
- `M2RuntimeMeshRebuildClassifier` now owns the memoized decision used when a
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
| M2 placement transform resolver | Implemented | [`m2-placement-transform-resolver.md`](m2-placement-transform-resolver.md) |
|
| M2 placement transform resolver | Implemented | [`m2-placement-transform-resolver.md`](m2-placement-transform-resolver.md) |
|
||||||
| M2 placement grouper | Implemented extraction | [`m2-placement-grouper.md`](m2-placement-grouper.md) |
|
| M2 placement grouper | Implemented extraction | [`m2-placement-grouper.md`](m2-placement-grouper.md) |
|
||||||
| M2 build batch planner | Implemented extraction | [`m2-build-batch-planner.md`](m2-build-batch-planner.md) |
|
| M2 build batch planner | Implemented extraction | [`m2-build-batch-planner.md`](m2-build-batch-planner.md) |
|
||||||
|
| M2 build dispatch planner | Implemented extraction | [`m2-build-dispatch-planner.md`](m2-build-dispatch-planner.md) |
|
||||||
| M2 build queue | Implemented extraction | [`m2-build-queue.md`](m2-build-queue.md) |
|
| M2 build queue | Implemented extraction | [`m2-build-queue.md`](m2-build-queue.md) |
|
||||||
| M2 static batch materializer | Implemented extraction | [`m2-static-batch-materializer.md`](m2-static-batch-materializer.md) |
|
| M2 static batch materializer | Implemented extraction | [`m2-static-batch-materializer.md`](m2-static-batch-materializer.md) |
|
||||||
| M2 runtime mesh rebuild classifier | Implemented extraction | [`m2-runtime-mesh-rebuild-classifier.md`](m2-runtime-mesh-rebuild-classifier.md) |
|
| M2 runtime mesh rebuild classifier | Implemented extraction | [`m2-runtime-mesh-rebuild-classifier.md`](m2-runtime-mesh-rebuild-classifier.md) |
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ flowchart LR
|
|||||||
Loader --> Planner
|
Loader --> Planner
|
||||||
Planner --> Plan[Detached batch plan]
|
Planner --> Plan[Detached batch plan]
|
||||||
Plan --> Loader
|
Plan --> Loader
|
||||||
Loader --> Ready{Resource ready?}
|
Loader --> Dispatch[M2BuildDispatchPlanner]
|
||||||
Ready --> Materialize[Animated or MultiMesh materialization]
|
Dispatch --> Materialize[Animated or MultiMesh materialization]
|
||||||
Loader --> Budget[RenderBudgetScheduler permit]
|
Loader --> Budget[RenderBudgetScheduler permit]
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -103,7 +103,8 @@ sequenceDiagram
|
|||||||
|
|
||||||
- The planner owns only call-local scalar values and the returned Dictionary.
|
- The planner owns only call-local scalar values and the returned Dictionary.
|
||||||
- `M2BuildQueue` owns typed jobs, FIFO ordering, serial numbers and group/offset
|
- `M2BuildQueue` owns typed jobs, FIFO ordering, serial numbers and group/offset
|
||||||
cursors. The loader owns tile checks, resource readiness/retry and adoption calls.
|
cursors. `M2BuildDispatchPlanner` owns the pure resource-state action decision;
|
||||||
|
the loader owns tile checks, resource observation/retry and adoption calls.
|
||||||
- Materializers own main-thread Node/MultiMesh construction under loader roots.
|
- Materializers own main-thread Node/MultiMesh construction under loader roots.
|
||||||
- The scheduler owns the frame-local `M2_BUILD` counter.
|
- The scheduler owns the frame-local `M2_BUILD` counter.
|
||||||
- Pure planning is thread-safe, though the current adapter calls it on main thread.
|
- Pure planning is thread-safe, though the current adapter calls it on main thread.
|
||||||
@@ -149,8 +150,8 @@ queue depth, build activity and hitch observability.
|
|||||||
|
|
||||||
## Extension points
|
## Extension points
|
||||||
|
|
||||||
- A later package may extract resource readiness/dispatch while retaining the
|
- A later package may extract resource observation/request orchestration while
|
||||||
typed build-job and FIFO contracts defined by `M2BuildQueue`.
|
retaining the dispatch and typed build-job/FIFO contracts.
|
||||||
- Spatial-cell batching must use measured culling/performance evidence and must
|
- Spatial-cell batching must use measured culling/performance evidence and must
|
||||||
not silently change this model-path batch cursor.
|
not silently change this model-path batch cursor.
|
||||||
|
|
||||||
@@ -160,7 +161,8 @@ queue depth, build activity and hitch observability.
|
|||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| Static/animated batch cursor planning | Implemented extraction | Contract/source/timing verifier | Asset-backed p95/p99 pending |
|
| Static/animated batch cursor planning | Implemented extraction | Contract/source/timing verifier | Asset-backed p95/p99 pending |
|
||||||
| Typed build queue/cursor state | Implemented extraction | M2 build queue lifecycle verifier | Asset-backed traversal pending |
|
| Typed build queue/cursor state | Implemented extraction | M2 build queue lifecycle verifier | Asset-backed traversal pending |
|
||||||
| Resource readiness/dispatch | Remains in loader | Existing lifecycle regressions | Stateful extraction pending |
|
| Resource dispatch decision | Implemented extraction | M2 build dispatch planner verifier | Asset-backed traversal pending |
|
||||||
|
| Resource observation/requests | Remains in loader | Existing lifecycle regressions | Stateful extraction pending |
|
||||||
| Spatial-cell batching | Planned | Renderer roadmap | Culling evidence/design pending |
|
| Spatial-cell batching | Planned | Renderer roadmap | Culling evidence/design pending |
|
||||||
|
|
||||||
## Known gaps and risks
|
## Known gaps and risks
|
||||||
@@ -176,6 +178,7 @@ queue depth, build activity and hitch observability.
|
|||||||
| Path | Responsibility |
|
| Path | Responsibility |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `src/render/m2/m2_build_batch_planner.gd` | Pure limit/count/cursor planning |
|
| `src/render/m2/m2_build_batch_planner.gd` | Pure limit/count/cursor planning |
|
||||||
|
| `src/render/m2/m2_build_dispatch_planner.gd` | Pure observed-state action/transition planning |
|
||||||
| `src/render/m2/m2_build_queue.gd` | Typed pending jobs, FIFO order and cursor ownership |
|
| `src/render/m2/m2_build_queue.gd` | Typed pending jobs, FIFO order and cursor ownership |
|
||||||
| `src/scenes/streaming/streaming_world_loader.gd` | Tile checks, resource readiness, progress adoption, materializer adapters and budgets |
|
| `src/scenes/streaming/streaming_world_loader.gd` | Tile checks, resource readiness, progress adoption, materializer adapters and budgets |
|
||||||
| `src/render/m2/m2_static_batch_materializer.gd` | Planned static-slice MultiMesh construction and attachment |
|
| `src/render/m2/m2_static_batch_materializer.gd` | Planned static-slice MultiMesh construction and attachment |
|
||||||
|
|||||||
@@ -0,0 +1,231 @@
|
|||||||
|
# M2 Build Dispatch Planner
|
||||||
|
|
||||||
|
## Metadata
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Status | Implemented extraction |
|
||||||
|
| Target/work package | M03 / `M03-RND-M2-BUILD-DISPATCH-PLANNER-001` |
|
||||||
|
| Owners | Pure M2 resource-state to build-action decision |
|
||||||
|
| Last verified | Worktree `work/sindo-main-codex/m03-m2-build-dispatch-planner`, 2026-07-18 |
|
||||||
|
| Profiles/capabilities | Existing static MultiMesh and animated-instance build paths |
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Select one M2 build action after the loader observes animation/static resource
|
||||||
|
state. The planner makes wait priority, materializer selection and the historical
|
||||||
|
missing-model serial transition explicit without owning resources or engine work.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Locate, request, load, cache or validate M2 resources.
|
||||||
|
- Size batches, own build jobs/queues or consume renderer permits.
|
||||||
|
- Create, attach or free Nodes, Meshes, MultiMeshes or animated instances.
|
||||||
|
- Change transforms, ordering, render settings, profiles or visible output.
|
||||||
|
- Add spatial-cell batching or M2 format/fidelity behavior.
|
||||||
|
|
||||||
|
## Context and boundaries
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
Loader[StreamingWorldLoader resource observations] --> Planner[M2BuildDispatchPlanner]
|
||||||
|
Planner --> Action[Detached action and transition plan]
|
||||||
|
Action --> Loader
|
||||||
|
Loader --> Queue[M2BuildQueue rotate/progress]
|
||||||
|
Loader --> Animated[M2AnimatedInstanceMaterializer]
|
||||||
|
Loader --> Static[M2StaticBatchMaterializer]
|
||||||
|
Loader --> Budget[RenderBudgetScheduler]
|
||||||
|
```
|
||||||
|
|
||||||
|
The planner depends only on `RefCounted`, scalar values, `StringName` constants
|
||||||
|
and a fresh Dictionary. ResourceLoader, caches, Nodes, Meshes, RenderingServer,
|
||||||
|
SceneTree, files, workers, mutexes, gameplay, network and Editor APIs are forbidden.
|
||||||
|
|
||||||
|
## Public API
|
||||||
|
|
||||||
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `ACTION_WAIT_FOR_ANIMATION` | Constant | Pending animation request blocks all other actions | Immutable | None |
|
||||||
|
| `ACTION_MATERIALIZE_ANIMATED` | Constant | Build the selected slice from animated prototype | Immutable | None |
|
||||||
|
| `ACTION_WAIT_FOR_STATIC_MESH` | Constant | Static resource is unresolved and queue must rotate | Immutable | None |
|
||||||
|
| `ACTION_MATERIALIZE_STATIC` | Constant | Build the selected slice from prepared static Mesh | Immutable | None |
|
||||||
|
| `ACTION_ADVANCE_WITHOUT_MATERIALIZATION` | Constant | Empty or terminally missing slice advances without a Node | Immutable | None |
|
||||||
|
| `plan_step(batch_count, animation_pending, has_animated, has_static_mesh, static_missing)` | Pure query | Return action, queue-rotation and serial-transition values | Any thread; call-local result | Raw booleans/count are accepted; non-positive count advances |
|
||||||
|
|
||||||
|
## Inputs and outputs
|
||||||
|
|
||||||
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| Input | Planned batch count | `M2BuildBatchPlanner` through loader | Dispatch planner | Integer copy | One call |
|
||||||
|
| Input | Animation request pending | Animation pipeline through loader | Dispatch planner | Boolean copy | One call |
|
||||||
|
| Input | Animated prototype available | Prototype cache/native loader adapter | Dispatch planner | Boolean copy; no Node reference | One call |
|
||||||
|
| Input | Static Mesh available | Mesh cache/request adapter | Dispatch planner | Boolean copy; no Mesh reference | One call |
|
||||||
|
| Input | Static model terminally missing | Prototype cache through loader | Dispatch planner | Boolean copy | One call |
|
||||||
|
| Output | Action | Dispatch planner | Loader materializer/wait branch | Immutable StringName | One operation |
|
||||||
|
| Output | `rotate_queue` | Dispatch planner | Loader queue adapter | Detached boolean | One operation |
|
||||||
|
| Output | `increment_batch_serial` | Dispatch planner | Loader progress adapter | Detached boolean | One operation |
|
||||||
|
|
||||||
|
The output Dictionary is newly allocated and caller-owned. The planner retains
|
||||||
|
no state, resource or engine-object reference.
|
||||||
|
|
||||||
|
## Data flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Input[Observed state] --> AnimationPending{Animation request pending?}
|
||||||
|
AnimationPending -->|yes| WaitAnimation[Wait animation; rotate]
|
||||||
|
AnimationPending -->|no| Positive{Batch count positive?}
|
||||||
|
Positive -->|no| AdvanceEmpty[Advance; keep serial]
|
||||||
|
Positive -->|yes| Animated{Animated prototype?}
|
||||||
|
Animated -->|yes| BuildAnimated[Materialize animated; increment serial]
|
||||||
|
Animated -->|no| StaticReady{Static Mesh ready?}
|
||||||
|
StaticReady -->|yes| BuildStatic[Materialize static; increment serial]
|
||||||
|
StaticReady -->|no| Missing{Model terminally missing?}
|
||||||
|
Missing -->|yes| AdvanceMissing[Advance without Node; increment serial]
|
||||||
|
Missing -->|no| WaitStatic[Wait static Mesh; rotate]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lifecycle/state
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> Observe
|
||||||
|
Observe --> Waiting: animation/static unresolved
|
||||||
|
Observe --> Materializing: animated/static resource ready
|
||||||
|
Observe --> Advancing: empty or terminally missing batch
|
||||||
|
Waiting --> [*]: detached wait plan
|
||||||
|
Materializing --> [*]: detached materialization plan
|
||||||
|
Advancing --> [*]: detached advance plan
|
||||||
|
```
|
||||||
|
|
||||||
|
The planner itself is stateless. Queue rotation, cursor adoption, cancellation,
|
||||||
|
map reset and shutdown remain external lifecycle transitions.
|
||||||
|
|
||||||
|
## Main sequence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant L as StreamingWorldLoader
|
||||||
|
participant B as M2BuildBatchPlanner
|
||||||
|
participant D as M2BuildDispatchPlanner
|
||||||
|
participant Q as M2BuildQueue
|
||||||
|
participant M as M2 materializer
|
||||||
|
participant S as RenderBudgetScheduler
|
||||||
|
L->>L: observe animation request/prototype
|
||||||
|
alt animation not pending
|
||||||
|
L->>B: plan_batch
|
||||||
|
L->>L: lookup/request static Mesh when required
|
||||||
|
end
|
||||||
|
L->>D: plan_step(observed scalar state)
|
||||||
|
D-->>L: action, rotate flag, serial flag
|
||||||
|
alt wait action
|
||||||
|
L->>Q: rotate_front
|
||||||
|
else materialize action
|
||||||
|
L->>M: materialize selected slice
|
||||||
|
L->>Q: adopt progress and serial
|
||||||
|
else advance action
|
||||||
|
L->>Q: adopt progress and optional serial
|
||||||
|
end
|
||||||
|
L->>S: consume one M2_BUILD permit
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependency diagram
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
Loader[StreamingWorldLoader] --> Dispatch[M2BuildDispatchPlanner]
|
||||||
|
Loader --> Batch[M2BuildBatchPlanner]
|
||||||
|
Loader --> Queue[M2BuildQueue]
|
||||||
|
Loader --> Caches[M2 cache/pipeline state]
|
||||||
|
Loader --> Materializers[M2 materializers]
|
||||||
|
Dispatch --> Values[Integers + booleans + StringName]
|
||||||
|
Dispatch -. no dependency .-> Engine[Node / Mesh / ResourceLoader]
|
||||||
|
Dispatch -. no dependency .-> State[Queue / cache / scheduler state]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ownership, threading and resources
|
||||||
|
|
||||||
|
- Planner owns call-local comparisons and the returned Dictionary only.
|
||||||
|
- Loader owns resource lookup/request order, action execution and permit use.
|
||||||
|
- `M2BuildQueue` owns pending jobs, FIFO keys and progress cursors.
|
||||||
|
- Materializers own main-thread scene construction under loader-owned roots.
|
||||||
|
- Pure calls are thread-safe; the current loader adapter calls on main thread.
|
||||||
|
|
||||||
|
## Errors, cancellation and recovery
|
||||||
|
|
||||||
|
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| Animation request pending | Boolean observation | Wait wins even if other flags are true | Priority fixture | Retry when queue returns front |
|
||||||
|
| Non-positive batch | Count comparison | Advance without serial increment | Zero/negative fixtures | Next group/cursor operation |
|
||||||
|
| Static Mesh unresolved | No Mesh and not missing | Rotate without serial increment | Retry fixture | Loader request/cache completes |
|
||||||
|
| Static model missing | Terminal missing flag | Advance without Node and increment serial | Missing fixture | Later group continues |
|
||||||
|
| Contradictory static ready/missing | Both true | Ready Mesh wins | Priority fixture | Loader normally prevents contradiction |
|
||||||
|
| Tile cancellation/root invalid | Outside planner | Loader cancels before dispatch | Queue/shutdown regressions | Eligible tile may requeue |
|
||||||
|
|
||||||
|
## Configuration and capabilities
|
||||||
|
|
||||||
|
The planner adds no setting. Existing animated/static batch limits, animation
|
||||||
|
enable flag, visibility/shadow settings and `M2_BUILD` permits remain external.
|
||||||
|
|
||||||
|
## Persistence, cache and migration
|
||||||
|
|
||||||
|
No state is serialized. No ADT/M2 cache format or version changes; no rebake or
|
||||||
|
migration is required.
|
||||||
|
|
||||||
|
## Diagnostics and observability
|
||||||
|
|
||||||
|
The planner emits no logs. Its verifier reports the action matrix, source
|
||||||
|
boundaries and bounded 20,000-call timing. Existing loader queue/hitch metrics
|
||||||
|
remain unchanged.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `verify_m2_build_dispatch_planner.gd` covers wait priority, zero/negative
|
||||||
|
counts, animated/static priority, unresolved retry, terminal missing, detached
|
||||||
|
results, loader/dependency boundaries and bounded timing.
|
||||||
|
- Queue, batch planner, materializers, caches, shutdown, facade, internal-access
|
||||||
|
and checkpoint tests protect adjacent behavior.
|
||||||
|
- Fidelity evidence is exact branch/transition extraction only. No private asset
|
||||||
|
or original-client visual comparison is claimed.
|
||||||
|
|
||||||
|
## Extension points
|
||||||
|
|
||||||
|
A later package may move resource observation/request execution behind a typed
|
||||||
|
service while preserving this scalar action contract. Generic callbacks, signals
|
||||||
|
and a shared state-machine framework remain intentionally excluded.
|
||||||
|
|
||||||
|
## Capability status
|
||||||
|
|
||||||
|
| Capability | Status | Evidence | Gap/next step |
|
||||||
|
|---|---|---|---|
|
||||||
|
| M2 build action selection | Implemented extraction | Priority/matrix/source/timing verifier | Asset-backed traversal pending |
|
||||||
|
| Resource observation and requests | Loader-owned | Cache/pipeline regressions | Typed orchestration extraction pending |
|
||||||
|
| Queue/cursor state | Implemented separately | M2 build queue verifier | Asset-backed traversal pending |
|
||||||
|
| Materialization | Implemented separately | Static/animated materializer verifiers | GPU/p95/p99 evidence pending |
|
||||||
|
|
||||||
|
## Known gaps and risks
|
||||||
|
|
||||||
|
- Planner consumes raw booleans rather than a typed resource snapshot.
|
||||||
|
- Loader still performs synchronous/native prototype attempts and static request I/O.
|
||||||
|
- Synthetic timing does not measure ResourceLoader, Node or GPU work.
|
||||||
|
- Private traversal, leak, visual and p95/p99 evidence remains unavailable.
|
||||||
|
|
||||||
|
## Source map
|
||||||
|
|
||||||
|
| Path | Responsibility |
|
||||||
|
|---|---|
|
||||||
|
| `src/render/m2/m2_build_dispatch_planner.gd` | Pure action priority and transition plan |
|
||||||
|
| `src/scenes/streaming/streaming_world_loader.gd` | Resource observation, action execution, permits and engine lifetime |
|
||||||
|
| `src/render/m2/m2_build_batch_planner.gd` | Batch count and cursor plan |
|
||||||
|
| `src/render/m2/m2_build_queue.gd` | Pending jobs, FIFO and cursor ownership |
|
||||||
|
| `src/tools/verify_m2_build_dispatch_planner.gd` | Matrix, boundary and timing regression |
|
||||||
|
|
||||||
|
## Related decisions and references
|
||||||
|
|
||||||
|
- [`m2-build-batch-planner.md`](m2-build-batch-planner.md)
|
||||||
|
- [`m2-build-queue.md`](m2-build-queue.md)
|
||||||
|
- [`m2-static-batch-materializer.md`](m2-static-batch-materializer.md)
|
||||||
|
- [`m2-animated-instance-materializer.md`](m2-animated-instance-materializer.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)
|
||||||
@@ -33,6 +33,7 @@ flowchart LR
|
|||||||
Queue --> Job[M2BuildJob]
|
Queue --> Job[M2BuildJob]
|
||||||
Queue --> Loader
|
Queue --> Loader
|
||||||
Loader --> Planner[M2BuildBatchPlanner]
|
Loader --> Planner[M2BuildBatchPlanner]
|
||||||
|
Loader --> Dispatch[M2BuildDispatchPlanner]
|
||||||
Loader --> Static[M2StaticBatchMaterializer]
|
Loader --> Static[M2StaticBatchMaterializer]
|
||||||
Loader --> Animated[M2AnimatedInstanceMaterializer]
|
Loader --> Animated[M2AnimatedInstanceMaterializer]
|
||||||
Loader --> Scheduler[RenderBudgetScheduler]
|
Loader --> Scheduler[RenderBudgetScheduler]
|
||||||
@@ -171,6 +172,8 @@ flowchart TB
|
|||||||
- Job retains the exact groups Dictionary, a fresh group-key snapshot and root.
|
- Job retains the exact groups Dictionary, a fresh group-key snapshot and root.
|
||||||
- Root ownership remains with the loader/tile SceneTree; queue release never frees it.
|
- Root ownership remains with the loader/tile SceneTree; queue release never frees it.
|
||||||
- Loader validates `is_instance_valid`, frees empty/aborted roots and mutates tile state.
|
- Loader validates `is_instance_valid`, frees empty/aborted roots and mutates tile state.
|
||||||
|
- `M2BuildDispatchPlanner` selects wait/materializer/advance actions without
|
||||||
|
borrowing queue-owned engine references.
|
||||||
- All current operations run on the renderer main thread; no mutex is required.
|
- All current operations run on the renderer main thread; no mutex is required.
|
||||||
- Group worker results cross their existing mutex mailbox before enqueue.
|
- Group worker results cross their existing mutex mailbox before enqueue.
|
||||||
|
|
||||||
@@ -229,7 +232,8 @@ queue base, signals and callbacks are intentionally excluded.
|
|||||||
| Typed keyed M2 jobs and FIFO | Implemented extraction | Lifecycle/order/source/timing verifier | Asset-backed traversal pending |
|
| Typed keyed M2 jobs and FIFO | Implemented extraction | Lifecycle/order/source/timing verifier | Asset-backed traversal pending |
|
||||||
| Cursor/serial ownership | Implemented extraction | Atomic progress fixtures | Typed batch-plan result remains Dictionary |
|
| Cursor/serial ownership | Implemented extraction | Atomic progress fixtures | Typed batch-plan result remains Dictionary |
|
||||||
| Root destruction | Existing loader-owned | Lifetime/source/shutdown regressions | Keep outside state service |
|
| Root destruction | Existing loader-owned | Lifetime/source/shutdown regressions | Keep outside state service |
|
||||||
| Resource readiness/dispatch | Existing loader-owned | Adjacent cache/build tests | Safe extraction remains |
|
| Resource dispatch decision | Implemented separately | Dispatch planner verifier | Asset-backed traversal pending |
|
||||||
|
| Resource observation/requests | Existing loader-owned | Adjacent cache/build tests | Safe extraction remains |
|
||||||
|
|
||||||
## Known gaps and risks
|
## Known gaps and risks
|
||||||
|
|
||||||
@@ -246,11 +250,13 @@ queue base, signals and callbacks are intentionally excluded.
|
|||||||
| `src/render/m2/m2_build_queue.gd` | Keyed job ownership, FIFO/stale/rotation lifecycle and diagnostics |
|
| `src/render/m2/m2_build_queue.gd` | Keyed job ownership, FIFO/stale/rotation lifecycle and diagnostics |
|
||||||
| `src/scenes/streaming/streaming_world_loader.gd` | Eligibility, readiness, permits, materialization, tile state and root cleanup |
|
| `src/scenes/streaming/streaming_world_loader.gd` | Eligibility, readiness, permits, materialization, tile state and root cleanup |
|
||||||
| `src/render/m2/m2_build_batch_planner.gd` | Batch count and cursor-plan calculation |
|
| `src/render/m2/m2_build_batch_planner.gd` | Batch count and cursor-plan calculation |
|
||||||
|
| `src/render/m2/m2_build_dispatch_planner.gd` | Resource-state action and transition planning |
|
||||||
| `src/tools/verify_m2_build_queue.gd` | Lifecycle/order/lifetime/boundary/timing regression |
|
| `src/tools/verify_m2_build_queue.gd` | Lifecycle/order/lifetime/boundary/timing regression |
|
||||||
|
|
||||||
## Related decisions and references
|
## Related decisions and references
|
||||||
|
|
||||||
- [`m2-build-batch-planner.md`](m2-build-batch-planner.md)
|
- [`m2-build-batch-planner.md`](m2-build-batch-planner.md)
|
||||||
|
- [`m2-build-dispatch-planner.md`](m2-build-dispatch-planner.md)
|
||||||
- [`m2-static-batch-materializer.md`](m2-static-batch-materializer.md)
|
- [`m2-static-batch-materializer.md`](m2-static-batch-materializer.md)
|
||||||
- [`m2-animated-instance-materializer.md`](m2-animated-instance-materializer.md)
|
- [`m2-animated-instance-materializer.md`](m2-animated-instance-materializer.md)
|
||||||
- [`world-renderer.md`](world-renderer.md)
|
- [`world-renderer.md`](world-renderer.md)
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ flowchart LR
|
|||||||
M2Transform --> Loader
|
M2Transform --> Loader
|
||||||
Loader --> M2Batch[M2BuildBatchPlanner]
|
Loader --> M2Batch[M2BuildBatchPlanner]
|
||||||
M2Batch --> Loader
|
M2Batch --> Loader
|
||||||
|
Loader --> M2Dispatch[M2BuildDispatchPlanner]
|
||||||
|
M2Dispatch --> Loader
|
||||||
Loader --> M2Queue[M2BuildQueue]
|
Loader --> M2Queue[M2BuildQueue]
|
||||||
M2Queue --> Loader
|
M2Queue --> Loader
|
||||||
Loader --> M2Static[M2StaticBatchMaterializer]
|
Loader --> M2Static[M2StaticBatchMaterializer]
|
||||||
@@ -136,6 +138,7 @@ from externally reading/writing loader-private queue, task, cache and tile-state
|
|||||||
| `M2PlacementTransformResolver.resolve_basis/resolve_origin_offset` | Internal pure M2 service | Resolves regular and calibrated model-specific ADT placement transforms | Worker/main thread; stateless | Unknown paths use regular basis and zero offset |
|
| `M2PlacementTransformResolver.resolve_basis/resolve_origin_offset` | Internal pure M2 service | Resolves regular and calibrated model-specific ADT placement transforms | Worker/main thread; stateless | Unknown paths use regular basis and zero offset |
|
||||||
| `M2PlacementGrouper.group_placements` | Internal pure M2 service | Validates and groups ordered tile-local placement transforms by normalized path | Worker/main thread; stateless | Invalid variants/name IDs/empty paths are skipped |
|
| `M2PlacementGrouper.group_placements` | Internal pure M2 service | Validates and groups ordered tile-local placement transforms by normalized path | Worker/main thread; stateless | Invalid variants/name IDs/empty paths are skipped |
|
||||||
| `M2BuildBatchPlanner.plan_batch` | Internal pure M2 service | Selects static/animated batch count and next group cursor | Main/any thread; stateless | Non-positive selected limit clamps to one; empty range completes |
|
| `M2BuildBatchPlanner.plan_batch` | Internal pure M2 service | Selects static/animated batch count and next group cursor | Main/any thread; stateless | Non-positive selected limit clamps to one; empty range completes |
|
||||||
|
| `M2BuildDispatchPlanner.plan_step` | Internal pure M2 service | Selects wait, animated/static materialization or no-Node advancement from observed resource state | Main/any thread; stateless | Pending animation has priority; unresolved static Mesh waits |
|
||||||
| `M2BuildQueue` / `M2BuildJob` | Internal M2 pending-state service | Own typed root/groups/cursor jobs and FIFO/stale tile keys | Renderer main thread; map session | Invalid enqueue rejected; stale keys drain independently of jobs |
|
| `M2BuildQueue` / `M2BuildJob` | Internal M2 pending-state service | Own typed root/groups/cursor jobs and FIFO/stale tile keys | Renderer main thread; map session | Invalid enqueue rejected; stale keys drain independently of jobs |
|
||||||
| `M2StaticBatchMaterializer.materialize_batch` | Internal M2 scene-materialization service | Builds and attaches one prepared-Mesh MultiMesh transform slice | Renderer main thread; stateless after each call | Invalid/empty input returns null; bounds are caller precondition |
|
| `M2StaticBatchMaterializer.materialize_batch` | Internal M2 scene-materialization service | Builds and attaches one prepared-Mesh MultiMesh transform slice | Renderer main thread; stateless after each call | Invalid/empty input returns null; bounds are caller precondition |
|
||||||
| `M2RuntimeMeshRebuildClassifier` | Internal memoized M2 service | Detects billboard/UV-rotation metadata requiring stale cached-mesh rebuild | Renderer main thread; cached until reset | Invalid variants/indices skipped; first path decision wins |
|
| `M2RuntimeMeshRebuildClassifier` | Internal memoized M2 service | Detects billboard/UV-rotation metadata requiring stale cached-mesh rebuild | Renderer main thread; cached until reset | Invalid variants/indices skipped; first path decision wins |
|
||||||
@@ -185,6 +188,7 @@ loader configuration remains transitional composition data, not a caller API.
|
|||||||
| Internal transform | Rotation/path/scale | Loader or grouper / `M2PlacementTransformResolver` | Group/placeholder/instance transforms | Value-only Basis/Vector3 | One placement |
|
| Internal transform | Rotation/path/scale | Loader or grouper / `M2PlacementTransformResolver` | Group/placeholder/instance transforms | Value-only Basis/Vector3 | One placement |
|
||||||
| Internal grouping | Tile origin, M2 names and placements | Loader / `M2PlacementGrouper` | Loader worker result/build job | Fresh Dictionary/Transform3D arrays | One grouping task |
|
| Internal grouping | Tile origin, M2 names and placements | Loader / `M2PlacementGrouper` | Loader worker result/build job | Fresh Dictionary/Transform3D arrays | One grouping task |
|
||||||
| Internal batch plan | Transform count/offset, path kind and limits | Loader / `M2BuildBatchPlanner` | Loader materialization/cursor adapter | Fresh scalar Dictionary | One build operation |
|
| Internal batch plan | Transform count/offset, path kind and limits | Loader / `M2BuildBatchPlanner` | Loader materialization/cursor adapter | Fresh scalar Dictionary | One build operation |
|
||||||
|
| Internal M2 dispatch plan | Batch count and observed animation/static resource state | Loader / `M2BuildDispatchPlanner` | Loader queue/materializer/progress adapter | Fresh scalar/action Dictionary | One build operation |
|
||||||
| Internal M2 pending build | Tile key, M2 root, grouped transforms and cursors | Loader / `M2BuildQueue` | Loader readiness, planner and materializer adapters | Queue-owned job/keys and strong references | Until finish/cancel/clear/replacement |
|
| Internal M2 pending build | Tile key, M2 root, grouped transforms and cursors | Loader / `M2BuildQueue` | Loader readiness, planner and materializer adapters | Queue-owned job/keys and strong references | Until finish/cancel/clear/replacement |
|
||||||
| Internal static M2 materialization | Parent, prepared Mesh, ordered transform slice and render settings | Loader / `M2StaticBatchMaterializer` | Attached MultiMeshInstance3D | Parent owns node/MultiMesh; exact Mesh reference retained | One main-thread build batch |
|
| Internal static M2 materialization | Parent, prepared Mesh, ordered transform slice and render settings | Loader / `M2StaticBatchMaterializer` | Attached MultiMeshInstance3D | Parent owns node/MultiMesh; exact Mesh reference retained | One main-thread build batch |
|
||||||
| Internal WMO placement | Path, MODF placement, tile/index | Loader / `WmoPlacementResolver` | WMO caches, registry and three instance adapters | Value-only String/Transform3D | Lookup/placement lifetime |
|
| Internal WMO placement | Path, MODF placement, tile/index | Loader / `WmoPlacementResolver` | WMO caches, registry and three instance adapters | Value-only String/Transform3D | Lookup/placement lifetime |
|
||||||
@@ -249,7 +253,8 @@ flowchart TD
|
|||||||
M2Grouper --> M2Batch[M2BuildBatchPlanner]
|
M2Grouper --> M2Batch[M2BuildBatchPlanner]
|
||||||
M2Grouper --> M2Queue[M2BuildQueue]
|
M2Grouper --> M2Queue[M2BuildQueue]
|
||||||
M2Queue --> M2Batch
|
M2Queue --> M2Batch
|
||||||
M2Batch --> M2Static[M2StaticBatchMaterializer]
|
M2Batch --> M2Dispatch[M2BuildDispatchPlanner]
|
||||||
|
M2Dispatch --> M2Static[M2StaticBatchMaterializer]
|
||||||
M2Static --> M2
|
M2Static --> M2
|
||||||
R --> WmoPlacement[WmoPlacementResolver]
|
R --> WmoPlacement[WmoPlacementResolver]
|
||||||
WmoPlacement --> WmoRegistry[WmoPlacementRegistry]
|
WmoPlacement --> WmoRegistry[WmoPlacementRegistry]
|
||||||
@@ -377,11 +382,13 @@ sequenceDiagram
|
|||||||
The loader retains tasks, mutex/result queues and stale-result checks; accepted
|
The loader retains tasks, mutex/result queues and stale-result checks; accepted
|
||||||
groups enter `M2BuildQueue` as typed pending jobs.
|
groups enter `M2BuildQueue` as typed pending jobs.
|
||||||
- `M2BuildBatchPlanner` is stateless and owns only call-local scalar plans.
|
- `M2BuildBatchPlanner` is stateless and owns only call-local scalar plans.
|
||||||
|
`M2BuildDispatchPlanner` is stateless and owns only call-local action/transition
|
||||||
|
plans after the loader observes resource availability.
|
||||||
`M2BuildQueue` owns typed pending jobs, FIFO/stale keys, grouped-transform
|
`M2BuildQueue` owns typed pending jobs, FIFO/stale keys, grouped-transform
|
||||||
references and group/offset/serial cursors without freeing engine objects.
|
references and group/offset/serial cursors without freeing engine objects.
|
||||||
`M2StaticBatchMaterializer` owns static MultiMesh construction/attachment;
|
`M2StaticBatchMaterializer` owns static MultiMesh construction/attachment;
|
||||||
the loader retains resource transitions, cursor-adoption decisions,
|
the loader retains resource observation/requests, action execution,
|
||||||
animated/static dispatch, root cleanup, budgets and Editor ownership.
|
cursor adoption, root cleanup, budgets and Editor ownership.
|
||||||
- `WmoPlacementResolver` is stateless and owns only call-local cache-key,
|
- `WmoPlacementResolver` is stateless and owns only call-local cache-key,
|
||||||
identity and transform values. `WmoPlacementRegistry` owns only placement-key
|
identity and transform values. `WmoPlacementRegistry` owns only placement-key
|
||||||
reference sets. `WmoRenderBuildStepPlanner` owns only a call-local operation
|
reference sets. `WmoRenderBuildStepPlanner` owns only a call-local operation
|
||||||
@@ -562,7 +569,8 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
|||||||
| M2 unique placement registry | Implemented extraction | Scene-free ownership/lifecycle/timing contract and historical `uid:11785` smoke | Group/build/tasks/finalization and asset-backed p95/p99 remain pending |
|
| M2 unique placement registry | Implemented extraction | Scene-free ownership/lifecycle/timing contract and historical `uid:11785` smoke | Group/build/tasks/finalization and asset-backed p95/p99 remain pending |
|
||||||
| M2 placement transform resolver | Implemented extraction | Scene-free formula/source/timing contract across three consumers | Asset-backed visual recheck and general placement parity pending |
|
| M2 placement transform resolver | Implemented extraction | Scene-free formula/source/timing contract across three consumers | Asset-backed visual recheck and general placement parity pending |
|
||||||
| M2 placement grouper | Implemented extraction | Scene-free validation/order/transform/source/timing contract | Worker/build state, spatial cells and asset-backed p95/p99 remain pending |
|
| M2 placement grouper | Implemented extraction | Scene-free validation/order/transform/source/timing contract | Worker/build state, spatial cells and asset-backed p95/p99 remain pending |
|
||||||
| M2 build batch planner | Implemented extraction | Scene-free limit/count/cursor/source/timing contract | Queue/resource state, spatial cells and asset-backed p95/p99 remain pending |
|
| M2 build batch planner | Implemented extraction | Scene-free limit/count/cursor/source/timing contract | Spatial cells and asset-backed p95/p99 remain pending |
|
||||||
|
| M2 build dispatch planner | Implemented extraction | Scene-free priority/action/transition/source/timing contract | Resource observation/orchestration and asset-backed traversal remain pending |
|
||||||
| M2 build queue | Implemented extraction | Typed lifecycle/FIFO/progress/lifetime/source/timing contract | Resource dispatch and asset-backed traversal remain pending |
|
| M2 build queue | Implemented extraction | Typed lifecycle/FIFO/progress/lifetime/source/timing contract | Resource dispatch and asset-backed traversal remain pending |
|
||||||
| M2 animation load pipeline state | Implemented extraction | Synthetic lifecycle/FIFO/source/timing contract | Asset-backed traversal/leak/animation-fidelity/p95/p99 pending |
|
| M2 animation load pipeline state | Implemented extraction | Synthetic lifecycle/FIFO/source/timing contract | Asset-backed traversal/leak/animation-fidelity/p95/p99 pending |
|
||||||
| M2 animated scene finalizer | Implemented extraction | Synthetic type/lifetime/material/player/source/timing contract | Asset-backed GLB traversal/material/animation comparison pending |
|
| M2 animated scene finalizer | Implemented extraction | Synthetic type/lifetime/material/player/source/timing contract | Asset-backed GLB traversal/material/animation comparison pending |
|
||||||
@@ -634,6 +642,7 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
|||||||
| `src/render/m2/m2_animation_playback_controller.gd` | Per-instance phase, selection, native copy/start and imported playback |
|
| `src/render/m2/m2_animation_playback_controller.gd` | Per-instance phase, selection, native copy/start and imported playback |
|
||||||
| `src/render/m2/m2_animated_instance_materializer.gd` | Animated duplicate/render/playback startup and non-empty batch attachment |
|
| `src/render/m2/m2_animated_instance_materializer.gd` | Animated duplicate/render/playback startup and non-empty batch attachment |
|
||||||
| `src/render/m2/m2_static_batch_materializer.gd` | Static MultiMesh construction, render setup and attachment |
|
| `src/render/m2/m2_static_batch_materializer.gd` | Static MultiMesh construction, render setup and attachment |
|
||||||
|
| `src/render/m2/m2_build_dispatch_planner.gd` | M2 wait/materializer/advance action priority and transition plan |
|
||||||
| `src/render/m2/m2_build_job.gd` | M2 root/groups references and group/offset/serial progress |
|
| `src/render/m2/m2_build_job.gd` | M2 root/groups references and group/offset/serial progress |
|
||||||
| `src/render/m2/m2_build_queue.gd` | Keyed pending jobs and FIFO/stale tile-key lifecycle |
|
| `src/render/m2/m2_build_queue.gd` | Keyed pending jobs and FIFO/stale tile-key lifecycle |
|
||||||
| `src/render/m2/m2_mesh_load_pipeline_state.gd` | Static M2 pending Resource paths, terminal statuses and finalize FIFO |
|
| `src/render/m2/m2_mesh_load_pipeline_state.gd` | Static M2 pending Resource paths, terminal statuses and finalize FIFO |
|
||||||
@@ -662,6 +671,7 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
|||||||
| `src/tools/verify_m2_animation_playback_controller.gd` | Animated M2 phase/selection/playback/native/boundary/timing regression |
|
| `src/tools/verify_m2_animation_playback_controller.gd` | Animated M2 phase/selection/playback/native/boundary/timing regression |
|
||||||
| `src/tools/verify_m2_animated_instance_materializer.gd` | Animated M2 order/render/playback/attachment/boundary/timing regression |
|
| `src/tools/verify_m2_animated_instance_materializer.gd` | Animated M2 order/render/playback/attachment/boundary/timing regression |
|
||||||
| `src/tools/verify_m2_static_batch_materializer.gd` | Static M2 node/Mesh/render/attachment/boundary/timing regression |
|
| `src/tools/verify_m2_static_batch_materializer.gd` | Static M2 node/Mesh/render/attachment/boundary/timing regression |
|
||||||
|
| `src/tools/verify_m2_build_dispatch_planner.gd` | M2 dispatch priority/action/transition/boundary/timing regression |
|
||||||
| `src/tools/verify_m2_build_queue.gd` | M2 job/FIFO/progress/lifetime/boundary/timing regression |
|
| `src/tools/verify_m2_build_queue.gd` | M2 job/FIFO/progress/lifetime/boundary/timing regression |
|
||||||
| `src/tools/verify_m2_mesh_load_pipeline_state.gd` | Static M2 request/terminal/FIFO/boundary/timing regression |
|
| `src/tools/verify_m2_mesh_load_pipeline_state.gd` | Static M2 request/terminal/FIFO/boundary/timing regression |
|
||||||
| `src/tools/verify_m2_mesh_resource_cache_state.gd` | Static M2 Mesh cache ownership/lifetime/boundary/timing regression |
|
| `src/tools/verify_m2_mesh_resource_cache_state.gd` | Static M2 Mesh cache ownership/lifetime/boundary/timing regression |
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
class_name M2BuildDispatchPlanner
|
||||||
|
extends RefCounted
|
||||||
|
|
||||||
|
## Selects the next M2 build action from already observed resource state.
|
||||||
|
## The planner retains no state or engine-object references.
|
||||||
|
|
||||||
|
const ACTION_WAIT_FOR_ANIMATION := &"wait_for_animation"
|
||||||
|
const ACTION_MATERIALIZE_ANIMATED := &"materialize_animated"
|
||||||
|
const ACTION_WAIT_FOR_STATIC_MESH := &"wait_for_static_mesh"
|
||||||
|
const ACTION_MATERIALIZE_STATIC := &"materialize_static"
|
||||||
|
const ACTION_ADVANCE_WITHOUT_MATERIALIZATION := &"advance_without_materialization"
|
||||||
|
|
||||||
|
|
||||||
|
## Returns a detached action/transition plan for one M2 build operation.
|
||||||
|
func plan_step(
|
||||||
|
batch_count: int,
|
||||||
|
animation_request_pending: bool,
|
||||||
|
has_animated_prototype: bool,
|
||||||
|
has_static_mesh: bool,
|
||||||
|
static_model_missing: bool
|
||||||
|
) -> Dictionary:
|
||||||
|
if animation_request_pending:
|
||||||
|
return _wait_plan(ACTION_WAIT_FOR_ANIMATION)
|
||||||
|
if batch_count <= 0:
|
||||||
|
return _advance_plan(ACTION_ADVANCE_WITHOUT_MATERIALIZATION, false)
|
||||||
|
if has_animated_prototype:
|
||||||
|
return _advance_plan(ACTION_MATERIALIZE_ANIMATED, true)
|
||||||
|
if has_static_mesh:
|
||||||
|
return _advance_plan(ACTION_MATERIALIZE_STATIC, true)
|
||||||
|
if static_model_missing:
|
||||||
|
return _advance_plan(ACTION_ADVANCE_WITHOUT_MATERIALIZATION, true)
|
||||||
|
return _wait_plan(ACTION_WAIT_FOR_STATIC_MESH)
|
||||||
|
|
||||||
|
|
||||||
|
func _wait_plan(action: StringName) -> Dictionary:
|
||||||
|
return {
|
||||||
|
"action": action,
|
||||||
|
"rotate_queue": true,
|
||||||
|
"increment_batch_serial": false,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func _advance_plan(action: StringName, increment_batch_serial: bool) -> Dictionary:
|
||||||
|
return {
|
||||||
|
"action": action,
|
||||||
|
"rotate_queue": false,
|
||||||
|
"increment_batch_serial": increment_batch_serial,
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://df0bv1x2x4j2x
|
||||||
@@ -60,6 +60,9 @@ const M2_PLACEMENT_GROUPER_SCRIPT := preload(
|
|||||||
const M2_BUILD_BATCH_PLANNER_SCRIPT := preload(
|
const M2_BUILD_BATCH_PLANNER_SCRIPT := preload(
|
||||||
"res://src/render/m2/m2_build_batch_planner.gd"
|
"res://src/render/m2/m2_build_batch_planner.gd"
|
||||||
)
|
)
|
||||||
|
const M2_BUILD_DISPATCH_PLANNER_SCRIPT := preload(
|
||||||
|
"res://src/render/m2/m2_build_dispatch_planner.gd"
|
||||||
|
)
|
||||||
const M2_BUILD_QUEUE_SCRIPT := preload("res://src/render/m2/m2_build_queue.gd")
|
const M2_BUILD_QUEUE_SCRIPT := preload("res://src/render/m2/m2_build_queue.gd")
|
||||||
const M2_STATIC_BATCH_MATERIALIZER_SCRIPT := preload(
|
const M2_STATIC_BATCH_MATERIALIZER_SCRIPT := preload(
|
||||||
"res://src/render/m2/m2_static_batch_materializer.gd"
|
"res://src/render/m2/m2_static_batch_materializer.gd"
|
||||||
@@ -270,6 +273,7 @@ var _m2_group_tasks: Dictionary = {}
|
|||||||
var _m2_group_result_mutex := Mutex.new()
|
var _m2_group_result_mutex := Mutex.new()
|
||||||
var _m2_group_result_queue: Array = []
|
var _m2_group_result_queue: Array = []
|
||||||
var _m2_build_queue_state := M2_BUILD_QUEUE_SCRIPT.new()
|
var _m2_build_queue_state := M2_BUILD_QUEUE_SCRIPT.new()
|
||||||
|
var _m2_build_dispatch_planner := M2_BUILD_DISPATCH_PLANNER_SCRIPT.new()
|
||||||
var _m2_unique_placement_registry := (
|
var _m2_unique_placement_registry := (
|
||||||
M2_UNIQUE_PLACEMENT_REGISTRY_SCRIPT.new()
|
M2_UNIQUE_PLACEMENT_REGISTRY_SCRIPT.new()
|
||||||
)
|
)
|
||||||
@@ -4342,32 +4346,72 @@ func _process_m2_build_jobs() -> void:
|
|||||||
animated_prototype = _get_or_load_m2_native_animated_prototype(rel_path)
|
animated_prototype = _get_or_load_m2_native_animated_prototype(rel_path)
|
||||||
if animated_prototype == null:
|
if animated_prototype == null:
|
||||||
animated_prototype = _get_or_load_m2_animated_prototype(rel_path)
|
animated_prototype = _get_or_load_m2_animated_prototype(rel_path)
|
||||||
if enable_m2_animated_instances and _m2_animation_load_pipeline_state.has_request(normalized_rel):
|
var animation_request_pending := (
|
||||||
_m2_build_queue_state.rotate_front()
|
enable_m2_animated_instances
|
||||||
_render_budget_scheduler.try_consume_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.M2_BUILD)
|
and _m2_animation_load_pipeline_state.has_request(normalized_rel)
|
||||||
continue
|
|
||||||
var batch_plan: Dictionary = _m2_build_batch_planner.plan_batch(
|
|
||||||
transforms.size(),
|
|
||||||
offset,
|
|
||||||
animated_prototype != null,
|
|
||||||
m2_animated_instances_per_tick,
|
|
||||||
m2_multimesh_batch_size
|
|
||||||
)
|
)
|
||||||
var batch_count: int = int(batch_plan["batch_count"])
|
var batch_plan: Dictionary = {}
|
||||||
|
var batch_count := 0
|
||||||
|
var static_mesh: Mesh = null
|
||||||
|
var static_model_missing := false
|
||||||
|
if not animation_request_pending:
|
||||||
|
batch_plan = _m2_build_batch_planner.plan_batch(
|
||||||
|
transforms.size(),
|
||||||
|
offset,
|
||||||
|
animated_prototype != null,
|
||||||
|
m2_animated_instances_per_tick,
|
||||||
|
m2_multimesh_batch_size
|
||||||
|
)
|
||||||
|
batch_count = int(batch_plan["batch_count"])
|
||||||
|
if batch_count > 0 and animated_prototype == null:
|
||||||
|
static_mesh = _get_m2_mesh_or_request(rel_path)
|
||||||
|
static_model_missing = (
|
||||||
|
static_mesh == null
|
||||||
|
and _m2_prototype_cache_state.is_model_missing(normalized_rel)
|
||||||
|
)
|
||||||
|
var dispatch_plan: Dictionary = _m2_build_dispatch_planner.plan_step(
|
||||||
|
batch_count,
|
||||||
|
animation_request_pending,
|
||||||
|
animated_prototype != null,
|
||||||
|
static_mesh != null,
|
||||||
|
static_model_missing
|
||||||
|
)
|
||||||
|
if bool(dispatch_plan["rotate_queue"]):
|
||||||
|
_m2_build_queue_state.rotate_front()
|
||||||
|
_render_budget_scheduler.try_consume_permit(
|
||||||
|
RENDER_BUDGET_SCHEDULER_SCRIPT.M2_BUILD
|
||||||
|
)
|
||||||
|
continue
|
||||||
var serial := _m2_build_queue_state.batch_serial_for(key)
|
var serial := _m2_build_queue_state.batch_serial_for(key)
|
||||||
var next_serial := serial
|
var next_serial := serial
|
||||||
if batch_count > 0:
|
var dispatch_action: StringName = dispatch_plan["action"]
|
||||||
if animated_prototype != null:
|
if (
|
||||||
_materialize_m2_animated_batch(root as Node3D, rel_path, animated_prototype, transforms, offset, batch_count, serial)
|
dispatch_action
|
||||||
else:
|
== M2_BUILD_DISPATCH_PLANNER_SCRIPT.ACTION_MATERIALIZE_ANIMATED
|
||||||
var mesh := _get_m2_mesh_or_request(rel_path)
|
):
|
||||||
if mesh == null:
|
_materialize_m2_animated_batch(
|
||||||
if not _m2_prototype_cache_state.is_model_missing(normalized_rel):
|
root as Node3D,
|
||||||
_m2_build_queue_state.rotate_front()
|
rel_path,
|
||||||
_render_budget_scheduler.try_consume_permit(RENDER_BUDGET_SCHEDULER_SCRIPT.M2_BUILD)
|
animated_prototype,
|
||||||
continue
|
transforms,
|
||||||
else:
|
offset,
|
||||||
_materialize_m2_group_batch(root as Node3D, rel_path, mesh, transforms, offset, batch_count, serial)
|
batch_count,
|
||||||
|
serial
|
||||||
|
)
|
||||||
|
elif (
|
||||||
|
dispatch_action
|
||||||
|
== M2_BUILD_DISPATCH_PLANNER_SCRIPT.ACTION_MATERIALIZE_STATIC
|
||||||
|
):
|
||||||
|
_materialize_m2_group_batch(
|
||||||
|
root as Node3D,
|
||||||
|
rel_path,
|
||||||
|
static_mesh,
|
||||||
|
transforms,
|
||||||
|
offset,
|
||||||
|
batch_count,
|
||||||
|
serial
|
||||||
|
)
|
||||||
|
if bool(dispatch_plan["increment_batch_serial"]):
|
||||||
next_serial = serial + 1
|
next_serial = serial + 1
|
||||||
var next_group_index := index
|
var next_group_index := index
|
||||||
var next_transform_offset := offset
|
var next_transform_offset := offset
|
||||||
|
|||||||
@@ -0,0 +1,225 @@
|
|||||||
|
extends SceneTree
|
||||||
|
|
||||||
|
## Asset-free M2 build dispatch priority, transition, source-boundary and
|
||||||
|
## bounded-timing regression.
|
||||||
|
|
||||||
|
const PLANNER_SCRIPT := preload(
|
||||||
|
"res://src/render/m2/m2_build_dispatch_planner.gd"
|
||||||
|
)
|
||||||
|
const PLANNER_PATH := "res://src/render/m2/m2_build_dispatch_planner.gd"
|
||||||
|
const LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd"
|
||||||
|
|
||||||
|
|
||||||
|
func _initialize() -> void:
|
||||||
|
var failures: Array[String] = []
|
||||||
|
_verify_wait_and_empty_actions(failures)
|
||||||
|
_verify_materialization_priority(failures)
|
||||||
|
_verify_static_retry_and_missing_actions(failures)
|
||||||
|
_verify_fresh_results(failures)
|
||||||
|
_verify_loader_and_dependency_boundaries(failures)
|
||||||
|
var elapsed_milliseconds := _verify_bounded_timing(failures)
|
||||||
|
if not failures.is_empty():
|
||||||
|
for failure in failures:
|
||||||
|
push_error("M2_BUILD_DISPATCH_PLANNER: %s" % failure)
|
||||||
|
quit(1)
|
||||||
|
return
|
||||||
|
print(
|
||||||
|
"M2_BUILD_DISPATCH_PLANNER PASS cases=10 iterations=20000 elapsed_ms=%.3f"
|
||||||
|
% elapsed_milliseconds
|
||||||
|
)
|
||||||
|
quit(0)
|
||||||
|
|
||||||
|
|
||||||
|
func _verify_wait_and_empty_actions(failures: Array[String]) -> void:
|
||||||
|
var planner: RefCounted = PLANNER_SCRIPT.new()
|
||||||
|
var animation_wait: Dictionary = planner.call(
|
||||||
|
"plan_step", 4, true, true, true, true
|
||||||
|
)
|
||||||
|
_expect_plan(
|
||||||
|
animation_wait,
|
||||||
|
PLANNER_SCRIPT.ACTION_WAIT_FOR_ANIMATION,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
"animation wait has absolute priority",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
var empty_batch: Dictionary = planner.call(
|
||||||
|
"plan_step", 0, false, true, true, true
|
||||||
|
)
|
||||||
|
_expect_plan(
|
||||||
|
empty_batch,
|
||||||
|
PLANNER_SCRIPT.ACTION_ADVANCE_WITHOUT_MATERIALIZATION,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
"zero batch advances without serial",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
var negative_batch: Dictionary = planner.call(
|
||||||
|
"plan_step", -3, false, false, false, false
|
||||||
|
)
|
||||||
|
_expect_plan(
|
||||||
|
negative_batch,
|
||||||
|
PLANNER_SCRIPT.ACTION_ADVANCE_WITHOUT_MATERIALIZATION,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
"negative batch advances without serial",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _verify_materialization_priority(failures: Array[String]) -> void:
|
||||||
|
var planner: RefCounted = PLANNER_SCRIPT.new()
|
||||||
|
var animated: Dictionary = planner.call(
|
||||||
|
"plan_step", 3, false, true, true, true
|
||||||
|
)
|
||||||
|
_expect_plan(
|
||||||
|
animated,
|
||||||
|
PLANNER_SCRIPT.ACTION_MATERIALIZE_ANIMATED,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
"animated prototype wins over static state",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
var static_mesh: Dictionary = planner.call(
|
||||||
|
"plan_step", 3, false, false, true, true
|
||||||
|
)
|
||||||
|
_expect_plan(
|
||||||
|
static_mesh,
|
||||||
|
PLANNER_SCRIPT.ACTION_MATERIALIZE_STATIC,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
"prepared static mesh wins over missing flag",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _verify_static_retry_and_missing_actions(failures: Array[String]) -> void:
|
||||||
|
var planner: RefCounted = PLANNER_SCRIPT.new()
|
||||||
|
var static_wait: Dictionary = planner.call(
|
||||||
|
"plan_step", 2, false, false, false, false
|
||||||
|
)
|
||||||
|
_expect_plan(
|
||||||
|
static_wait,
|
||||||
|
PLANNER_SCRIPT.ACTION_WAIT_FOR_STATIC_MESH,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
"unresolved static mesh rotates without serial",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
var missing: Dictionary = planner.call(
|
||||||
|
"plan_step", 2, false, false, false, true
|
||||||
|
)
|
||||||
|
_expect_plan(
|
||||||
|
missing,
|
||||||
|
PLANNER_SCRIPT.ACTION_ADVANCE_WITHOUT_MATERIALIZATION,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
"terminal missing model advances serial",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _verify_fresh_results(failures: Array[String]) -> void:
|
||||||
|
var planner: RefCounted = PLANNER_SCRIPT.new()
|
||||||
|
var first: Dictionary = planner.call("plan_step", 1, false, true, false, false)
|
||||||
|
first["action"] = &"mutated"
|
||||||
|
first["rotate_queue"] = true
|
||||||
|
var second: Dictionary = planner.call("plan_step", 1, false, true, false, false)
|
||||||
|
_expect_plan(
|
||||||
|
second,
|
||||||
|
PLANNER_SCRIPT.ACTION_MATERIALIZE_ANIMATED,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
"plan results are detached",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _verify_loader_and_dependency_boundaries(failures: Array[String]) -> void:
|
||||||
|
var loader_source := FileAccess.get_file_as_string(LOADER_PATH)
|
||||||
|
var planner_source := FileAccess.get_file_as_string(PLANNER_PATH)
|
||||||
|
_expect_true(
|
||||||
|
loader_source.contains("M2_BUILD_DISPATCH_PLANNER_SCRIPT.new()"),
|
||||||
|
"loader composes dispatch planner",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
_expect_true(
|
||||||
|
loader_source.contains("_m2_build_dispatch_planner.plan_step("),
|
||||||
|
"loader delegates dispatch decision",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
_expect_true(
|
||||||
|
loader_source.contains("_get_m2_mesh_or_request(rel_path)")
|
||||||
|
and loader_source.contains("_materialize_m2_animated_batch(")
|
||||||
|
and loader_source.contains("_materialize_m2_group_batch(")
|
||||||
|
and loader_source.contains("try_consume_permit("),
|
||||||
|
"loader retains readiness, materialization and permits",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
for forbidden_token in [
|
||||||
|
"preload(",
|
||||||
|
"ResourceLoader",
|
||||||
|
"RenderingServer",
|
||||||
|
"FileAccess",
|
||||||
|
"queue_free",
|
||||||
|
"Node3D",
|
||||||
|
"WorkerThreadPool",
|
||||||
|
"Thread",
|
||||||
|
"Mutex",
|
||||||
|
]:
|
||||||
|
_expect_false(
|
||||||
|
planner_source.contains(forbidden_token),
|
||||||
|
"planner omits %s ownership" % forbidden_token,
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _verify_bounded_timing(failures: Array[String]) -> float:
|
||||||
|
var planner: RefCounted = PLANNER_SCRIPT.new()
|
||||||
|
var started_microseconds := Time.get_ticks_usec()
|
||||||
|
for iteration in range(20000):
|
||||||
|
planner.call(
|
||||||
|
"plan_step",
|
||||||
|
iteration % 5,
|
||||||
|
iteration % 11 == 0,
|
||||||
|
iteration % 3 == 0,
|
||||||
|
iteration % 3 == 1,
|
||||||
|
iteration % 7 == 0
|
||||||
|
)
|
||||||
|
var elapsed_milliseconds := (
|
||||||
|
float(Time.get_ticks_usec() - started_microseconds) / 1000.0
|
||||||
|
)
|
||||||
|
_expect_true(
|
||||||
|
elapsed_milliseconds < 1000.0,
|
||||||
|
"20,000 dispatch plans remain bounded",
|
||||||
|
failures
|
||||||
|
)
|
||||||
|
return elapsed_milliseconds
|
||||||
|
|
||||||
|
|
||||||
|
func _expect_plan(
|
||||||
|
plan: Dictionary,
|
||||||
|
expected_action: StringName,
|
||||||
|
expected_rotation: bool,
|
||||||
|
expected_serial_increment: bool,
|
||||||
|
label: String,
|
||||||
|
failures: Array[String]
|
||||||
|
) -> void:
|
||||||
|
if plan.get("action", &"") != expected_action:
|
||||||
|
failures.append("%s action" % label)
|
||||||
|
if bool(plan.get("rotate_queue", false)) != expected_rotation:
|
||||||
|
failures.append("%s rotation" % label)
|
||||||
|
if (
|
||||||
|
bool(plan.get("increment_batch_serial", false))
|
||||||
|
!= expected_serial_increment
|
||||||
|
):
|
||||||
|
failures.append("%s serial" % label)
|
||||||
|
|
||||||
|
|
||||||
|
func _expect_true(condition: bool, label: String, failures: Array[String]) -> void:
|
||||||
|
if not condition:
|
||||||
|
failures.append(label)
|
||||||
|
|
||||||
|
|
||||||
|
func _expect_false(condition: bool, label: String, failures: Array[String]) -> void:
|
||||||
|
_expect_true(not condition, label, failures)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://ck4mypi4djlmy
|
||||||
@@ -56,7 +56,8 @@ Runtime и Editor используют facade; planner/scheduler тестиру
|
|||||||
`M03-RND-M2-ANIMATION-PLAYBACK-001`,
|
`M03-RND-M2-ANIMATION-PLAYBACK-001`,
|
||||||
`M03-RND-M2-ANIMATED-INSTANCE-MATERIALIZER-001`,
|
`M03-RND-M2-ANIMATED-INSTANCE-MATERIALIZER-001`,
|
||||||
`M03-RND-M2-STATIC-BATCH-MATERIALIZER-001`,
|
`M03-RND-M2-STATIC-BATCH-MATERIALIZER-001`,
|
||||||
`M03-RND-M2-BUILD-QUEUE-001`
|
`M03-RND-M2-BUILD-QUEUE-001`,
|
||||||
|
`M03-RND-M2-BUILD-DISPATCH-PLANNER-001`
|
||||||
- Commands: dedicated scheduler/planner/facade/focus/coordinate/manifest/shutdown
|
- Commands: dedicated scheduler/planner/facade/focus/coordinate/manifest/shutdown
|
||||||
headless verifiers; Godot cold editor parse; coordination, documentation and diff gates.
|
headless verifiers; Godot cold editor parse; coordination, documentation and diff gates.
|
||||||
- Results: scheduler `cases=6 iterations=20000 elapsed_ms=10.216`; planner
|
- Results: scheduler `cases=6 iterations=20000 elapsed_ms=10.216`; planner
|
||||||
@@ -251,6 +252,13 @@ Runtime и Editor используют facade; planner/scheduler тестиру
|
|||||||
Post-merge queue (`65.998ms`), planner, static/animated materializers, Mesh/
|
Post-merge queue (`65.998ms`), planner, static/animated materializers, Mesh/
|
||||||
prototype state, shutdown, materials, facade, internal-access `30`, manifest
|
prototype state, shutdown, materials, facade, internal-access `30`, manifest
|
||||||
`7/7`, documentation and coordination gates remained green.
|
`7/7`, documentation and coordination gates remained green.
|
||||||
|
M2 build dispatch planner passed `cases=10 iterations=20000
|
||||||
|
elapsed_ms=11.933` with animation-wait priority, zero/negative batch advance,
|
||||||
|
animated/static selection, unresolved static retry, terminal missing-model
|
||||||
|
serial advancement, detached results and source/dependency boundaries. All 53
|
||||||
|
autonomous headless regressions and checkpoint dry-run `7/7` passed; the
|
||||||
|
proprietary ADT probe remained unavailable. Internal-access inventory remains
|
||||||
|
`30`; resource observation/requests and action execution remain loader-owned.
|
||||||
M02 terrain-query regression
|
M02 terrain-query regression
|
||||||
remained green (13 pre-existing expired M00 claim warnings).
|
remained green (13 pre-existing expired M00 claim warnings).
|
||||||
- Fidelity comparison: all 16 historical operation limits and drain sites were
|
- Fidelity comparison: all 16 historical operation limits and drain sites were
|
||||||
@@ -282,6 +290,9 @@ Runtime и Editor используют facade; planner/scheduler тестиру
|
|||||||
Pending M2 builds retain keyed latest-job replacement, duplicate/stale FIFO
|
Pending M2 builds retain keyed latest-job replacement, duplicate/stale FIFO
|
||||||
entries, one-key rotation, strong root/group references and exact group/offset/
|
entries, one-key rotation, strong root/group references and exact group/offset/
|
||||||
serial progression; releasing queue records still never frees scene nodes.
|
serial progression; releasing queue records still never frees scene nodes.
|
||||||
|
M2 build dispatch retains pending-animation priority before batch planning,
|
||||||
|
animated-before-static selection, unresolved-static rotation without progress,
|
||||||
|
and the historical serial increment for terminally missing positive batches.
|
||||||
WMO cache-key normalization, positive MODF identity with tile/index fallback
|
WMO cache-key normalization, positive MODF identity with tile/index fallback
|
||||||
and world position/Euler/unclamped-scale transforms are unchanged across
|
and world position/Euler/unclamped-scale transforms are unchanged across
|
||||||
lightweight render, cached-scene and live-prototype paths.
|
lightweight render, cached-scene and live-prototype paths.
|
||||||
@@ -397,6 +408,7 @@ Runtime и Editor используют facade; planner/scheduler тестиру
|
|||||||
M2 animated instance materializer and loader log/Editor-owner adapter,
|
M2 animated instance materializer and loader log/Editor-owner adapter,
|
||||||
M2 static batch materializer and loader readiness/cursor/Editor-owner adapter,
|
M2 static batch materializer and loader readiness/cursor/Editor-owner adapter,
|
||||||
typed M2 build job/queue and loader enqueue/drain/progress/cancel/clear adapters,
|
typed M2 build job/queue and loader enqueue/drain/progress/cancel/clear adapters,
|
||||||
|
stateless M2 build dispatch planner and loader resource-observation/action adapter,
|
||||||
expanded facade/internal-access/coordinate verifiers; work-package claims and this evidence.
|
expanded facade/internal-access/coordinate verifiers; work-package claims and this evidence.
|
||||||
- Remaining risks: worker concurrency and stale-result validation remain
|
- Remaining risks: worker concurrency and stale-result validation remain
|
||||||
streamer-owned; cancellation stops new permits but does not interrupt
|
streamer-owned; cancellation stops new permits but does not interrupt
|
||||||
@@ -420,7 +432,8 @@ Runtime и Editor используют facade; planner/scheduler тестиру
|
|||||||
static MultiMesh materialization is also separated but remains synchronous;
|
static MultiMesh materialization is also separated but remains synchronous;
|
||||||
headless timing does not measure transform upload GPU cost and spatial-cell
|
headless timing does not measure transform upload GPU cost and spatial-cell
|
||||||
batching still needs culling/performance evidence; typed M2 pending state is
|
batching still needs culling/performance evidence; typed M2 pending state is
|
||||||
separated, while resource readiness/dispatch and root cleanup remain in loader;
|
separated and dispatch decisions are explicit, while resource observation/
|
||||||
|
requests, action execution and root cleanup remain in loader;
|
||||||
WMO ResourceLoader/FileAccess I/O, live fallback and group materialization
|
WMO ResourceLoader/FileAccess I/O, live fallback and group materialization
|
||||||
remain in the loader;
|
remain in the loader;
|
||||||
asset-backed WMO placement/portal/material/leak/p95/p99 evidence remains pending;
|
asset-backed WMO placement/portal/material/leak/p95/p99 evidence remains pending;
|
||||||
|
|||||||
Reference in New Issue
Block a user