acdream/docs/plans/2026-07-22-gamewindow-slice-7-render-frame-orchestration.md
Erik 96f8bfcfa8 docs(architecture): close GameWindow render slice
Record the clean Release, lifecycle, soak, and framebuffer gates for Slice 7, advance the structural program to Slice 8, and preserve the observed process-residency diagnostic gap as issue 232 rather than weakening its leak threshold.\n\nCo-authored-by: Codex <codex@openai.com>
2026-07-22 08:40:52 +02:00

602 lines
32 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# GameWindow Slice 7 — render-frame orchestration
**Status:** Complete 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
- [x] A — freeze the complete render graph, correct the architecture SSOT, and
introduce data-only frame contracts plus deterministic order/failure tests.
- [x] B — extract paperdoll, panel-layout/devtools, and render-diagnostics leaf
owners while `GameWindow` still orders the frame.
- [x] C — extract frame-resource begin/upload/publication and live display,
and weather/display foundations that do not depend on camera/root
classification.
- [x] 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.
- [x] E — replace `RetailPViewDrawContext`'s callback bag with a typed
`RetailPViewPassExecutor` and data-only inputs.
- [x] F — extract `WorldSceneRenderer`, including both shared-alpha scopes,
PView/fallback world branches, particles, debug world draw, and completion.
- [x] G — compose `RenderFrameOrchestrator`, cut `GameWindow.OnRender` to one
handoff, and delete obsolete frame bodies, fields, helpers, and callbacks.
- [x] 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
The frame orchestrator and phase owners borrow the already-created GL/render
owners and do not dispose them. The developer-tools backend is itself the
canonical owner of its ImGui bootstrap and disposes that bootstrap when the
existing retryable shutdown transaction retires the backend; the frame
presenter only borrows it. Shutdown 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 the outer resource/world/private/diagnostic order across normal,
portal-replacement, login-wait, and screenshot outcome shapes; defer the
concrete indoor/outdoor/null-root branch tests to the world builder and scene
owner in D/F; 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.
Completed 2026-07-22. `RenderFrameOrchestrator` now owns the inert five-owner
outer transaction contract; `GpuFrameFlightController` implements its narrow
lifetime seam. Twenty-six focused render/GPU tests pin value-only contracts,
required-owner construction, exact phase prefixes, per-phase input propagation,
and every begin/render/close failure combination. Release build and the full
suite pass (7,199 passed / 5 skipped after adding 17 tests). All three
corrected-diff reviews are clean; production `GameWindow.OnRender` is unchanged.
### 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.
Completed 2026-07-22. Paperdoll clone/re-dress/pose/FBO work now belongs to
`PaperdollFramePresenter`; retained surfaces are narrow and optional, while
live identity and DAT pose application resolve through focused adapters.
`DevToolsFramePresenter` owns the ImGui frame, menus, panels, layout, and input
actions while borrowing the bootstrap; `ImGuiDevToolsFrameBackend` explicitly
owns and disposes that bootstrap at shutdown.
`WorldRenderDiagnostics`, `DebugVmRenderFactsPublisher`, and
`RenderFrameDiagnosticsController` own the former render probes, debug facts,
title/resource cadence, and public snapshot. The production handoff now reports
the observed login/world and screenshot outcomes, preserves the original shared
TERRAIN→FRAME diagnostic transaction, and keeps the devtools-off scan path
allocation-free. `GameWindow.cs` is 6,270 raw lines, 60.1% below the 15,723-line
campaign baseline. Sixty-two focused tests, the Release build, and the complete
suite pass (7,255 passed / 5 skipped). Retail, architecture, and adversarial
corrected-diff reviews are clean.
### 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.
Completed 2026-07-22. `RenderFrameResourceController` now owns the exact
resource-begin → atmosphere/GL-clear → live-upload/reveal/particle transaction,
with a single captured GPU slot shared by every phase. Focused sources own the
login reveal cell, teleport render state, upload timing, and render-time weather
clock. `DisplayFramePacingController` owns requested VSync, the event-refreshed
monitor-rate cache, software pacing, and the post-render callback without adding
a monitor query to the frame hot path. Borrowed frame owners are withdrawn before
render teardown; portal fallback ownership transfers before the throwable sink
bind; pacing releases its borrowed surface/profiler before their owners close.
`GameWindow.cs` is 6,181 raw lines, 60.7% below the 15,723-line campaign
baseline. Twenty-one focused tests, the Release build, and the complete suite
pass (7,269 passed / 5 skipped). Retail, architecture, and adversarial
corrected-diff reviews are clean.
### 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.
Completed 2026-07-22. `WorldRenderFrameBuilder` now owns the ordered camera,
visibility, live settings/listener, player-lighting versus collided-viewer root,
sky/lighting/fog, animated-ID, and building-candidate preparation. Reusable
animated/building/light-cell collections retain their explicit one-frame
borrowed lifetime; PView drawable-cell feedback returns through the builder and
is copied before the renderer reuses its own scratch. The diagnostic sky-PES
experiment moved to `SkyPesFrameController`, and fog startup overrides now enter
through typed `RuntimeOptions`. The frame graph is withdrawn before its borrowed
equipped/effect/audio owners during shutdown. `GameWindow.cs` is 5,715 raw
lines, 63.6% below the 15,723-line campaign baseline. Twenty-nine focused tests,
the Release build, and the complete suite pass (7,281 passed / 5 skipped).
Retail, architecture, and adversarial corrected-diff reviews are clean.
### 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.
Completed 2026-07-22. `RetailPViewRenderer` is now the retail visibility and
ordering owner only; `RetailPViewPassExecutor` owns the concrete clip upload,
EnvCell/entity, landscape, portal depth, particle, alpha, and diagnostic pass
implementations. The old callback bag and broad window closures are gone.
`WorldRenderDiagnostics` now owns every PView print-on-change signature and
probe sink, while a focused terrain-diagnostics controller preserves the
shared five-second terrain/frame publication transaction across fallback and
PView draws. Real `DrawInside` fixtures cover outdoor, interior-exit,
no-outside, entity/static/dynamic particle, transparent-shell, look-in, and
second-call scratch-reset behavior; structural tests pin concrete forwarding,
no delegate/back-reference leakage, no borrowed frame/result retention, and
early shutdown withdrawal. `GameWindow.cs` is 5,303 raw lines, 66.3% below the
15,723-line campaign baseline. Release build and the complete suite pass
(7,295 passed / 5 skipped). Retail, architecture, and adversarial
corrected-diff reviews are clean.
### 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`.
Completed 2026-07-22. `WorldSceneRenderer` now owns the normal-world
transaction, one shared-alpha frame, retail selection accumulation, PView versus
null-root fallback branching, post-world particles/weather, visibility
publication, and world diagnostics. `WorldScenePassExecutor`, focused runtime
sources, and `WorldSceneDiagnosticsController` carry the concrete GL and data
seams without retaining `GameWindow`, delegates, or borrowed PView products.
Failed frames abort PView, pass routing, particle visibility, alpha, and
selection in reverse ownership order; one shared `RenderFrameGlStateController`
restores scissor/stencil/blend/clip/mask/depth/cull/program/buffer state before
the next clear. Normal portal-depth exits now restore the same cull-off
convention, and alpha cleanup preserves the original draw failure while
attempting every source reset. `GameWindow.cs` is 4,765 raw lines, 69.7% below
the 15,723-line campaign baseline. Release build and the complete suite pass
(7,313 passed / 5 skipped). Retail, architecture, and adversarial
corrected-diff reviews are clean.
### 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; prove immediate frame owners retain
no direct `GameWindow`/anonymous-delegate back-reference, and document rather
than deny recursive paths through borrowed canonical UI/input owners;
- preserve the existing shutdown dependency order while making the
developer-tools backend's pre-existing bootstrap ownership explicit;
- measure line/field/method counts. Ownership decides completion; the expected
signal is roughly 5,0005,500 lines before Slice 8 composition cleanup.
Completed 2026-07-22. `GameWindow.OnRender` now creates one value-only
`RenderFrameInput` and hands the complete frame to `RenderFrameOrchestrator`.
Focused preparation, world-scene, private-presentation, and diagnostic owners
preserve the accepted resource → ImGui begin → weather → world → portal →
paperdoll → retained UI → ImGui submit → screenshot → diagnostics → GPU close
order. Recovery now closes an active ImGui frame through the owning Silk
controller and restores the exact text-render GL state after success or
failure. Screenshot dimensions travel in the frame input rather than through a
window callback, and immediate frame owners have no direct `GameWindow` or
anonymous-delegate back-reference. `GameWindow.cs` is 4,666 raw lines / 196
fields / 70 methods: 11,057 lines (70.3%) below the 15,723-line campaign
baseline. The Release build and complete suite pass (7,341 passed / 5 skipped),
and retail-conformance, architecture, and adversarial corrected-diff reviews
are clean.
### 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.
Completed 2026-07-22. The 315.6-second lifecycle/reconnect gate passed at
`9d7df1bf` with six valid PNG checkpoints, code-zero graceful capped exit, and
fresh-process uncapped reconnect. Side-by-side comparison with the Slice 6
artifacts preserved world geometry, UI/paperdoll layering, private viewports,
and depth/presentation; only live weather, particle timing, authoritative
vitals, and small scripted camera-settling differences changed. The unchanged
nine-stop gate then passed in 395.2 seconds with nine materializations, all
Caul/Holtburg movement exercises, code-zero graceful shutdown, update p95 at
or below 0.8 ms, and same-location Caul plateau deltas of +85.7 MiB working set
and +59.3 MiB private memory. Two immediately preceding identical-binary runs
crossed the coarse process-residency limit while every other deterministic
lifecycle, timing, and shutdown check passed; #232 records the need to add
canonical managed/GPU/cache owner snapshots to that gate instead of weakening
its leak threshold. Release build and the complete suite remain green at 7,341
passed / 5 skipped. Slice 7 is closed with no new retail divergence.
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.