diff --git a/docs/architecture/acdream-architecture.md b/docs/architecture/acdream-architecture.md index 743ab0a5..96431340 100644 --- a/docs/architecture/acdream-architecture.md +++ b/docs/architecture/acdream-architecture.md @@ -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 diff --git a/docs/architecture/code-structure.md b/docs/architecture/code-structure.md index f27abb03..07d89b24 100644 --- a/docs/architecture/code-structure.md +++ b/docs/architecture/code-structure.md @@ -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, diff --git a/docs/plans/2026-07-22-gamewindow-slice-7-render-frame-orchestration.md b/docs/plans/2026-07-22-gamewindow-slice-7-render-frame-orchestration.md new file mode 100644 index 00000000..135cf817 --- /dev/null +++ b/docs/plans/2026-07-22-gamewindow-slice-7-render-frame-orchestration.md @@ -0,0 +1,481 @@ +# GameWindow Slice 7 — render-frame orchestration + +**Status:** Active 2026-07-22. +**Parent program:** [`docs/architecture/code-structure.md`](../architecture/code-structure.md), Slice 7. +**Baseline:** `9512404e`; `GameWindow.cs` is 7,026 raw lines, 241 fields, and +108 methods. The Release suite passes 7,182 tests / 5 fixture or environment +skips. The 314-second lifecycle/reconnect gate and 394-second synchronized +nine-stop resource soak pass. +**Behavior rule:** This is an ownership extraction. Preserve every accepted +world/PView/alpha/private-viewport/UI draw edge, render-thread publication +edge, GL-state boundary, and GPU-flight failure contract. Do not combine the +extraction with a render-quality change, visibility correction, frame-order +port, or resource-lifetime redesign. + +## Progress ledger + +- [ ] A — freeze the complete render graph, correct the architecture SSOT, and + introduce data-only frame contracts plus deterministic order/failure tests. +- [ ] B — extract paperdoll, panel-layout/devtools, and render-diagnostics leaf + owners while `GameWindow` still orders the frame. +- [ ] C — extract frame-resource begin/upload/publication and live display, + and weather/display foundations that do not depend on camera/root + classification. +- [ ] D — extract frame-data classification and reusable world scratch without + duplicating live identity, spatial, visibility, or resource ownership; then + run the dependent audio, sky, lighting, and listener preparation. +- [ ] E — replace `RetailPViewDrawContext`'s callback bag with a typed + `RetailPViewPassExecutor` and data-only inputs. +- [ ] F — extract `WorldSceneRenderer`, including both shared-alpha scopes, + PView/fallback world branches, particles, debug world draw, and completion. +- [ ] G — compose `RenderFrameOrchestrator`, cut `GameWindow.OnRender` to one + handoff, and delete obsolete frame bodies, fields, helpers, and callbacks. +- [ ] H — corrected-diff reviews, full Release suite, connected lifecycle and + soak gates, framebuffer comparison, documentation, memory, and metrics. + +Every checked checkpoint is one bisectable architectural commit. A checkpoint +does not complete while a new owner merely delegates a substantial body back to +`GameWindow`. + +## 1. Outcome and non-goals + +At slice exit, `GameWindow.OnRender` supplies one immutable delta/viewport input +to `RenderFrameOrchestrator`. The orchestrator owns the GPU-flight transaction +and composes focused frame-resource, world-scene, private-presentation, and +diagnostic phases. `GameWindow` remains the construction and shutdown shell; it +does not keep the draw graph, PView pass callbacks, frame scratch, paperdoll +assembly, or performance-title body. + +The target ownership graph is: + +```text +RenderFrameOrchestrator + ├── RenderFrameResourceController GPU slot + resource BeginFrame/upload + ├── WorldRenderFrameBuilder camera/root/light/classification facts + ├── WorldSceneRenderer accepted world draw graph + │ ├── RetailPViewRenderer visibility/order owner + │ ├── RetailPViewPassExecutor concrete GL pass implementation + │ └── WorldRenderDiagnostics probes/debug counters/signatures + ├── PrivatePresentationRenderer portal + paperdoll + retained UI + ImGui + │ ├── PaperdollFramePresenter + │ └── DevToolsFramePresenter + └── RenderFrameDiagnosticsController title/resource/frame snapshots +``` + +Names may narrow as code lands, but the ownership boundaries do not collapse +into one replacement god object. No new owner may receive `GameWindow`, a broad +service locator, or an anonymous list of callbacks into the window. + +This slice does **not** change: + +- the viewer-cell render root or player-cell lighting root; +- synthetic outdoor-cell behavior or the null-root safety path; +- portal flood, building pre-gates, clip-plane/scissor limits, punch/seal + depth behavior, shell lift, viewcone culling, or draw distances; +- shared-alpha sort keys, scope boundaries, blend/cull behavior, or immediate + EnvCell/private alpha; +- weather, fog, point-light selection, particle visibility/range, terrain + quality, FOV, frame pacing, or audio-listener formulas; +- portal-space, paperdoll, retained gameplay UI, ImGui, or screenshot visuals; +- resource ownership, shutdown/disposal order, or three-frame GPU retirement; +- TS-53's retained-UI time placement on the render seam. + +Issue #225's translucent lifestone/particle comparison remains a separate user +visual gate. Passing this structural slice must not mark it resolved. + +## 2. Retail oracle and accepted adaptations + +This extraction reuses the existing named-retail research rather than +reinterpreting the draw algorithms: + +- `SceneTool::BeginScene @ 0x0043DAD0` establishes the black scene-clear + baseline used by portal CreatureMode replacement; +- `SmartBox::DrawNoBlit @ 0x00454C20` updates the viewer and invokes the normal + world render before post-world selection work; +- `SmartBox::RenderNormalMode @ 0x00453AA0` owns landscape/inside dispatch and + the final retail alpha flush; +- `RenderDeviceD3D::DrawInside @ 0x0059F0D0`, + `PView::DrawInside @ 0x005A5860`, and + `PView::ConstructView @ 0x005A57B0` own the portal traversal; +- `PView::DrawCells @ 0x005A4840` fixes the outside-landscape → alpha flush → + conditional depth clear → exit masks → shells → object-list order; +- `LScape::draw @ 0x00506330`, `GameSky::Draw @ 0x00506FF0`, + `DrawBuilding @ 0x0059F2A0`, `DrawBlock @ 0x005A17C0`, and + `DrawObjCellForDummies @ 0x005A0760` constrain the landscape/building/cell + phases; +- `AddMeshToAlphaList @ 0x0059C230` and + `FlushAlphaList @ 0x0059D2E0` constrain alpha submission/flush boundaries; +- `CPhysicsPart::UpdateViewerDistance @ 0x0050E030` and + `CShadowPart::insertion_sort @ 0x006B5130` constrain transformed DAT + SortCenter/CYpt and stable far-to-near ordering; +- `CreatureMode::Render @ 0x004529D0` constrains portal and paperdoll private + viewports; +- `SceneTool::EndFrame @ 0x0043FB30` proves retail gameplay UI precedes retail + profiler/debug overlays; placing ImGui above gameplay UI is acdream's IA-12 + modern coexistence adaptation, not literal Keystone parity. + +The readable pseudocode oracle is +[`docs/research/2026-06-05-retail-pview-indoor-render-pseudocode.md`](../research/2026-06-05-retail-pview-indoor-render-pseudocode.md), +with the current full reference in +[`docs/research/2026-06-02-retail-render-pipeline-full-reference.md`](../research/2026-06-02-retail-render-pipeline-full-reference.md). +Shared-alpha ordering is pinned separately by +[`docs/research/2026-07-18-retail-shared-alpha-list-pseudocode.md`](../research/2026-07-18-retail-shared-alpha-list-pseudocode.md). + +This slice preserves, rather than retires, the registered modern adaptations: +IA-8/9/10/12/14/15; AD-16/17/19/24; +AP-21/32/33/34/35/85/92/115/116/117; +TS-6/7; and TS-53. Source pointers move with ownership in the same commit, but +mechanism/risk language changes only if behavior actually changes. + +## 3. Frozen production render order + +The complete accepted order is: + +1. call `GpuFrameFlightController.BeginFrame` outside the render `try`; +2. begin the composite-texture frame and cache tick; +3. begin, in order, dispatcher, EnvCell, portal depth, world text, retained-UI + text, clip, terrain, lighting UBO, then profiler frame state for the current + GPU slot; +4. select the portal replacement viewport; reset particle visibility when it + is active; +5. choose portal black or normal fog clear color, force depth writes, clear + color/depth/stencil, and establish CW/back-face frame-global state; +6. run GL-state/surface diagnostics; +7. advance render-thread publication in order: `WbMeshAdapter.Tick`, destination + reveal preparation/evaluation, then `ParticleRenderer.BeginFrame`; +8. begin ImGui, tick weather, and begin retail selection-scene accumulation; +9. when a normal camera/world viewport is active: + - begin the one frame-scoped `RetailAlphaQueue` collection; its landscape + `Flush` drains the first sub-scope and `EndFrame` later drains the final + world sub-scope without a second begin; + - resolve camera/projection/frustum and begin particle/terrain visibility; + - publish world-reveal visibility/completion; + - preview audio/display settings and publish the listener; + - resolve player lighting cell and collided viewer render cell; + - build prior-visible-cell point-light candidacy, sun/fog/lighting UBO, and + animated/equipped classification; + - construct the synthetic outdoor root/building candidates when required; + - run the null-root safety world or the one `RetailPViewRenderer.DrawInside` + graph; + - draw the post-world Scene particles/weather fallback and debug geometry; + - sample current-frame DebugVM visible-landblock and nearest-collision facts + before the developer UI can consume them; + - close the final shared-alpha scope and publish terrain/cell particle + visibility; +10. complete retail selection-scene accumulation; +11. draw the portal CreatureMode replacement viewport; +12. render the private paperdoll FBO before its 2-D widget blit; +13. tick/update-cursor/draw retained gameplay UI; +14. render ImGui menus and developer panels above gameplay UI; +15. capture a requested default-framebuffer screenshot; +16. publish window-title, resource-dump, and frame-profile diagnostics; +17. close the exact GPU frame once in `finally`. + +Within `RetailPViewRenderer.DrawInside`: + +1. construct the main PView; merge per-building exterior floods only for the + synthetic outdoor root and build separate look-in frames for interior roots; +2. assemble/upload the one clip arena, prepare EnvCell batches, partition + statics/dynamics, and classify outside-stage dynamics; +3. draw each outside slice early: sky, terrain, and outdoor statics; +4. draw building look-ins: all far-Z punches, opaque shells, then transparent + shells plus statics/dynamics/emitters; +5. draw outside-stage dynamic meshes, Scene particles, and weather late; +6. draw one interior-root unattached Scene-particle pass; +7. flush the **landscape** shared-alpha scope; +8. for an interior root, perform the full depth clear, then write true-depth + exit seals; for an outdoor root, retain world depth and use far-Z entry + punches; +9. draw opaque EnvCell shells once, then transparent shell cells immediate + far-to-near; +10. draw cell statics as one cross-cell batch, then their particles; +11. draw the surviving main-flood dynamics last, then their particles; look-in + and outside-stage dynamics remain in their earlier landscape phases. + +The landscape alpha flush must never move across the depth clear. The final +world alpha close must never move after portal/paperdoll private viewports. + +## 4. Architecture and interfaces + +### 4.1 Frame transaction + +Use immutable values equivalent to: + +```csharp +internal readonly record struct RenderFrameInput( + double DeltaSeconds, + int ViewportWidth, + int ViewportHeight); + +internal readonly record struct WorldRenderFrameOutcome( + int VisibleLandblocks, + int TotalLandblocks, + bool NormalWorldDrawn); +``` + +Portal-viewport selection and drawing belong to `PrivatePresentationRenderer`, +which runs after the world phase. If a combined public outcome becomes useful, +the top-level orchestrator composes it only after private presentation; the +world owner never claims that a later viewport was drawn. + +`RenderFrameOrchestrator` receives a small fixed set of typed phase owners. It +does not receive every renderer separately, a mutable frame-services bag, or +`GameWindow`. + +`BeginFrame` remains outside the `try`: if it fails, no `EndFrame` is attempted. +After begin succeeds, every failure closes exactly once. Render failure plus +close failure becomes the current explicit `AggregateException`; close-only +failure propagates directly. Alpha, particle-visibility, and selection-scene +completion retain their current non-finally behavior; changing that recovery +contract requires separate approval and evidence. + +### 4.2 Borrowed resources and shutdown + +Slice 7 collaborators borrow the already-created GL/render owners. They do not +dispose them. The existing retryable shutdown transaction keeps its proven +dependency order: wait for submitted work; withdraw UI/render publications; +release textures and mesh owners; then dispose GL backing stores, with +`GpuFrameFlightController` last among render resources. Slice 8 may group +construction/disposal after the render cutover is stable. + +`DisplayFramePacingController` is the focused shared owner for requested VSync, +live display preview, monitor/window callbacks, and the separate +`OnFrameRendered` pacing event. Render preparation and window lifecycle wiring +invoke that typed owner directly; neither calls back into a substantial +`GameWindow` method. Its existing disposal remains in the Slice 7 shutdown +transaction. + +### 4.3 World frame data + +`WorldRenderFrameBuilder` owns reusable classification scratch: + +- animated/equipped entity IDs; +- synthetic outdoor node and candidate building cells; +- previous-frame drawable cells used by point-light candidacy; +- camera/root/classification facts with one-frame borrowed lifetime. + +The builder owns only facts available before PView execution. Slice-local +visible/outdoor/unattached particle-owner IDs belong to +`RetailPViewPassExecutor`. The current interior partition is produced by +`RetailPViewRenderer.DrawInside` and remains in its borrowed result/diagnostic +snapshot; it is never stored as stale pre-draw builder state. + +It queries canonical `LiveEntityRuntime`, `GpuWorldState`, player-mode/camera, +physics cell graph, cell visibility, and landblock presentation sources. It +does not copy their dictionaries or create a second visibility computation. +Returned collections are borrowed until the next build; tests pin this +lifetime and consumers cannot retain them. + +### 4.4 PView pass execution + +Replace the current `RetailPViewDrawContext` callback bag with a data-only +`RetailPViewFrameInput`, a typed cell-visibility source, and one explicit +`IRetailPViewPassExecutor`/`RetailPViewPassExecutor`. The executor owns the GL +resources needed for these named operations: + +```text +SetTerrainClip +DrawLandscapeSlice +DrawLandscapeSliceLate +ClearInteriorDepth +DrawExitPortalMask +DrawLookInPortalPunch +DrawUnattachedSceneParticles +FlushLandscapeAlpha +DrawCellParticles +DrawDynamicsParticles +EmitDiagnostics +``` + +`RetailPViewRenderer` remains the retail ordering owner and calls these typed +operations. The executor may not call `GameWindow` or make independent +visibility/order decisions. Existing borrowed `RetailPViewFrameResult` scratch +semantics remain explicit. + +### 4.5 Presentation and diagnostics + +`PaperdollFramePresenter` owns dirty state, DAT-backed clone/re-dress, pose +selection/application, and FBO rendering. Inventory/session paths call +`MarkDirty`; they do not mutate its internals. + +`PrivatePresentationRenderer` owns portal viewport, paperdoll, retained UI, +ImGui, and screenshot order. A focused panel-layout/devtools owner handles menu +actions and is also reused by framebuffer resize; it cannot call a substantial +window method. + +`WorldRenderDiagnostics` owns pass-local PView/GL/scissor state, render +signatures, and world-pass timings. `RenderFrameDiagnosticsController` owns +FPS/frame-time accumulation, final visible-landblock counters, performance +title/resource publication, and the public render-frame snapshot. Only +render-derived DebugVM providers move in Slice 7. Update/session/streaming +providers and the cross-domain world-lifecycle resource sampler stay at the +composition boundary until Slice 8 rather than turning either diagnostics +owner into a replacement service locator. Diagnostics observe the frame; they +never choose a draw branch or mutate canonical world ownership. + +## 5. Implementation checkpoints + +### A — contracts, architecture, and oracles + +- correct the stale render-pipeline and per-frame architecture sections; +- add `RenderFrameInput`, phase interfaces, and a recording orchestrator; +- pin normal order, portal replacement, login wait, indoor/outdoor/null-root + branches, and screenshot placement; camera is an initialized runtime + invariant, so the slice does not invent a new graceful missing-camera path; +- pin the exact GPU begin/end exception matrix; +- add source guards against `GameWindow` back-references, mega contexts, and a + new PView delegate bag; +- capture the current connected framebuffer/resource baseline. + +### B — leaf presentation and diagnostics + +- extract paperdoll refresh/pose/dirty/FBO ownership; +- extract panel-layout reuse and devtools menu/panel rendering; +- extract pass-local render signature, PView/GL/scissor probes, and timing state + into `WorldRenderDiagnostics`; extract FPS, final counts, title/resource + publication, and the public render snapshot into + `RenderFrameDiagnosticsController`; +- preserve the pre-presentation render-derived DebugVM sampling site separately + from the post-screenshot title/resource publication site; leave non-render + DebugVM providers and cross-domain world-lifecycle sampling at composition; +- keep call sites in their current `GameWindow.OnRender` positions. + +### C — frame resources and live preparation + +- extract ordered per-resource `BeginFrame` calls, clear/state establishment, + histogram probe, mesh upload, reveal evaluation, and particle begin; +- extract weather accumulation and display foundations that do not consume the + camera/root classification, preserving their current render-time positions; +- consolidate requested-VSync/live-preview, monitor callbacks, and + `OnFrameRendered` pacing through `DisplayFramePacingController`, while + leaving its disposal in the existing shutdown transaction; +- prove `WbMeshAdapter.Tick` remains before reveal evaluation and drawing; +- prove dispatcher begin occurs once for the whole world/paperdoll frame. + +### D — world frame builder + +- move animated/equipped classification, camera/root selection, outdoor-node + building gather, and prior-visible-cell light scratch out of the window; +- after those facts exist, run focused audio/listener, live preview, sky PES, + sun/fog/point-light, and lighting-UBO preparation; do not build an interim + `GameWindow` mega-context; +- keep PView-generated particle-owner sets in `RetailPViewPassExecutor` and the + post-draw partition in the borrowed PView result/diagnostic snapshot; +- pin viewer-cell versus player-cell roles, portal replacement, login wait, + fallback/null-root, sealed/SeenOutside, and borrowed scratch reset; +- preserve allocation-free warmed frames and hysteretic scratch retention. + +### E — typed PView pass executor + +- replace `Action`/`Func` draw callbacks in `RetailPViewDrawContext`; +- move landscape early/late, scissor/clip, depth seal/punch, particle passes, + landscape alpha flush, and PView diagnostics to the executor; +- add a recording executor test for the complete `DrawInside` call sequence; +- verify no executor operation rebuilds portal visibility or retains a borrowed + frame result across calls. + +### F — world scene owner + +- extract alpha/selection/particle-visibility begin/end and the normal-world + conditional; +- extract sky/terrain/null-root fallback and unified PView branches; +- extract post-world particles/weather and debug collision drawing; +- return a small `WorldRenderFrameOutcome` for diagnostics/presentation; +- delete the world draw body and helper closure from `GameWindow`. + +### G — final orchestration cutover + +- compose concrete resource, world, private-presentation, and diagnostic owners; +- replace `OnRender` with one `RenderFrameOrchestrator.Render` handoff; +- remove obsolete fields/helpers/callbacks and prove the transitive render graph + has no substantial path back to `GameWindow`; +- preserve the existing shutdown resource list without transferring disposal; +- measure line/field/method counts. Ownership decides completion; the expected + signal is roughly 5,000–5,500 lines before Slice 8 composition cleanup. + +### H — release and connected gates + +After three clean corrected-diff reviews: + +1. run focused App/Core render, PView, alpha, portal, screenshot, and resource + tests; +2. run `dotnet build AcDream.slnx -c Release`; +3. run the complete Release suite; +4. run `tools/run-connected-world-lifecycle-gate.ps1 -SkipBuild`; +5. run `tools/run-connected-r6-soak.ps1 -SkipBuild`; +6. require code-zero graceful exits, exact lifecycle checkpoints/PNGs, no + fatal/invariant/reveal/pending-resource failures, and unchanged same-location + resource tolerances; +7. compare the six existing lifecycle/reconnect PNGs (`capped_login`, + `aerlinthe_first`, `facility_hub`, `holtburg_after_dungeon`, + `aerlinthe_revisit`, and `uncapped_reconnect`) with the Slice 6 baseline; +8. audit architecture, divergence pointers, roadmap, milestones, issues, + `AGENTS.md`, `CLAUDE.md`, and durable memory. + +The accepted baseline at docs-only commit `9512404e` is behavior-equivalent to +production cutover `e91f3102`: 7,182 passed / 5 skipped, 314.195-second +lifecycle/reconnect, and 393.581-second soak. Exact counters are recorded in +`memory/project_gamewindow_decomposition.md` and the Slice 6 closeout ledger. +Building/doorway poses, an open paperdoll, transient portal-exit visuals, and +#225 remain user visual gates; the stable post-materialization PNGs do not +overclaim them. + +## 6. Mandatory review gate + +After every implementation checkpoint, three read-only reviews run: + +1. **Retail conformance:** named anchors, PView/alpha/depth/private-viewport/UI + order, and registered adaptation boundaries. +2. **Architecture/integration:** focused owner scope, no replacement god object, + no service locator/back-callback, render-thread mutation, borrowed lifetime, + GL-state symmetry, and shutdown dependency direction. +3. **Adversarial:** begin/end failures, missing resources, portal/login + branches, stale borrowed scratch, callback reentrancy, frame spikes, + mid-frame resource churn, and GUID/session replacement. + +The primary agent fixes confirmed findings at their root, then requests +corrected-diff re-review until clean. Each checkpoint then runs focused tests, +Release build/full suite, divergence/doc audit, and lands as one commit. + +## 7. Final acceptance matrix + +### Automated + +- exact orchestrator order for normal, login, portal, indoor, outdoor, and + fallback frames, plus construction-time rejection of missing required phase + owners; +- exact GPU begin/end/aggregate failure behavior; +- PView landscape/look-in/flush/clear/seal/shell/static/dynamics ordering; +- screenshot after retained UI/ImGui, before title/resource/frame-profile + diagnostics, and before GPU `EndFrame`; +- dispatcher frame begin once across world and paperdoll scopes; +- frame scratch reset/reuse after skipped, portal, normal, and failed frames; +- no `GameWindow` reference or anonymous draw delegate bag in extracted owners; +- existing PView replay, shared-alpha, particle visibility, portal, paperdoll, + GPU-fence, screenshot, resource, and structural suites remain green; +- Release build and all tests green; +- connected lifecycle/reconnect and synchronized soak reports pass. + +### In-client campaign gate + +- outdoor dense scenes, terrain, sky, weather, particles, and selection match; +- buildings, doorway look-in/out, depth seals/punches, and dynamics stay stable; +- sealed dungeons keep correct visibility and lighting; +- portal replacement and destination reveal preserve the accepted flow; +- paperdoll, retained gameplay UI, cursor, and ImGui layering are unchanged; +- no resource growth, GPU reset, render exception, or shutdown regression. + +## 8. Fixed assumptions + +- all render/GL mutation remains on the existing render thread; +- `DatCollection` remains the only DAT reader; +- `LiveEntityRuntime`, `GpuWorldState`, cell visibility, session, and resource + owners remain canonical and are not copied; +- existing render resources are borrowed through Slice 7 and disposed by the + current shutdown transaction; +- frame-local results and scratch collections are borrowed until the next + frame/call unless explicitly copied; +- no quality/range/performance tradeoff is introduced to make the extraction + easier; +- missing optional diagnostic/UI owners skip only their own presentation and + never suppress world drawing or GPU frame closure.