docs(architecture): plan GameWindow render-frame extraction
This commit is contained in:
parent
9512404e25
commit
f316bbd817
3 changed files with 545 additions and 73 deletions
|
|
@ -605,89 +605,76 @@ visual-distance reduction.
|
|||
## Per-Frame Update Order (current runtime)
|
||||
|
||||
```
|
||||
1. Network tick
|
||||
└── Drain inbound queue → process CreateObject, UpdateMotion,
|
||||
UpdatePosition, PlayerTeleport → create/update GameEntities
|
||||
|
||||
2. Streaming tick
|
||||
└── Compute observer position → load/unload landblocks →
|
||||
create terrain + scenery GameEntities
|
||||
|
||||
3. Input tick (player mode only)
|
||||
└── InputDispatcher scopes → PlayerMovementController →
|
||||
MotionInterpreter/body prediction → ResolveWithTransition →
|
||||
send MoveToState/AutonomousPosition to server
|
||||
|
||||
4. Entity / animation tick
|
||||
└── Current code still has scattered world/entity state. L.1 owns
|
||||
animation parity; L.2 owns movement/collision conformance.
|
||||
|
||||
5. Render tick
|
||||
└── Read current entity mesh refs, draw
|
||||
TerrainModernRenderer + WbDrawDispatcher + EnvCellRenderer
|
||||
(frustum cull, translucency pass, portal visibility, etc.)
|
||||
World Wb translucents + Scene particles submit to RetailAlphaQueue;
|
||||
RetailPViewRenderer flushes the landscape scope before the conditional
|
||||
depth clear and GameWindow flushes the final scope before private
|
||||
viewports/UI. Sky/private viewport particles remain immediate.
|
||||
Projectiles remain ordinary live-entity draws; there is no global or
|
||||
projectile-specific render pass.
|
||||
|
||||
6. Plugin tick
|
||||
└── Fire IEvents, drain IActions queue
|
||||
|
||||
6a. UI tick
|
||||
IPanelHost.Draw → iterate registered IPanel instances, build
|
||||
ViewModels from IGameState, dispatch user Commands via ICommandBus.
|
||||
Backend-agnostic — ImGui or custom retail-look draws here depending
|
||||
on which is compiled in. See docs/plans/2026-04-24-ui-framework.md.
|
||||
UpdateFrameOrchestrator
|
||||
1. retry retained live-entity teardown tombstones
|
||||
2. normalize/publish the host and PhysicsScript clocks
|
||||
3. converge streaming origin, readiness, residence, and rescued projections
|
||||
4. sample semantic input, raw mouse, and combat intent
|
||||
5. advance local/ordinary/static live objects and deferred effect tails
|
||||
6. drain inbound session traffic in one GpuWorldState mutation batch
|
||||
7. run the local post-network CommandInterpreter position tail
|
||||
8. reconcile roots, children, emitters, and lights without advancing time
|
||||
9. expire liveness
|
||||
10. advance local teleport/reveal/tunnel presentation
|
||||
11. evaluate one-shot player-mode entry
|
||||
12. publish fly/chase/player camera presentation
|
||||
```
|
||||
|
||||
`GameWindow.OnUpdate` owns only the profiler scope and one orchestrator handoff.
|
||||
The accepted host order above is preserved as TS-53; it is not overclaimed as
|
||||
the exact retail `Client::UseTime` order. See the completed Slice 6 ledger and
|
||||
`memory/project_gamewindow_decomposition.md`.
|
||||
|
||||
The render callback currently owns the GPU frame boundary and draw graph. Slice
|
||||
7 extracts that graph without changing its order: per-resource frame begins and
|
||||
render-thread uploads; world/PView and its two shared-alpha scopes; portal and
|
||||
paperdoll private viewports; retained gameplay UI; ImGui devtools; screenshot;
|
||||
then the GPU-flight close in `finally`. Both UI stacks coexist in the same frame.
|
||||
|
||||
---
|
||||
|
||||
## Render Pipeline (SSOT — current state + unified-PView target)
|
||||
## Render Pipeline (SSOT — current accepted state)
|
||||
|
||||
> The modern path (Phase N.5, mandatory) is
|
||||
> `WbDrawDispatcher` (entities) + `EnvCellRenderer` (indoor cell shells) +
|
||||
> `TerrainModernRenderer` (terrain), fed by the portal-visibility stack. This section is
|
||||
> the authoritative description of how indoor/outdoor rendering is *supposed* to work and
|
||||
> where the code currently diverges. Canonical reset handoff:
|
||||
> `docs/research/2026-05-31-render-architecture-reset-handoff.md`.
|
||||
> `TerrainModernRenderer` (terrain), fed by the unified PView stack. This is the
|
||||
> authoritative current draw model; the 2026-05-31 reset handoff is historical.
|
||||
|
||||
**The principle (retail PView).** acdream must render the world the way retail does —
|
||||
through **one** portal-visibility traversal whose output **gates every geometry type
|
||||
uniformly**. From the player's cell, walk the portal graph; each visible cell carries a
|
||||
screen-space clip region (its portal opening, recursively intersected); the **outside**
|
||||
(terrain + outdoor scenery) is reached only through **exit portals** and is clipped to
|
||||
those openings. Interior cell shells, interior statics, and the outside are **all**
|
||||
clipped to their PView region. This is why retail is **seamless by construction**. Decomp
|
||||
anchors: `PView::ConstructView` (`:433750`), `InitCell` (`:432896`), `DrawCells`
|
||||
(`:432715`), `CEnvCell::find_visible_child_cell` (`:311397`), `SmartBox::update_viewer`
|
||||
(`:92761`). Reference port acdream owns but never invokes: WB `RenderInsideOut` /
|
||||
`VisibilityManager`.
|
||||
**One visibility owner.** `RetailPViewRenderer.DrawInside` is the production
|
||||
world gate. Its root is the collided camera/viewer cell, or the synthetic
|
||||
outdoor cell adaptation; the player's current cell separately owns sunlight
|
||||
and indoor lighting. A null root exists only for login/debug/streaming-gap
|
||||
fallback frames. `RetailPViewRenderer` is the one authoritative PView owner and
|
||||
product family: it builds the main frame, deliberate per-building exterior
|
||||
floods for the synthetic outdoor root, and separate interior-root look-in
|
||||
frames. No second per-frame ACME visibility BFS competes with that family.
|
||||
|
||||
**The one rule:** *compute visibility once; enforce it once, for all geometry.* Indoors,
|
||||
you see the outside **only** through portal openings (clipped); an empty outside-view
|
||||
(windowless interior) draws **no** outdoor geometry. Outdoors, the gate is "everything."
|
||||
**Current draw discipline.** Outside-view slices draw sky, terrain, and outdoor
|
||||
statics first. Interior-root building look-ins then punch all entry apertures
|
||||
before drawing their shell and contents. The landscape shared-alpha scope
|
||||
flushes before the root-specific depth boundary. Interior roots perform the
|
||||
conditional depth clear and then write true-depth exit seals; the synthetic
|
||||
outdoor root retains world depth and writes far-Z building-entry punches.
|
||||
Opaque EnvCell shells, immediate far-to-near transparent EnvCell shells, cell
|
||||
statics and their particles, then the surviving main-stage dynamics and their
|
||||
particles follow. Look-in and outside-stage dynamics intentionally draw in
|
||||
their earlier landscape phases. The final world shared-alpha scope flushes
|
||||
before private portal/paperdoll viewports and UI.
|
||||
|
||||
**Current divergence (the patchwork — what the reset must fix).** acdream computes the
|
||||
visibility correctly (`CellVisibility.ComputeVisibility` → `PortalVisibilityBuilder.Build`,
|
||||
a `ConstructView` port → `ClipFrameAssembler`) but then **enforces it three different,
|
||||
inconsistent ways**:
|
||||
1. `TerrainModernRenderer` — gated by `TerrainClipMode {Skip|Scissor|Planes}` (the Scissor
|
||||
fallback over-includes).
|
||||
2. `EnvCellRenderer` — gated by the per-cell clip slot (≈correct; the shells DO render —
|
||||
proven by the `[shell]` probe, `ACDREAM_PROBE_SHELL`).
|
||||
3. `WbDrawDispatcher` — gated by `ParentCellId ∈ visibleCellIds`, **but outdoor stabs
|
||||
(`ParentCellId==null`) bypass the gate** → outdoor scenery/terrain shows from inside
|
||||
(issue #78).
|
||||
The modern renderer intentionally does not hard-clip every shell or entity to
|
||||
the accumulated polygon. It combines PView admission and viewcone checks with
|
||||
retail's punch/seal depth discipline; terrain/outside slices use the bounded
|
||||
clip-plane/scissor adaptation. World Wb translucents and Scene particles share
|
||||
one stable far-to-near `RetailAlphaQueue`; EnvCell transparent shells and
|
||||
private viewports remain immediate. Projectiles are ordinary live-entity draws,
|
||||
never a separate global pass.
|
||||
|
||||
Three gates that must agree but don't → structural seams (transparent walls,
|
||||
terrain-through-floor, grey enclosure). **The reset consolidates them into the single
|
||||
PView gate** (outside content clipped to the `OutsideView` region; no `ParentCellId==null`
|
||||
bypass; no Scissor over-include). This is a **consolidation of existing machinery**
|
||||
(`PortalVisibilityBuilder` + `ClipFrame`), not a rewrite. Do NOT add a fourth special-case
|
||||
gate to mask a seam — that anti-pattern produced the patchwork.
|
||||
Retail anchors are `SmartBox::RenderNormalMode @ 0x00453AA0`,
|
||||
`PView::DrawInside @ 0x005A5860`, `PView::DrawCells @ 0x005A4840`,
|
||||
`LScape::draw @ 0x00506330`, `D3DPolyRender::FlushAlphaList @ 0x0059D2E0`,
|
||||
and `SceneTool::EndFrame @ 0x0043FB30`. The deliberate modern/PView
|
||||
adaptations are audited in the retail divergence register; Slice 7 changes
|
||||
their ownership only, not their behavior.
|
||||
|
||||
### Streaming publication ownership
|
||||
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ src/AcDream.App/
|
|||
├── RuntimeOptions.cs # typed startup options (Rule 4)
|
||||
├── Rendering/
|
||||
│ ├── GameWindow.cs # thin: GL/window lifecycle + delegates per-frame to RenderFrameOrchestrator
|
||||
│ ├── RenderFrameOrchestrator.cs # per-frame draw order (sky → terrain → opaque → trans → particles → debug → UI)
|
||||
│ ├── RenderFrameOrchestrator.cs # GPU-flight boundary + typed world/private/UI render phases
|
||||
│ ├── LiveEntityAnimationScheduler.cs # shipped: ordinary live-object update workset
|
||||
│ ├── LiveEntityAnimationPresenter.cs # final part-pose/mesh/effect composition after scheduler output
|
||||
│ ├── RetailStaticAnimatingObjectScheduler.cs # shipped: separate static-animation workset
|
||||
|
|
@ -606,6 +606,10 @@ registered; this behavior-preserving extraction introduced no new divergence.
|
|||
|
||||
#### Slice 7 — extract `RenderFrameOrchestrator`
|
||||
|
||||
Detailed execution ledger:
|
||||
[`docs/plans/2026-07-22-gamewindow-slice-7-render-frame-orchestration.md`](../plans/2026-07-22-gamewindow-slice-7-render-frame-orchestration.md)
|
||||
(active).
|
||||
|
||||
Move the complete draw graph and its reusable frame-local scratch state into a
|
||||
GL-owning App collaborator. Preserve the exact modern pipeline order, clip
|
||||
routing, PView flood, landscape/opaque/shared-alpha flush boundaries,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue