acdream/docs/research/2026-09-01-overhaul/oh1-retail-world-contract.md

21 KiB

OH1 — retail world construction and frame contract

Date: 2026-09-01

Campaign: OVERHAUL / OH1

Status: evidence synthesis; tooling implementation and review gate pending

Behavioral changes: none in this document

1. Decision

Retail does not build a set of portal-clipped copies of ordinary world meshes. It builds portal views, uses each installed view to perform Boolean admission, and submits a complete built mesh after the first successful admission. A render stamp suppresses later ordinary submissions in the same stamp. The local player is a deliberate exception to the ordinary part-stamp gate.

The solid world is therefore the composition of four exact mechanisms, not a single clipping heuristic:

  1. one canonical object-registration transaction determines the ordered cells crossed by the object and each render part;
  2. the PView walk determines cell/view admission and exact traversal order;
  3. ordinary built parts use sphere admission followed by a whole-mesh submission, built EnvCell shells use a direct stamp gate followed by a whole-shell submission, and portal mask polygons alone use polygon clipping;
  4. the depth epoch, two render-stamp boundaries, landscape/building interleave, and two FIFO alpha lists determine which admitted surfaces finally cover which others.

The current acdream path already has a materially aligned candidate for the ordinary part rule: it tests the live views, selects slot zero after admission, emits one whole mesh, stamps after admission, and preserves the local-player bypass. The remaining divergences are membership reconstruction, landscape/ depth order, the portal-fan approximation, and alpha ownership. Campaign OVERHAUL must preserve the proven ordinary-part behavior while repairing those owners rather than tune cathedral-specific culling.

2. Evidence packets and authority

This synthesis is backed by the following focused reports:

  • oh1-construction-landscape-contract.md — static-object construction, crossed-cell membership, child propagation, and landscape leaf order;
  • oh1-built-mesh-view-contract.md — installed views, built-mesh admission, draw stamps, and alpha ownership;
  • oh1-alpha-list-contract.md — exact subset-mask priority, delayed-list classification, preference/detail inputs, capacity, and flush barriers;
  • oh1-depth-lifecycle.md — outside-view gate, persistent portal counter, depth clear/punch/seal state, and both render-stamp advances;
  • oh2-cellstruct-surface-contract.md — exact CellStruct side, surface, subset, mask, and built-EnvCell material admission contract.

The named Sept. 2013 retail corpus was inspected first. Branch facts that could change the result were independently arbitrated in the live Ghidra project. The load-bearing addresses are:

Retail function Address Contract owned here
CPhysicsObj::calc_cross_cells_static 0x00515160 object and part cell membership
CPartArray::AddPartsShadow 0x00517E40 per-part render-shadow insertion
CPhysicsObj::add_shadows_to_cells 0x00514AE0 shared ordered CELLARRAY consumption
PView::DrawCells 0x005A4840 outside gate, list direction, depth epoch
Render::copy_view / Render::set_view named/Ghidra report view polygon/planes and global installation
RenderDeviceD3D::DrawEnvCell 0x0059F170 shell stamp and complete-shell draw
RenderDeviceD3D::DrawMeshInternal 0x0059F360 ordinary part admission/stamp
D3DPolyRender::DrawMesh 0x0059D4A0 material subset admission/alpha routing
D3DPolyRender::AddMeshToAlphaList 0x0059C230 FIFO alpha-list ownership
D3DPolyRender::FlushAlphaList 0x0059D2E0 clip-list then alpha-list drain
DrawPortalPolyInternal 0x0059BC90 the actual polygon-clipped depth fan
RenderDeviceD3D::Flip 0x0059FE50 normal per-present render-stamp advance

No visual observation is used to decide an object identity, branch direction, surface side, or list order.

3. Canonical construction and membership

3.1 Static object registration

For an authored EnvCell static, retail creates the CPhysicsObj, enters it in the authored cell, copies the authored frame, updates children, and then runs static crossed-cell calculation. That calculation owns one CELLARRAY and produces both object-level physics membership and part-level render-shadow membership.

The exact high-level transaction is:

create static object
add_obj_to_cell(authored cell)
copy/set authored frame
update children
calc_cross_cells_static
  initialize CELLARRAY in source order
  choose retail sphere or per-part bounding-box route
  remove prior shadows
  add new CShadowObj entries to every crossed cell
  AddPartsShadow for the exact part/cell intersections
  propagate the same CELLARRAY to children

The bounding-box route evaluates visual parts in part order. A part may use its physics sphere, or its drawing sphere for the quick portal-plane reject, then its exact transformed bounding box for portal intersection and neighbour containment. An outside crossing is retained by this route; it is not pruned later merely because an aggregate owner cell appears preferable.

add_shadows_to_cells consumes the same ordered CELLARRAY twice: once for object-level CShadowObj membership and again for exact part-shadow entries. Children inherit the root object's transaction. Removal is symmetric. The particle-state special case uses its one current-cell route and must remain an explicit typed case.

3.2 Ownership consequence

Render and physics membership are sibling outputs of one canonical registration operation. Neither is allowed to reconstruct membership from a per-frame scene record, an aggregate owner id, or a fresh AABB query.

Cell traversal order and part-shadow insertion order are related only at the draw join:

  • registration fixes the stable order of each cell's shadow objects and parts;
  • PView fixes the cells and views visited in the current frame;
  • the object leaf draws the already-registered shadows for that cell, with retail's CShadowPart::insertion_sort establishing the cell-local order.

PView traversal must not recreate part membership, and membership construction must not predict a later PView order.

4. Canonical view and built-mesh contract

4.1 What a retail view does

Render::copy_view copies the view polygon and derives its edge planes. Render::set_view installs that view in render globals. It does not establish a hardware scissor or a general polygon clip for every following mesh. Render::viewconeCheck is the ordinary sphere admission test.

4.2 Ordinary built object

For an ordinary built GfxObj part, retail walks the live views for the cell:

  1. install a view;
  2. test the part's sphere;
  3. if rejected, try the next view without stamping the part;
  4. on the first accepted view, enter DrawMeshInternal, stamp the non-player part, and submit the complete eligible constructed mesh;
  5. later accepted views encounter the stamp and do not submit another copy.

Thus multiple surviving object views normally produce repeated admission tests followed by one whole-mesh draw, not multiple portal-clipped copies.

The local player bypasses the inner ordinary-part stamp. It can therefore be submitted whole for more than one admitted cell/view. That retail exception is load-bearing at seams and must not be normalized into the ordinary object rule.

4.3 Built EnvCell shell

DrawEnvCell has no sphere admission. It gates the cell directly by the current render stamp before doing work. The built branch submits the complete eligible constructed shell once for that stamp. The forced building pass is separate: it draws the building portal BSP fans; it is not a clipped substitute for the ordinary shell.

4.4 Geometry that is actually clipped

Retail polygon clipping occurs in the portal machinery (PView::GetClip and DrawPortalPolyInternal through polyClipFinish). It produces the far-depth punch and true-depth seal fans. It is not the submission path for normal built EnvCell shells, static parts, dynamic parts, or the local player.

5. Canonical frame, landscape, depth, and stamp order

5.1 Two different timestamps and two render-stamp advances

PView::master_timestamp belongs to visibility construction. RenderDevice::m_nFrameStamp belongs to render deduplication. They are not interchangeable.

The render stamp normally advances once on every RenderDeviceD3D::Flip return path after the Present retry loop, including a device-lost result. When an interior DrawCells has at least one outside view, retail advances it a second time in the middle of that frame, after landscape and its alpha flush and before the previous-call portal counter is consumed. The mid-frame advance re-arms content drawn through landscape/building look-ins for the later interior-root repaint.

5.2 Interior DrawCells

The binding order is:

if outside_view.view_count != 0:
    useSunlightSet(1)
    draw landscape with Render::PortalList = outside_view
      sky
      for each admitted land cell in retail order:
        DrawLandCell
        DrawSortCell
          building pass 1 / look-in work / building shell as applicable
          land-cell object turn
      weather
    flush landscape alpha
    increment render stamp                 # conditional mid-frame barrier
    consume previous portalsDrawnCount
    reset that uint16 counter to zero
    full viewport depth clear iff consumed count != 0
    emit current true-depth exit-seal attempts
      for reverse cells, every live view, every outside portal

useSunlightSet(0)
restore interior lighting
draw EnvCell shells in reverse cell_draw_list order
draw object cells in reverse cell_draw_list order
reset object scale
useSunlightSet(1)
return to caller
flush top-level alpha at the caller's barrier
later Flip/present increments render stamp # normal frame boundary

When outside_view.view_count == 0, the entire landscape/intermediate-flush/ mid-stamp/counter-consume/clear/seal block is skipped. Any pending portal count survives the gap. The reverse shell/object passes and sunlight tail still run, and the normal later Flip increment still occurs.

5.3 Persistent portal counter

portalsDrawnCount is a persistent machine uint16. A non-far DrawPortalPolyInternal attempt increments it after the local-boundary reject but before polyClipFinish; the increment therefore does not prove that a fan survived clipping. The next eligible outside-view DrawCells consumes and resets the prior count. Arithmetic wraps modulo 65,536. Far-depth punches do not increment it.

5.4 Portal mask state

The far punch uses exact depth bits 0x3F7FFFEF. Before xformStart, retail rejects the whole source polygon if all local input vertices lie on any one of x == +12, x == -12, y == +12, or y == -12.

An accepted portal fan uses no texture, alpha test off, SRCALPHA/INVSRCALPHA/ADD, depth test ALWAYS, depth write on, cull none, and normal color writes enabled. Its source alpha is zero, so the blend preserves destination color while depth changes. Disabling color writes is pixel-outcome-equivalent for that fan, but it is not the retail state.

The current GPU gl_ClipDistance route cannot report retail's clipped vertex count or whether polyClipFinish produced a fan. Those fields must remain explicitly unavailable until the CPU clip is ported or audited; they must not be inferred from CPU submission.

6. Canonical built-EnvCell surface and alpha contract

CellStruct sides are selected only by sides_type. NoPos and NoNeg mean that a UV-index array is absent; they do not remove a face. A missing UV array uses UV index/coordinates zero.

sides_type Ordered constructed candidates
ST_SINGLE (0) positive surface, positive normal, ordinary fan
ST_DOUBLE (1) positive candidate, then the same positive surface with negative normal and reversed fan
ST_BOTH (2) positive candidate, then negative surface/UV with negative normal and no index reversal

The semantic subset/material owner is the source surface-array index. Nonempty subsets are emitted in ascending surface-index order. Equal Surface DIDs in two slots remain distinct. Stippling and format grouping do not create the owner.

The built EnvCell later draws a subset exactly when:

(Surface.Type & (BASE1_IMAGE | BASE1_CLIPMAP)) != 0

Untextured candidates are constructed and then skipped by this built-cell draw rule. Ordinary GfxObj behavior has a different fallback and must not inherit this filter.

Retail owns two delayed mesh lists: clip first, then alpha. Both are FIFO; no distance sort is performed at flush. Ordering was already established by the cell walk and cell-local shadow-part sort. The exact constructed-mask priority, signed positive-surface stippling quirk, immediate/delayed branch table, material-alpha fallback, multipass clip-map duplicate, detail/sky gates, capacity behavior, preference defaults, and flush thresholds are established in oh1-alpha-list-contract.md. A delayed entry carries no portal view, cell, or part context, so queuing a portal-slice mesh for later cannot reproduce retail.

7. Direct answers required by OH1

Which built meshes are submitted whole after a Boolean sphere test?

Ordinary built GfxObj parts. They try live views until the first sphere admission and then submit the complete eligible mesh. Built EnvCell shells do not have a sphere test; they are stamp-gated directly and drawn complete.

Which geometry is polygon-clipped by the installed portal view?

Portal clip/punch/seal polygons in the PView and portal-depth paths. Normal built shells and object meshes are not clipped into per-aperture copies.

Do multiple surviving views cause multiple built-mesh draws?

Normally no: they cause repeated admission tests until the first accepted whole draw stamps the part. The local player deliberately bypasses that inner part stamp and is an exception.

Which stamp applies to landscape look-ins versus the interior root?

Landscape and its building look-ins draw under the stamp established by the prior Flip. An eligible interior DrawCells then advances the stamp mid-frame, so the root repaint uses the next stamp. Every Flip return path after the Present retry loop advances it again for the following frame, including the device-lost outcome.

Which list owns transparent EnvCell subsets?

When retail delays a subset, mask bit 0x08 selects the CLIP FIFO; other delayed masks select the ALPHA FIFO. Flush drains CLIP before ALPHA. A non-null current detail surface, sky rendering, or zero global delay mask forces immediate rendering. With default delay mask 0x0E, masks 2/3/4/5 enter ALPHA and masks 8/9 enter CLIP; multipass clip maps also render an immediate base pass. Masks 0/1 are immediate unless the current material's alpha fallback applies. oh1-alpha-list-contract.md is the executable table.

What increments portalsDrawnCount, and when is it cleared?

Every accepted non-far portal-mask attempt after the local-boundary reject, even if clipping later yields fewer than three vertices. The next outside-view-bearing DrawCells consumes and clears it before current exit seals repopulate it. Zero-outside-view calls neither consume nor reset it.

How do cell traversal and shadow insertion relate?

Registration creates stable ordered shadow memberships first. PView later chooses and orders cell turns. The object leaf consumes the existing cell membership and retail sort; neither stage reconstructs the other.

8. Current acdream divergences and owning slices

Current behavior Retail contract Owning slice
CellStruct face admission uses NoPos/NoNeg; batches are texture/format/stipple keyed sides come from sides_type; UV absence only; source surface-index subsets OH2
App rebuilds render buckets from scene records/AABBs and aggregate owner cells one registration transaction publishes exact render/physics membership OH3
ordinary parts already test views, select slot zero after admission, emit once, stamp after admission, and preserve the player bypass materially aligned with retail whole-mesh admission; protect with executable transcript and remove only residue that contradicts it OH4 validation/deletion
all terrain is emitted before building/object turns DrawLandCell then DrawSortCell per admitted land cell OH5
clear/seals are emitted even with no outside view; no persistent prior-call counter entire depth block is outside-view gated; persistent modulo-uint16 counter OH6
far depth uses an approximate constant/boundary/clip path exact bits, local-input boundary reject, audited clip semantics OH6
one distance-sorted alpha queue retains slice context two FIFO lists, exact classification and barriers, no retained portal context OH7
landscape/particle/light consumers use reconstructed visibility unions each consumer borrows the exact retail walk and registration outputs OH8/OH9

The current architecture document overstates several of these candidate mechanisms as accepted retail behavior. OH1 must correct that SSOT before its review closes; later slices then replace each explicitly recorded divergence.

9. Executable evidence contract

OH1 tooling uses a versioned semantic transcript. Stable source IDs, exact event order, integer states, and raw float bits participate in equality. Pointers, Vulkan handles, allocation addresses, timestamps, and upload offsets do not.

The required semantic record families are:

  1. frame header and presented-stamp transition;
  2. camera/player/root identity and raw pose bits;
  3. PView creation, todo insertion/pop, clipped portal view, and draw-list append;
  4. landscape cell admission and exact per-cell leaf order;
  5. building pass, look-in, alpha barrier, punch, and shell events;
  6. EnvCell shell and object-cell turns;
  7. part admission, source membership, and render stamp result;
  8. particle owner/degrade result;
  9. alpha enqueue/list/ordinal and flush site;
  10. outside-block counter, clear, punch, seal, sunlight, and stamp events;
  11. final draw-leaf sequence.

The depth transcript distinguishes current GPU submission from retail-only CPU clip facts. It never fabricates clippedVerts or fanDrawn while the current renderer cannot observe them.

The geometry manifest follows source polygon and surface-array order and records raw sides_type, stippling/UV presence, surface indices/DIDs/types, ordered constructed candidates, exact winding/sign lane, built-cell admission, retail mask, subset ordinal, and deterministic emitted-data hashes.

The membership manifest records stable object incarnation/kind, Setup and part identity, root/part transforms as raw bits, drawing sphere/part bounds, resident cell, every crossed shadow cell in insertion order, inheritance source, typed render/physics membership, and any finite fallback reason.

Schema and serializer implementation plus legacy FW fixture conversion are completed in the OH1 tooling commit. Geometry generation lands with OH2 so A8 delegates to the same exact descriptor used by production rather than copying the current wrong NoPos rule. Installed-DAT membership generation lands with OH3 beside the canonical graph owner; implementing it earlier in A8 would create the second membership owner this campaign deletes. OH1 still defines and tests both manifest schemas. Their exact type names are implementation details; the fields and equality rules above are binding.

10. Slice boundary and unresolved facts

  • OH1 changes evidence and diagnostics only; it may not alter a draw result.
  • Existing FW0 captures cover only frame/pose/DrawInside/DrawCells/landscape/ building entry. Full semantic equality requires a bounded richer retail capture before the owning later slices close; absent fields remain explicit null/uncovered and are never promoted from acdream's own output.
  • polyClipFinish @0x006B6D00 still needs a direct port/audit before OH6 can claim exact clipped-vertex/fan parity.
  • Alpha branch sense is closed in oh1-alpha-list-contract.md. Runtime registry/user values and nullable generated detail surfaces remain inputs that OH7 must represent exactly.
  • The pixel-level use of the installed outside-view list by every landscape leaf remains an OH5/OH8 audit item; the top-level ownership and order are already proven.
  • The installed Environment CellStruct corpus examined for OH2 has no ST_BOTH; synthetic coverage is mandatory despite the decomp being clear.
  • The canonical cathedral/Facility cells' NoPos candidates are untextured, so OH2 is a real global correctness fix but not a standalone explanation of the cathedral leak.
  • No cathedral-specific suppression, surface blacklist, larger bounds, or screen-space workaround is permitted by this contract.

11. Review checklist

  • all direct OH1 questions have a cited, executable answer;
  • both render-stamp advances are represented independently;
  • zero-outside-view calls preserve the persistent portal counter;
  • current GPU clip output is not misreported as retail CPU clip output;
  • local-player stamp bypass remains explicit;
  • whole-mesh admission is not described as portal polygon clipping;
  • alpha lists are FIFO and ordered clip-before-alpha;
  • membership is produced once and borrowed by render and physics;
  • CellStruct NoPos/NoNeg are UV flags only;
  • architecture SSOT is reconciled before OH1 closes;
  • no production draw result changes in the OH1 commits (close after tooling gate).