docs(streaming): define cost-budgeted lifecycle
This commit is contained in:
parent
66690805c5
commit
2945896a6f
3 changed files with 479 additions and 0 deletions
|
|
@ -724,6 +724,8 @@ checkpoint retained staged or retiring bytes. Evidence:
|
|||
|
||||
### Slice E — Cost-budgeted streaming and retirement
|
||||
|
||||
**Active plan:** [`2026-07-24-modern-runtime-slice-e-cost-budgeted-streaming.md`](2026-07-24-modern-runtime-slice-e-cost-budgeted-streaming.md).
|
||||
|
||||
**Purpose:** Remove update-thread portal transactions.
|
||||
|
||||
- Introduce explicit stage queues and `StreamingWorkBudget`.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,300 @@
|
|||
# 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 `StreamingWorkBudget` defines 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 `WorldRevealCoordinator` remains 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`](../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:
|
||||
|
||||
```csharp
|
||||
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:
|
||||
|
||||
1. destination collision/required Near scene;
|
||||
2. visible Near;
|
||||
3. ordinary Near;
|
||||
4. Far/speculative;
|
||||
5. 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-only `MaxCompletionsPerFrame` execution disappear at cutover.
|
||||
|
||||
## 6. Immediate quiesce versus deferred release
|
||||
|
||||
At a hard destination-generation edge:
|
||||
|
||||
1. The old world generation is marked unavailable to normal world drawing,
|
||||
picking, radar, status targeting, and collision queries.
|
||||
2. Object/static animation, PhysicsScript, particle emission, and ambient/
|
||||
owner-tagged audio for that generation are frozen or silenced.
|
||||
3. The portal tunnel/UI/event path continues, matching retail's
|
||||
`blocking_for_cells` branch.
|
||||
4. Stable resident landblock IDs are captured once.
|
||||
5. 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:
|
||||
|
||||
```text
|
||||
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 `StreamingWorkBudgetOptions` to `RuntimeOptions`.
|
||||
- Add pure `StreamingWorkMeter` and 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 `_deferredApply` with 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 `PriorityRadius` execution 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:
|
||||
|
||||
1. No priority or unload path bypasses all work budgets.
|
||||
2. No frame exceeds any configured count/byte/entity dimension.
|
||||
3. Every time overrun names one indivisible operation; none remain above the
|
||||
configured time ceiling at closeout.
|
||||
4. Old generations become unavailable, tick-frozen, and audio-silent at the
|
||||
hard transition edge.
|
||||
5. Retirement/publication retries resume exact cursors without replay.
|
||||
6. Deferred retained CPU bytes stay within their queue budget.
|
||||
7. Reveal is possible only for the active destination generation and the
|
||||
existing readiness predicate.
|
||||
8. Portal-window p99 and largest frame allocation materially improve over the
|
||||
post-Slice-A physical baseline.
|
||||
9. Complete connected routes end with zero pending publication, retirement,
|
||||
upload, effect, collision, audio, and stale-generation owners.
|
||||
10. Visual output/range/quality and retail gameplay behavior are unchanged.
|
||||
Loading…
Add table
Add a link
Reference in a new issue