acdream/docs/research/2026-09-01-overhaul/oh1-alpha-list-contract.md

205 lines
10 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.

# OH1 evidence — retail alpha classification and list contract
**Campaign:** OVERHAUL / OH1
**Status:** evidence complete; behavior belongs to OH7
**Retail build:** September 2013 End of Retail
**Arbitration:** named-retail pseudo-C plus Ghidra/disassembly for field offsets
This note closes the bounded alpha ambiguity left by the first OH1 synthesis.
It is an executable contract for OH7, not permission to change alpha order
before OH5 restores the landscape/building/object walk that owns the barriers.
## 1. Retail anchors
| Retail symbol | Address | Contract established here |
|---|---:|---|
| `D3DPolyRender::ConstructMesh` | `0x0059DFA0` | initial/final subset mask |
| `D3DPolyRender::DrawMesh` | `0x0059D4A0` | immediate versus delayed branch table |
| `D3DPolyRender::AddMeshToAlphaList` | `0x0059C230` | list selection, capacity, entry state |
| `D3DPolyRender::FlushAlphaList` | `0x0059D2E0` | threshold and drain order |
| `D3DPolyRender::RenderMeshSubset` | `0x0059CA10` | delayed replay/detail behavior |
| `D3DPolyRender::SetSurface` | `0x0059C4D0` | clip-map override behavior |
| `D3DPolyRender::Begin` | `0x0059EAD0` | delay-mask registry binding |
| `RenderDeviceD3D::DrawEnvCell` | `0x0059F170` | environment-detail owner |
| `RenderDeviceD3D::DrawBuilding` | `0x0059F2A0` | building-detail owner and barrier |
| `RenderDeviceD3D::DrawPartCell` | `0x005A07A0` | ordinary-object detail reset |
| `RenderDeviceD3D::DrawBlock` | `0x005A18D0` | per-land-cell emergency barrier |
| `PView::DrawCells` | `0x005A4840` | post-landscape full barrier |
| `SmartBox::RenderNormalMode` | `0x00453AA0` | top-level full barrier |
| `LScape::CleanupDetailSurfaces` | `0x00504AE0` | detail-surface release/reset |
| `LScape::SetDetailTexturing` | `0x00506B40` | detail-surface regeneration |
| `LScape::ChangeRegion` | `0x00506CB0` | detail preference application |
| `GameSky::Draw` | `0x00506FF0` | `m_currentlyDrawingSky` producer |
| `Render::SetOverallGraphicsQuality` | `0x0054B020` | `MultiPassAlpha` quality defaults |
| `Render::Startup` | `0x0054E9B0` | preference registration/safe override |
The named-retail pseudo-C establishes the control flow. Ghidra and instruction
disassembly arbitrate the two facts for which a wrong field name changes the
branch meaning: the renderer byte at `+0x7E0` is
`m_currentlyDrawingSky`, while material-alpha mode is at `+0x794`; and polygon
stippling is compared as a **signed** byte.
## 2. Exact constructed subset mask
For each source surface, `ConstructMesh` chooses exactly one initial mask by
this priority:
```text
if (Surface.Type & 0x00010300) != 0: mask = 0x02
else if (Surface.Type & 0x00000004) != 0: mask = 0x08
else if (Surface.Type & 0x00000010) != 0: mask = 0x04
else: mask = 0x00
```
`0x00010300` is the union of `ALPHA (0x100)`, `INVALPHA (0x200)`, and
`ADDITIVE (0x10000)`. The order is material: a surface carrying both an
alpha-family bit and `BASE1_CLIPMAP` receives mask `0x02`, not `0x08`.
For each polygon, retail then ORs bit `0x01` into the **positive surface's**
mask only when `(signed char)polygon.stippling > 0`. Raw bytes `0x80..0xFF`
therefore do not set the bit. The complete constructed mask domain is:
| Surface class | Without positive stippling | With positive stippling |
|---|---:|---:|
| plain | `0x00` | `0x01` |
| alpha / inverse-alpha / additive | `0x02` | `0x03` |
| translucent | `0x04` | `0x05` |
| clip map | `0x08` | `0x09` |
Bit `0x01` is absent from the registry help text, but a configured delay mask
still intersects it normally. It is a retail quirk, not a flag to normalize.
## 3. Delay-mask and preference state
`s_AlphaDelayMask @ 0x00820D88` has static default `0x0E`. Its only behavior
read is in `DrawMesh`; `D3DPolyRender::Begin` exposes it as
`RenderD3D.AlphaDelayMask` with help for Alpha=`2`, Translucent=`4`, and
ClipMap=`8`. The registry may replace the value. Unsupported higher bits can
keep the outer delay gate nonzero but never intersect a constructed mask.
`m_RenderPrefs.MultiPassAlpha @ 0x0081EF96` has static default false.
`SetOverallGraphicsQuality` selects false for quality levels 13 and true for
levels 45. Startup registers the user preference, and the safe-render branch
forces it false. `DrawMesh` is its sole draw-path reader;
`DetermineOverallGraphicsQuality` also reads it while deriving the aggregate
quality setting.
The building and environment detail globals default null. Their only writers
are `SetBuildingDetailSurface` and `SetEnvironmentDetailSurface`; region
cleanup clears/releases them, and region detail setup regenerates both from
the `EnvironmentDetailTextures` preference. Generation may still yield null.
## 4. Exact `DrawMesh` branch table
After the subset passes its ordinary draw-eligibility gate, apply these rows
in order:
| Priority | Condition | Result |
|---:|---|---|
| 1 | `m_currentlyDrawingSky` **or** delay mask is zero **or** `curr_detail_surface != null` | render immediately; enqueue nothing |
| 2 | `MultiPassAlpha && (subsetMask & 0x08) != 0` | append to **CLIP** with `overrideClipmap=true`, then also render immediately with `overrideClipmap=false` |
| 3 | `(delayMask & subsetMask) != 0` | append to **CLIP** iff bit `0x08` is set, otherwise **ALPHA**; no immediate draw |
| 4 | `(delayMask & 0x04) != 0 && currentMaterial != null && currentMaterial.has_alpha != 0` | append to **ALPHA**; no immediate draw |
| 5 | otherwise | render immediately |
Row 2 depends on the global delay mask being nonzero because it is inside the
outer delay gate, but it does **not** require delay-mask bit `0x08`. It is the
clip-map base pass now plus an alpha-blended second pass later.
Under default delay mask `0x0E`:
- masks `0x02/0x03` and `0x04/0x05` enter ALPHA;
- masks `0x08/0x09` enter CLIP, with the additional immediate pass when
`MultiPassAlpha` is true;
- masks `0x00/0x01` are immediate unless the material-alpha fallback applies.
`overrideClipmap=false` lets `SetSurface` enable the clip-map alpha-test arm.
`overrideClipmap=true` selects the alpha-blend arm and suppresses that clip-map
alpha-test arm. Immediate rendering passes
`detailEnabled=(curr_detail_surface != null)`; delayed replay always passes
`detailEnabled=false`.
## 5. List entry, capacity, and replay
Retail owns two independent static arrays of 3000 `AlphaListEntry` records and
two `uint16` counts. `AddMeshToAlphaList` appends strictly FIFO; it performs no
distance or material sort.
Each `DrawMesh` invocation owns two independent `first-for-this-list` flags.
The first subset appended to each list captures the current `CMaterial` and
object matrix and sets `bNewObject=true`. Later entries for that list inherit
that state during replay. The entry also stores `overrideClipmap`; it stores no
portal view, clip planes, cell, or part identity.
When a list is already at capacity, append returns false. A non-multipass
delayed subset has no immediate fallback draw. Exact capacity behavior must be
preserved and tested rather than silently expanded or recovered differently.
`FlushAlphaList(threshold)` returns false without changing either list only
when **both** counts are below `threshold * 3000`. Otherwise it drains the
entire CLIP list in append order, resets it, then drains the entire ALPHA list
in append order and resets it. It restores the object matrix after replay; it
does not restore the material. The DrawBlock threshold is the immutable global
`0.75`, so equality at 2250 in either list triggers a full two-list drain.
## 6. Detail owner consequences
- `DrawEnvCell` installs the environment detail surface, calls the built
`DrawMesh(skipNoTexture=true)` path, then clears the current detail. A
non-null environment detail makes every eligible subset immediate.
- `DrawBuilding` installs building detail, performs `FlushAlphaList(0)` before
portal/part/shell work, then draws its forced portal-BSP pass and normal
built shell. A non-null building detail makes the shell immediate.
- `DrawBuildingLeaf` preserves outer state, but `DrawPartCell` explicitly
forces current detail null. Ordinary leaf parts can therefore enqueue even
when their containing building shell uses detail.
- Ordinary object-cell GfxObj paths do not install the separate object detail
surface; their current detail is normally null.
## 7. Exact normal-world flush barriers
1. `DrawBuilding` calls `FlushAlphaList(0)` after installing building detail
and before any building portal/leaf/shell draw. Delayed replay itself has
detail disabled.
2. `DrawBlock` visits each admitted land cell as `DrawLandCell` followed by
`DrawSortCell` (building, then objects), then calls
`FlushAlphaList(0.75)`. This is normally an emergency capacity drain.
3. `PView::DrawCells` calls `FlushAlphaList(0)` after outside landscape and
before the mid-frame stamp/depth-clear/interior repaint block.
4. `SmartBox::RenderNormalMode` calls `FlushAlphaList(0)` after the complete
world path.
The fifth retail caller is the private `CreatureMode::Render @ 0x004529D0`
(call site `0x00452BF0`). After `DrawObjCellForDummies(creature_cell)` and its
frame pop, that private presentation mode calls `FlushAlphaList(0)`. It is not
part of the normal-world OH5/OH7 ordering cutover, but it must remain an exact
consumer of the shared two-list implementation.
Consequently, OH7 cannot be implemented faithfully while current acdream
still stages all terrain before all building/object turns. OH5 must first
restore the per-cell landscape interleave on which these barriers operate.
## 8. Required OH7 cutover
Current `RetailAlphaQueue` is a single scope-global, viewer-distance-sorted
stream with an approximate `FlushFartherThan`. Retail instead requires:
- separate CLIP and ALPHA FIFO lists;
- no queue-level sort;
- preservation of upstream cell order, `CShadowPart` order, and authored
subset order;
- raw surface type plus the exact constructed mask and positive-surface
stippling ownership;
- `overrideClipmap` and independent per-list object-boundary state;
- the multipass clip-map immediate-plus-delayed duplicate;
- material `has_alpha` fallback;
- sky/detail immediate gates and delayed `detailEnabled=false`;
- exact capacity and threshold behavior;
- full drains at the exact building, landscape, and top-level barriers.
Runtime state remains configurable: registry may override delay mask, the user
preference may override MultiPassAlpha, and detail generation may return null.
Those are input states to the exact branch table, not unresolved branch sense.