docs(render): V11 closeout — register, architecture, code structure, issues

Retires the GL framing from the documents that described a two-backend,
two-UI-stack client, and files what the deletion left behind.

Divergence register:
  * AD-46 (anisotropic tap pattern in dense alpha scenery) is REFRAMED rather
    than retired. Its substance survives -- distant foliage may read denser
    than retail's -- but it was measured GL-vs-Vulkan, and with GL gone it is
    a Vulkan-vs-retail question against the D3D oracle it already cited. The
    measurement is kept as the evidence that the residual is a driver tap
    pattern; the row now records that it is no longer falsifiable by
    self-differential, which is a real loss the deletion causes.
  * AD-47 and AD-48 are NEW, and the campaign's own risk register scheduled
    them here: MSAA sample positions (measured at 8.83% of the frame at 4x,
    which is why every strict gate runs MSAA off -- and therefore why a
    regression confined to the multisample path would not be caught) and
    present pacing (#235 is the live instance).
  * AD-17's justification moves from a GL clip-plane citation to Vulkan's
    maxClipDistances floor, which is the same 8, so the divergence is
    unchanged and only its authority moves.
  * AP-92 keeps IUiViewportRenderer.TextureIsBottomUp rather than folding it
    flat, because it is what let the origin question be answered by data.

Architecture and code structure: the layer diagram, the frame order, the
residency vocabulary and the reference table all said OpenGL. The UI section
said two stacks. Rule 3's rationale is rewritten around what actually
happened -- ImGui was deleted and not one panel, ViewModel or command had to
change, because none of them had ever imported ImGuiNET. That is the rule
paying for itself, so it is recorded as evidence rather than removed as
obsolete.

Issues: #258 files the dev-panel host as a decision rather than an accident,
and #255 is REOPENED. Its TaskCreationOptions.LongRunning fix asks the
scheduler for a thread but does not promise two callbacks overlap; under nine
concurrent test assemblies it still failed 2 of 5 whole-suite runs. The
earlier evidence tested a narrower pool, not a contended one. The fix it
needs is a rendezvous inside the read stub -- not a weakened assertion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-29 03:20:04 +02:00
parent 5852bdb877
commit c265b52d4b
5 changed files with 143 additions and 62 deletions

View file

@ -6,7 +6,7 @@ A modern C# .NET 10 Asheron's Call client that:
- **Behaves identically to the retail client** — same physics, same
animations, same terrain, same collision, same network protocol
- **Looks identical to the retail client** — same meshes, same textures,
same lighting, same blending, rendered via modern Silk.NET OpenGL
same lighting, same blending, rendered via modern Silk.NET Vulkan
- **Adds a plugin API** the retail client never had — native C# plugins
+ Lua macro scripting for player automation
- **Is NOT a 1:1 C++ port** — uses modern C# patterns (composition over
@ -64,7 +64,7 @@ well-defined interfaces that the retail client never had.
│ ► wire-format identical to retail │
├──────────────────────────────────────────────────────────────┤
│ LAYER 1: Renderer │
│ Silk.NET OpenGL 4.3 core profile
│ Silk.NET Vulkan 1.3 (the only backend since Campaign V V11)
│ TerrainModernRenderer, WbDrawDispatcher, EnvCellRenderer │
│ Shaders (terrain blending, mesh lighting, translucency) │
│ ► completely different from retail (D3D7), same visual │
@ -79,28 +79,35 @@ well-defined interfaces that the retail client never had.
### UI Architecture (two coexisting presentation stacks)
The 2026-04-24 design began with a swappable renderer abstraction. D.2b proved
that retail fidelity needs a retained LayoutDesc/DAT tree, while ImGui remains
valuable as permanent devtools. The current architecture therefore has two
presentation stacks over shared state, ViewModels, events, and commands. Full
history and the corrected contract live in
that retail fidelity needs a retained LayoutDesc/DAT tree, and for most of the
project's life ImGui coexisted beside it as permanent devtools. **Campaign V
slice V11 ended that (2026-07-29):** ImGui was a GL-only frontend, the campaign
did not port it, and deleting OpenGL deleted it. There is now **one** presentation
stack. Full history and the corrected contract live in
`docs/plans/2026-04-24-ui-framework.md`.
```
┌─────────────────────────────────────────────────────────────┐
│ DEVELOPER UI │
│ IPanel/IPanelRenderer → permanent ImGui devtools │
│ GAMEPLAY UI │
│ GAMEPLAY UI (the only stack) │
│ LayoutDesc/DAT → UiRoot retained widgets + controllers │
├─────────────────────────────────────────────────────────────┤
│ SHARED CONTRACTS │
│ ViewModels, commands, input actions, state/event services │
│ ► one model and mutation path, two presentation projections
│ ► one model and mutation path, one presentation projection
├─────────────────────────────────────────────────────────────┤
│ Game state + events (unchanged) │
│ IGameState / IEvents / WorldSession — UI only reads │
└─────────────────────────────────────────────────────────────┘
```
`AcDream.UI.Abstractions` — the `IPanel`/`IPanelRenderer` contract, the
ViewModels and the commands — **survives intact**. It was always
backend-agnostic, which is exactly what Code Structure Rule 3 was written to
protect, and it is what a future developer-panel host would bind to. Only the
ImGui *backend* was deleted. `ACDREAM_DEVTOOLS=1` still selects Vulkan's
debug-utils extensions and now logs that the developer UI is gone; replacing it
is issue **#258**, deliberately unscheduled.
`AcDream.UI.Abstractions` owns backend-neutral ViewModels, commands, input,
and the `IPanel`/`IPanelRenderer` devtools contract. `AcDream.App/UI` owns the
retained gameplay tree, LayoutDesc importer, window runtime, and panel
@ -112,8 +119,8 @@ retail client command (`ExecuteClientCommandCmd`), an ACE-owned command
handlers and controllers translate those intents to `WorldSession`; panels
never inspect or construct wire messages.
Plugins register retained gameplay markup through the BCL-only
`AcDream.Plugin.Abstractions.IUiRegistry`; they do not import App or ImGui
assemblies. Core `SelectionState` is the sole selected-object owner for world,
`AcDream.Plugin.Abstractions.IUiRegistry`; they do not import App or
presentation assemblies. Core `SelectionState` is the sole selected-object owner for world,
radar, inventory, paperdoll, toolbar, use/examine consumers, and plugins;
`IPluginHost.Selection` exposes that same state and retail-style old/new callback.
Temporary pointer modes are separate App orchestration in `InteractionState` and
@ -131,11 +138,11 @@ Every retained gameplay window has a typed `RetailWindowHandle` and at most one
`IRetainedPanelController` lifecycle owner. Multi-controller windows use
`RetainedPanelControllerGroup`; the manager disposes controllers exactly once in
reverse ownership order. `UiHost` removes Silk device subscriptions first, then
disposes the manager/controllers, then its GL renderer. `GameWindow.OnClosing`
disposes the manager/controllers, then its renderer. `GameWindow.OnClosing`
tears this runtime down before session and game-state sources.
`RetailUiRuntime` is the production composition boundary. `GameWindow` creates
the GL/DAT resolvers and supplies focused state/action binding records in one
the device/DAT resolvers and supplies focused state/action binding records in one
mount call; the runtime owns all LayoutDesc imports, controller construction,
window registration, plugin mounts, cursor feedback, layout persistence, and the
retained tick/draw/restore/dispose paths. Panel-specific construction must not
@ -166,7 +173,7 @@ parallel window-lifecycle map.
```
src/
AcDream.Core/ Layer 2-4: no GL, no Silk.NET, pure logic
AcDream.Core/ Layer 2-4: no Vulkan, no Silk.NET, pure logic
Physics/
PhysicsBody.cs -> body state / integration foundation (done)
CollisionPrimitives.cs -> retail primitive helpers (partial, active)
@ -247,7 +254,7 @@ src/
RuntimeGenerationReset.cs -> one retryable canonical-generation reset
-> Slice J complete; graphical and no-window hosts share one GameRuntime
-> may reference Core, Core.Net, Content, and Plugin.Abstractions only
-> must never reference App, UI, Silk.NET, OpenAL, Arch, or ImGui
-> must never reference App, UI, Silk.NET, OpenAL, or Arch
AcDream.Headless/ Linux/Windows no-window production host
Program.cs -> CLI entry only
@ -275,10 +282,10 @@ src/
GraphicalWindowBackendSelection.cs -> immutable GLFW 3.4
Win32/X11/Wayland selection and
packaged-native preference
GraphicalCapabilityRecord.cs -> pre-render platform/GL/audio/input
capability report and startup guard
GraphicalGlFunctionProbe.cs -> active modern-GL shader/MDI/SSBO/
timer/FBO/persistent-buffer validation
Gpu/Vk/VulkanCapabilityRecord.cs -> pre-render platform/device/audio/
input capability report + startup gate
Gpu/Vk/VulkanBringUpHost.cs -> active device/descriptor/pipeline/
offscreen-readback validation
GraphicalLegacyConfigurationMigrator.cs -> no-overwrite Windows
LocalAppData migration
Physics/
@ -646,7 +653,7 @@ centralizing physical storage. `ResidencyManager` owns generation-safe logical
asset identity, owner tokens, leases, immutable budgets, and aggregate
accounting. Specialized render/content owners continue to own their mesh
ranges, texture arrays, staging payloads, decoded animations/audio, and
retained shared-alpha scratch. The manager never sees a GL name: policy runs as
retained shared-alpha scratch. The manager never sees a device handle: policy runs as
a single writer during update/resource maintenance, while logical eviction and
fence-delayed physical release remain render-thread owner operations. Prepared
package address space is reported separately from committed CPU bytes, and
@ -732,13 +739,13 @@ every region visited:
128 MiB respectively, with an 896 MiB physical ceiling that includes an
in-progress migration and its retired predecessor.
OpenGL deletion and range/slot reuse are not synonymous with logical release.
Vulkan object destruction and range/slot reuse are not synonymous with logical release.
`GpuFrameFlightController` fences three frames in flight. Mesh-buffer stores,
texture handles, atlas layers, terrain slots, and landblock render records enter
retirement only after they are no longer publishable, and their physical ids are
recycled only after the corresponding fence signals. Shutdown follows the same
dependency order and remains retryable: UI/controllers and render registrations
withdraw first, then owner leases and caches, then GL backing stores. This keeps
withdraw first, then owner leases and caches, then device backing stores. This keeps
drivers from reading freed memory without adding a portal-specific purge or a
visual-distance reduction.
@ -770,9 +777,9 @@ the exact retail `Client::UseTime` order. See the completed Slice 6 ledger and
`GameWindow.OnRender` likewise owns only one handoff to the shipped
`RenderFrameOrchestrator`. Its frozen graph begins per-resource frames and
render-thread uploads; draws world/PView and its two shared-alpha scopes;
renders portal and paperdoll private viewports, retained gameplay UI, ImGui
renders portal and paperdoll private viewports, retained gameplay UI,
devtools, and screenshots; then closes the GPU-flight transaction in
`finally`. Both UI stacks coexist in the same frame.
`finally`. There is one UI stack: the retained tree.
---
@ -849,7 +856,7 @@ exact reference identity and retry position, rejects stale generations, and
executes every class through the same meter. The canonical reveal generation
supplies one exact destination cell/radius reservation; the meter protects its
configured share across time, completion admissions, retained CPU bytes,
entity operations, requested GPU bytes, and GL-retirement admissions. A
entity operations, requested GPU bytes, and device-retirement admissions. A
completion retains the reveal generation that classified it, so a stale
completion or teardown cannot consume or clear a replacement generation's
reservation. Priority changes order and reserved share, never the global
@ -866,7 +873,7 @@ readiness continue. Canonical live records remain retained until physical
teardown converges, so quiescence neither reconstructs server identity nor
invents Hidden/UnHide transitions. A shared-origin recenter atomically removes
the complete old spatial generation and captures one exact receipt per
landblock before the origin changes. Expensive script, physics, render, and GL
landblock before the origin changes. Expensive script, physics, render, and GPU
release then advances only from `StreamingController.Tick` through stable
per-owner entity/stage cursors on the frame's one meter. Publication remains
fenced only by an older receipt for the same canonical landblock; the active
@ -944,7 +951,7 @@ rejects a stable checkpoint while any host acknowledgement remains.
`FrameScreenshotController` performs the requested default-framebuffer readback
after world and retained UI draw on the render thread. The external connected
gate launches normal Release processes and observes these artifacts, but never
mutates streaming, physics, GL, or entity state from a worker thread.
mutates streaming, physics, GPU, or entity state from a worker thread.
### Teleport cell identity at the streaming boundary
@ -1077,7 +1084,7 @@ Current movement/collision ownership:
fallback/transfer. `RuntimeSettingsController` is the sole settings
persistence/current-state owner, supplies the immutable pre-window snapshot,
applies startup pacing/display/FOV/audio once, and later borrows complete
runtime targets without replay. Checked GL construction/state boundaries
runtime targets without replay. Checked GPU construction/state boundaries
retain exact cleanup obligations across failure. The executable nine-phase
production pipeline and terminal session start are complete. Checkpoint J
moves the exact shutdown dependency graph into `GameWindowLifetime`, whose
@ -1127,7 +1134,7 @@ For acdream-specific code (renderer, plugin API, streaming):
| Physics/collision | `docs/research/named-retail/` | ACE Physics/ + older decompiled chunks |
| Animation | `docs/research/named-retail/` + ACE Animation/ | — |
| Terrain | ACME ClientReference.cs | named retail / older decompiled chunks |
| Rendering | WorldBuilder (Silk.NET) | ACViewer |
| Rendering | WorldBuilder (Silk.NET; its GL calls now read as RHI intent) | ACViewer |
| Protocol | holtburger | AC2D |
| Server behavior | ACE | — |