docs(render): scope the Vulkan composition host, and correct where V4t blocks

§5.5.7 asked for "a Vulkan composition host — a slice the plan has never
scoped." This scopes it and stops there, for the same reason V6f stopped short
of the world fork: the honest measurement is worth more than a half-built second
path, and §3.1 and §7.1 rule 3 exist to prevent exactly the unexercised arm this
would otherwise have landed.

Three findings, all verified against source rather than inferred.

The fork seam mostly already exists. GameWindowPlatformResult<TGraphics, TInput>
is fully generic — only its call sites pin GL — and the host phase already has
IHostInputCameraCompositionFactory with a single Retail implementation, so the
Vulkan device arm is a new file rather than a modification. What does not exist
is a Vulkan frame root: FrameRootComposition assembles the clear phase, both
pass executors and the GPU profiler measurement from a GL handle plus six raw-GL
world renderers, none of which exist on Vulkan. That second assembly is the
slice's centre of gravity, and it is most of V4h.

The retained UI is NOT blocked on V4t. V6f wrote that the retail widget tree's
chrome comes from a GL-only TextureCache, which is true of the type but not of
the path: V4a and V6d already moved UploadUiTexture onto IGpuDevice, and the
public UploadRgba8 that IconComposer composes retail icons with routes into it.
The raw-_gl uploads that remain are the world's Texture2D/Texture2DArray paths,
which no UI draw reaches. Three small things stand in the way — a non-null GL in
TextureCache's constructor, one ((GlGpuTexture)texture).GlName cast for VRAM
accounting, and the UI-probe screenshot controller — and none of them is V4t.
So V4t is a hard prerequisite for the WORLD arm, not for the host, which moves
the host ahead of it in the sequence rather than behind.

That reordering is the point of the commit. The corrected sequence puts the
composition host at step 2, before V4t, because its acceptance criterion — the
real UI renders — makes it the first frame acdream draws on Vulkan that is the
client's own frame rather than a scene written to prove the backend.

Also records the argument for keeping VulkanBringUpHost as a capability-probe
harness: its window/instance/surface/device/swapchain sequence is what the
composition arm needs, and deleting it would mean writing that twice.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-28 10:51:26 +02:00
parent 24834a6478
commit 46d893f7c3

View file

@ -1195,6 +1195,96 @@ instrument to a gate.
world arm needs one descriptor set per renderer, or per-renderer sub-ranges of one
buffer, and it needs to know that before it is written.**
#### 5.5.9 The Vulkan composition host: the seam, measured (V6g)
§5.5.7's step 1 asked for "a Vulkan composition host — a slice the plan has never
scoped." V6g scoped it and did not build it. What follows is the specification, so
the slice that does build it starts from a file list rather than a survey. The
short version: **it is most of V4h, it is roughly 1,2002,000 changed lines across
~20 files including `GameWindow.cs`, and it is a load-bearing slice in its own
right** — not a preface to one.
**The fork seam is already there, in three places, and only one of them is
missing.**
1. **Platform acquisition is already backend-neutral.**
`GameWindowPlatformResult<TGraphics, TInput>` and
`IGameWindowPlatformPublication<TGraphics, TInput>`
(`Composition/GameWindowPlatformAcquisition.cs:3,11`) are fully generic. Only
the *call sites* pin `TGraphics = GL`. Nothing in the acquisition machinery
needs changing.
2. **The host phase already has a factory interface built for exactly this
substitution.** `IHostInputCameraCompositionFactory`
(`Composition/HostInputCameraComposition.cs:49`) declares
`CreateViewportTarget`, `CreateGpuFrameFlights`, `CreateGpuDevice` and
`CreateWorldRenderDiagnostics`, each taking a `GL`, with one implementation
(`RetailHostInputCameraCompositionFactory`). **A `VulkanHostInputCameraCompositionFactory`
returning `VulkanGpuDevice` is the fork, and it is a new file, not a
modification.** The four signatures lose their `GL` parameter and take the
platform result instead.
3. **The frame root is the seam that does not exist yet**, and it is the real
work. `FrameRootComposition` builds `RuntimeRenderFrameClearPhase`,
`RetailPViewPassExecutor`, `WorldScenePassExecutor` and
`FrameProfilerGpuMeasurement` from `d.Gl` plus six raw-GL world renderers
(`live.DrawDispatcher`, `live.EnvCellRenderer`, `foundation.Terrain`,
`live.SkyRenderer`, `live.ParticleRenderer`, `live.PortalDepthMask`). On Vulkan
none of those exist, so the Vulkan arm is a **second frame-root assembly** whose
render graph is the retained UI and debug lines through the RHI and nothing
else. That is the slice's centre of gravity.
**What the retained UI actually needs — better news than §5.5.7 implied.** V6f
wrote that "the retail widget tree is built from LayoutDesc and DAT chrome by
`TextureCache`, which is still a GL type until slice V4t." That is true of the type
but **not of the UI's texture path**, which V4a and V6d already moved onto
`IGpuDevice`: `TextureCache.UploadUiTexture` (`Rendering/TextureCache.cs:311`) goes
through `CreateTexture`/`CreateSampler`/`RegisterTexture`, and the public
`UploadRgba8` that `IconComposer` composes retail icons with routes into it. The
raw-`_gl` uploads that remain are the world's `Texture2D`/`Texture2DArray` paths,
which no UI draw reaches. **Three things, all small, stand between the real
`UiHost` and a Vulkan frame:**
- `TextureCache`'s constructor requires a non-null `GL` (`:21`).
- `UploadUiTexture` hard-casts to `GlGpuTexture` for VRAM accounting
(`Rendering/TextureCache.cs:337`: `uint glName = ((GlGpuTexture)texture).GlName;`).
- `InteractionRetainedUiComposition:533` builds the UI-probe
`FrameScreenshotController` from `d.Gl`, reachable only when
`ACDREAM_UI_PROBE` is on.
So **the retained UI is not V4t-blocked; the world is.** That is the one place
§5.5.7's sequencing should be read more precisely: V4t is a hard prerequisite for
step 4 (the world arm), not for step 1.
**The mechanical remainder**, for estimation: `GL` appears concretely in five
dependency records (`FrameRootDependencies.Gl`, `LivePresentationDependencies.Gl`,
`InteractionRetainedUiDependencies.Gl`, plus the host and settings phases), each
guarded by a `ReferenceEquals(_dependencies.Gl, platform.Graphics)` consistency
check that has to become backend-aware. `SettingsDevToolsComposition` is ImGui,
which is not ported and is deleted at V11, so the Vulkan arm simply omits DevTools.
`GameWindow.Run` opens a `ContextAPI.OpenGL` window with MSAA and stencil
attributes; the Vulkan arm needs `WindowOptions.DefaultVulkan` and the
instance/surface/device/swapchain sequence `VulkanBringUpHost.CreateWindow`
through `CreateFrameResources` already performs — that code is reusable, which is
the argument for reducing `VulkanBringUpHost` to a capability-probe harness rather
than deleting it outright.
**Corrected sequence**, superseding §5.5.7's four steps:
1. ~~The three validation defects~~ — done at V6g (§5.5.8). They came first
because every one is on any world frame's path, and because a host built on a
frame that fails validation cannot be debugged.
2. **The Vulkan composition host**, as specified above, delivering DAT load,
streaming, camera, entity table, session and the real retained UI on Vulkan,
with no world renderers. Its gate is an offline Vulkan launch reaching the real
composition with a captured UI frame, a converging ownership ledger at shutdown,
and the strict GL offline gate unmoved.
3. **V4t**, the texture stack, which the world arm cannot be written without.
4. **The world arm** — V4c/V4d's content behind the construction-time backend
selection at the frame-root seam.
Step 2's own acceptance criterion — "the real UI renders" — is what makes it worth
doing before V4t rather than after: it is the first frame acdream draws on Vulkan
that is the *client's* frame rather than a scene written to prove the backend.
### 5.4 The null-target `BeginPass` divergence (V4c) — must be undone at V6
V4c had to stop GL's `BeginPass` from binding framebuffer 0 when a pass declares