14 KiB
Modern Runtime Slice E — Cost-budgeted streaming and retirement
Status: active — E0 contract/inventory complete
Program: docs/plans/2026-07-24-modern-runtime-architecture.md
Baseline: Slice D closeout commit 66690805
Behavior contract: no visual-quality or retail-behavior reduction
1. Outcome
Slice E removes whole-window portal publication and retirement transactions
from a single update frame. It preserves the existing worker, canonical
GpuWorldState, presentation owners, retry receipts, world-reveal barrier,
and renderer resource owners.
The change is scheduling and lifecycle architecture:
- one immutable
StreamingWorkBudgetdefines real per-frame limits; - one single-writer scheduler admits and advances explicit work stages;
- destination-critical work receives reserved capacity across portal frames;
- old generations become unavailable and quiescent immediately;
- expensive owner/resource teardown resumes from exact cursors;
- publication advances through exact owner receipts under time, byte, entity, upload, and retirement-operation limits;
- the existing
WorldRevealCoordinatorremains the only reveal predicate.
No distance, texture, particle, cell, collision, or presentation quality is reduced.
2. Retail boundary
Retail teardown is synchronous:
CLandBlock::destroy_static_objects @ 0x0052FA50;CLandBlock::Destroy @ 0x0052FAA0;CObjectMaint::DestroyObjects @ 0x00508C30.
Retail portal/cell blocking freezes object maintenance, physics, landscape,
game time, and ambient sound while continuing scene/UI/event work:
SmartBox::UseTime @ 0x00455410.
Acdream's async adaptation may defer physical cleanup, but may not leave a
retired owner active. Full translation:
../research/2026-07-24-retail-streaming-retirement-pseudocode.md.
3. Existing-owner inventory
| Concern | Current owner | Current behavior | Slice E action |
|---|---|---|---|
| Worker inbox/outbox | LandblockStreamer |
unbounded channels; near jobs prioritized | retain worker; expose bounded, allocation-free single-consumer drain and backlog facts |
| Completion apply | StreamingController._deferredApply |
retained List; count-budgeted loads; unload and priority ring bypass |
replace with explicit generation/priority FIFO stage queues and exact retained-byte accounting |
| Full reload | BeginFullWindowRetirement |
snapshots and begins every resident retirement synchronously | retain stable snapshot/cursor and advance admissions under budget |
| Origin recenter | OriginRecenterRetirement |
has a cursor but drains it to completion in one call | make the cursor the canonical budgeted admission receipt |
| Logical/spatial detach | GpuWorldState.DetachLandblock/DetachNearLayer |
exact immediate bucket withdrawal; captures entity receipt | preserve; add destination-generation quiesce before cursored detaches |
| Retirement ledger | LandblockRetirementCoordinator |
retryable per-stage/per-entity state, but advances a complete ticket on begin/advance | expose one bounded atomic step at a time; stable FIFO |
| Retirement effects | LandblockPresentationRetirementOwner |
lights, translucency, plugin, terrain, physics, cells/buildings | preserve order; each entity/stage consumes measured budget |
| Publication ledger | LandblockPresentationPipeline |
retryable stage booleans, but Advance runs the complete transaction |
convert booleans/cursors into resumable atomic publication steps |
| Render publication | LandblockRenderPublisher |
terrain/visibility/AABB/buildings/EnvCells whole stages | retain receipts; meter GPU bytes and split iterable registries where needed |
| Physics publication | LandblockPhysicsPublisher |
rebuilds terrain/cells/buildings/statics/reflood in one call | cursor cell/building/static preparation and commit at safe mutation boundaries |
| Static presentation | LandblockStaticPresentationPublisher |
allocates dictionaries/sorts arrays, then loops prior/plugin entities | prepare stable ordered arrays once; cursor cleanup/light/plugin work |
| Mesh upload | ObjectMeshManager/WbMeshAdapter |
existing per-frame renderer owner/budgets | report exact upload bytes/operations to the streaming meter; never move GL ownership |
| Reveal | WorldRevealCoordinator + WorldRevealReadinessBarrier |
one correct login/portal predicate | add destination generation/readiness stage facts; never duplicate predicate |
| Portal wait cue | PortalTunnelPresentation |
authored tunnel and centered wait notice port exists | hold tunnel past five seconds and publish diagnostic; never early reveal |
| Audio | OpenAlAudioEngine/AudioHookSink |
one-shot owner is not retained in source slot | tag 3-D voices by owner; stop old-generation owners at quiesce |
4. Work model
4.1 Immutable budget
The App-layer type is equivalent to:
public readonly record struct StreamingWorkBudget(
TimeSpan MaxUpdateTime,
int MaxCompletionAdmissions,
long MaxAdoptedCpuBytes,
int MaxEntityOperations,
long MaxGpuUploadBytes,
int MaxGlRetireOperations,
float DestinationReserveFraction);
All fields are positive and validated as one profile. The initial High/default profile is evidence-tuned, not a reduction in quality:
- 2.0 ms update-thread streaming work;
- 64 completion admissions;
- 8 MiB retained CPU payload adoption;
- 256 entity operations;
- 8 MiB requested GPU upload;
- 64 logical GL-retirement admissions;
- 75% reserved for destination-critical work while a reveal is pending.
These are initial scheduling ceilings, not content ceilings. Work continues on later frames until exact convergence. Connected E gates may retune the profile as one atomic change.
The compatibility MaxCompletionsPerFrame setting maps to complete Low/Medium/
High/Ultra work profiles during migration. It does not remain an independent
hidden second throttle.
4.2 Meter
StreamingWorkMeter is stack/frame scoped and single-thread owned. It records:
- start/deadline timestamp;
- admitted results and retained CPU bytes;
- entity operations;
- GPU bytes requested;
- GL retirement operations;
- stage completions, yields, overruns, failures, and oldest-work age.
Reservation is checked before an atomic operation using its conservative known cost. Elapsed time is checked after every atomic operation. A first operation may run when a dimension is otherwise empty so progress cannot deadlock; any single-operation overrun is named in diagnostics and becomes a required split site.
4.3 Cost estimation
LandblockStreamResultCost is computed once from immutable payloads:
- terrain vertex/index bytes;
- entity count;
- mesh-reference count;
- EnvCell visibility/shell count;
- physics cell/building/GfxObj counts;
- prepared payload arrays retained by the completion.
It never uses process working set as a scheduling unit and never counts the memory-mapped package's address space as adopted bytes.
5. Queue and ordering contract
One scheduler owns queues by (generation, priority, stage).
Priorities:
- destination collision/required Near scene;
- visible Near;
- ordinary Near;
- Far/speculative;
- retirement cleanup after immediate quiesce.
Rules:
- FIFO is exact within one generation/priority.
- Generation supersession cancels stale unpublished work and releases its retained-byte charge.
- Destination reserve applies only while the canonical reveal generation is pending.
- Unreserved capacity is work-conserving: any queue may use it.
- At least one noncritical/retirement operation is admitted when capacity remains, preventing starvation during a slow destination.
- Failed stages retain their exact receipt and queue position.
- Callback reentrancy appends work; it never mutates the active cursor.
_deferredApply,MaxDrainIterations, the eager priority-radius bypass, and count-onlyMaxCompletionsPerFrameexecution disappear at cutover.
6. Immediate quiesce versus deferred release
At a hard destination-generation edge:
- The old world generation is marked unavailable to normal world drawing, picking, radar, status targeting, and collision queries.
- Object/static animation, PhysicsScript, particle emission, and ambient/ owner-tagged audio for that generation are frozen or silenced.
- The portal tunnel/UI/event path continues, matching retail's
blocking_for_cellsbranch. - Stable resident landblock IDs are captured once.
- Per-landblock spatial detach and owner/resource release advance from cursors.
Quiesce is idempotent and generation-scoped. It does not destroy live server identity, replay Hidden/UnHide, or clear the replacement generation.
For ordinary Near→Far demotion outside a hard portal edge, the exact landblock detach remains the immediate visibility/collision boundary; only its expensive owner cleanup is deferred.
7. Publication contract
One accepted completion progresses through:
Admitted
-> PreparedReceipt
-> RenderPrefix
-> PhysicsBase
-> RenderCellsBuildings
-> StaticEntityPresentationAndCollision
-> SpatialCommit
-> RenderPinsAndEnvCellReplay
-> LiveProjectionRecovery
-> Complete
Each transition owns a retry receipt and known cost. Mutation order remains the existing retail-rooted order. A stage cannot publish partial externally visible state unless its receipt describes and resumes that exact partial prefix.
Destination reveal consumes only Complete/renderer-upload facts belonging to
the active destination generation. Far-ring work may continue after reveal.
8. Threading
- Worker threads build immutable
LandblockBuild/prepared payloads and enqueue immutable completions only. - The update/render thread is the sole scheduler, meter, world-state, publication, and retirement writer.
- GL upload/non-residency/deletion remains with renderer resource owners.
- No update operation waits for the worker, a GPU fence, file I/O, or a lock held by render work.
- Back-pressure never blocks the update thread. Worker-side bounded queues may wait/cancel; update-side admission is non-blocking.
9. Delivery checkpoints
E0 — Oracle, inventory, and contract
- Record named-retail teardown/blocking pseudocode.
- Inventory every current publication/retirement owner and bypass.
- Define budget dimensions, atomic work, queue ordering, quiesce, retries, threading, diagnostics, and acceptance.
Complete 2026-07-24.
E1 — Typed budgets, meter, cost model, and diagnostics
- Add validated
StreamingWorkBudgetOptionstoRuntimeOptions. - Add pure
StreamingWorkMeterand deterministic injected-clock tests. - Add exact immutable completion-cost estimation.
- Publish per-stage backlog, bytes, oldest age, work/yield/overrun facts through lifecycle artifacts.
- Preserve current execution while measuring the would-yield decisions.
E2 — Explicit admission queues
- Replace
_deferredApplywith stable generation/priority queues. - Bound result admission by count and CPU bytes.
- Remove
MaxDrainIterations. - Preserve exact retry position and stale-generation rejection.
- Keep current publication atomic until E4, but execute only work admitted by the typed meter.
E3 — Quiesce and budgeted retirement
- Add destination-generation world quiesce.
- Tag/stop owner audio at the immediate edge.
- Make full-window and recenter retirement admission cursor/time/entity bounded.
- Make each retirement ticket advance by bounded atomic entity/stage work.
- Prove no old generation ticks, emits, collides, targets, or renders while its retained memory converges.
E4 — Cursor-budgeted publication
- Move stable ordered arrays/cost facts into retained receipts.
- Cursor static cleanup, static light/collision/plugin publication, physics cell/building work, and environment-cell publication at safe boundaries.
- Feed GPU requested-byte and retirement-operation facts from renderer owners.
- Prove every failure resumes the exact unfinished operation without replay.
E5 — Destination reservation and reveal generation
- Replace eager
PriorityRadiusexecution with destination reservations. - Join scheduler destination generation to the existing reveal coordinator.
- Keep the tunnel animating and surface the retail wait cue/diagnostic beyond five seconds.
- Remove the last count-only execution/bypass paths.
E6 — Gates and closeout
- Deterministic pressure, reentrancy, failure, cancellation, stale-generation, GUID reuse, dungeon, outdoor, and session-reset suites.
- Release build and full solution tests.
- Physical-local capped/uncapped nine-stop and pinned dense-Arwic routes.
- Compare portal p99 and maximum single-frame allocation to Slice A and Slice C.
- Require zero
viewport-before-ready, zero stranded old generation, staged upload, collision, effect, audio, or GPU owner. - Fixed-camera screenshots and subjective visual gate only if pixels change.
- Update architecture, inventory, issues, roadmap, divergence register, and durable memory.
10. Acceptance
Slice E closes only when:
- No priority or unload path bypasses all work budgets.
- No frame exceeds any configured count/byte/entity dimension.
- Every time overrun names one indivisible operation; none remain above the configured time ceiling at closeout.
- Old generations become unavailable, tick-frozen, and audio-silent at the hard transition edge.
- Retirement/publication retries resume exact cursors without replay.
- Deferred retained CPU bytes stay within their queue budget.
- Reveal is possible only for the active destination generation and the existing readiness predicate.
- Portal-window p99 and largest frame allocation materially improve over the post-Slice-A physical baseline.
- Complete connected routes end with zero pending publication, retirement, upload, effect, collision, audio, and stale-generation owners.
- Visual output/range/quality and retail gameplay behavior are unchanged.